jbosscache-core-2.2.2.GA/0000755000175000017500000000000011376174036015003 5ustar twernertwernerjbosscache-core-2.2.2.GA/README-i18n.txt0000644000175000017500000000223410676513755017266 0ustar twernertwernerInternationalising documentation -------------------------------- Documentation for JBoss Cache is written using docbook XML. There is a lot of reference material on docbook XML if you are unfamiliar with it, but JBoss Cache's usage of docbook is pretty basic and most of it can be picked up by looking through the existing documentation sources, which are in src/main/docbook. For each document, the 'en' version is treated as the reference source for all translations. Starting a new translation -------------------------- ** NB: This section needs updating since moving to subversion. Each time a new translation is started, the docs directory should be updated and tagged as I18N_<2-letter iso lang code>_<2-letter iso country code> (optional) such as I18N_IT or I18N_PT_BR (note all in upper case) Updating translations --------------------- Each time a new release is made, translations should be updated by: 1) Doing a diff of the 'en' docs, comparing the latest against the translation tag (e.g., I18N_IT) 2) Updating the translation (e.g., the 'it' docs) 3) Moving the translation tag on the 'en' files (e.g., I18N_IT) to the current snapshot. - Manik Surtani jbosscache-core-2.2.2.GA/pom.xml0000644000175000017500000004310411147506161016314 0ustar twernertwerner 4.0.0 2.2.2.GA unit install org.jboss.cache jbosscache-common-parent 1.4 org.jboss.cache jbosscache-core ${jbosscache-core-version} JBoss Cache - Core Edition JBoss Cache - Core Edition http://www.jbosscache.org jar jgroups jgroups 2.6.5.GA javax.transaction jta 1.1 commons-logging commons-logging 1.0.4 org.jboss jboss-common-core 2.2.8.GA apache-slide webdavlib apache-xerces xml-apis apache-httpclient commons-httpclient jdbm jdbm 1.0 true c3p0 c3p0 0.9.1.1 true sleepycat je 1.7.0 true net.jcip jcip-annotations 1.0 true net.noderunner amazon-s3 1.0.0.0 true org.easymock easymock 2.3 test jboss.jbossts jbossjta 4.3.0.GA test beanshell bsh 2.0b4 test net.noderunner http 1.0 test javax.servlet servlet-api 2.3 test maven-assembly-plugin 2.2-beta-1 assemble install attached assembly/bin.xml assembly/doc.xml assembly/all.xml ${artifactId}-${jbosscache-core-version} target/distribution target/assembly/work org.apache.maven.plugins maven-jar-plugin true true org.jboss.cache.Version build-test-jar test-jar true true repository.jboss.org http://repository.jboss.org/maven2 snapshots.jboss.org http://snapshots.jboss.org/maven2 e-xml.sourceforge.net http://e-xml.sourceforge.net/maven2/repository Docs false package org.jboss.maven.plugins maven-jdocbook-plugin 2.0.0 true org.jboss jbossorg-docbook-xslt 1.1.0 org.jboss jbossorg-jdocbook-style 1.1.0 jdocbook-style userguide_en package resources generate master.xml ${basedir}/src/main/docbook/userguide/en ${basedir}/src/main/docbook/images ${basedir}/src/main/docbook/css ${basedir}/target/docbook/userguide_en pdf classpath:/xslt/org/jboss/pdf.xsl userguide_en.pdf html classpath:/xslt/org/jboss/xhtml.xsl index.html html_single classpath:/xslt/org/jboss/xhtml-single.xsl index.html false tutorial_en package resources generate master.xml ${basedir}/src/main/docbook/tutorial/en ${basedir}/src/main/docbook/images ${basedir}/src/main/docbook/css ${basedir}/target/docbook/tutorial_en pdf classpath:/xslt/org/jboss/pdf.xsl tutorial_en.pdf html classpath:/xslt/org/jboss/xhtml.xsl index.html html_single classpath:/xslt/org/jboss/xhtml-single.xsl index.html false faq_en package resources generate master.xml ${basedir}/src/main/docbook/faq/en ${basedir}/src/main/docbook/images ${basedir}/src/main/docbook/css ${basedir}/target/docbook/faq_en pdf classpath:/xslt/org/jboss/pdf.xsl faq_en.pdf html classpath:/xslt/org/jboss/xhtml.xsl index.html html_single classpath:/xslt/org/jboss/xhtml-single.xsl index.html false test-hudson functional,unit test-functional functional test-unit unit test-jgroups jgroups test-transaction transaction test-integration integration JBossAS false 2.2.1-SNAPSHOT-JBossAS functional,unit jgroups jgroups 2.6.5.GA org.jboss.javaee jboss-javaee 5.0.0.CR2 org.jboss jboss-common-core 2.2.8.GA commons-logging commons-logging 1.1.0.jboss jboss.jbossts jbossjta 4.4.0.GA test jbosscache-core-2.2.2.GA/README-Maven.txt0000644000175000017500000002171111021255727017541 0ustar twernertwerner 1. WORKING WITH MAVEN ===================== Requirements: * Java 5.0 and above * Maven 2.0.8 and above 1.1. Quickstart: Typical lifecycle phases ----------------------------------------- Maven will create a target/ directory under the root for the creation of output at every stage. * mvn clean: Cleans out any old builds and binaries * mvn compile: Compiles java source code. * mvn test: Runs the TestNG unit test suite on the compiled code. Will also compile the tests. See the testing section below for more information on running different test groups. The default test group run is the "unit" group. * mvn package: Packages the module as a JAR file, the resulting JAR file will be in target/ * mvn package -Dmaven.test.skip=true: Creates a JAR file without running tests. * mvn package -P Docs: Packages the module as a JAR file, and builds the javadocs and user documentation from docbook sources. * mvn install: will install the artifacts in your local repo for use by other projects (such as JBoss Cache POJO edition which depends on JBoss Cache Core). Will also use Maven's assembly plugin to build ZIP files for download (in target/distribution) * mvn deploy: will build and deploy the project to the JBoss snapshots repository. Note that you should have your WebDAV username and password set up. (Deploys snapshots to http://snapshots.jboss.org/maven2/org/jboss/cache/). If you have a non-SNAPSHOT version number in your pom.xml, it will be deployed to the live releases repository (see below) * mvn clean site -Ptest-functional,codeCoverage: will run all tests in the test-functional profile and generate code coverage reports using EMMA. 1.2. Setting up your WebDAV username and password to deploy project snapshots ----------------------------------------------------------------------------- You will also have to configure maven to use your username and password to access this repository. For this, you will have to modify the servers section of maven settings file ($MAVEN_HOME/conf/settings.xml, or ~/.m2/settings.xml). Something similar to the following should be added: ... ... snapshots.jboss.org webdav-user webdav-pass ... ... 1.3. Deploying a release to a live repository --------------------------------------------- Very simple. Make sure you have the version number in your pom.xml set to a non-SNAPSHOT version. Maven will pick up on this and deploy to your release repository rather than the snapshot repository. JBoss release repository cannot be accessed via WebDAV, as the snapshot repository can. So what you need to do is: 1) Check out the release repository from Subversion (svn co https://svn.jboss.org/repos/repository.jboss.org/maven2) 2) Add a property in ~/.m2/settings.xml to point to this 'local' copy of the repo. (See maven settings below) 3) Update your project's pom.xml to reflect a non-SNAPSHOT version number 4) Deploy your project (mvn clean deploy) 5) Check in your 'local' copy of the repo checked out in (1), adding any new directories/files created by (4). 1.4. Maven settings.xml ----------------------- Working with the JBoss Cache source tree, I have configured my ~/.m2/settings.xml to look like: snapshots.jboss.org MY_JBOSS_ORG_USERNAME WONT_TELL_YOU jboss /Users/manik/Code/maven2 jboss 2. TESTING ========== Tests are written against the TestNG testing framework. Each test should belong to one or more group. The group acts as a filter, and is used to select which tests are ran as part of the maven test lifecycle. There are 3 groups that are currently in use, but there is not formal, you can make up any group name if you like. 2.1. Current Groups ------------------- * unit - Unit tests using stubs to isolate and test each major class in JBoss Cache. This is the default group run if no test group is specified. * functional - Tests which test the general functionality of JBoss Cache * jgroups - Tests which need to send data on a JGroups Channel * transaction - Tests which use a transaction manager * profiling - Tests used for manual profiling, not meant for automated test runs * manual - Other tests that are run manually * integration - Integration tests emulating usage patterns for specific products such as JBoss AS Clustering and Hibernate clustering It should be noted that every test (except those not intended to be run by Hudson) should at least be in the functional group, since this is the default test group that is executed by maven, and the one that is required to prepare a release. 2.2. Executing the default test run ----------------------------------- The default run executes all tests in the functional group. To just run the tests with txt and xml output the command is: $ mvn test Alternatively, you can execute the tests AND generate a report with: $ mvn surefire-report:report If you already have ran a test cycle, and you want to generate a report off the current reports, then you use the report-only goal, ike so: $ mvn surefire-report:report-only 2.3. Executing different groups ------------------------------- A group can be executed (using the default configuration) by simply using the appropriate profile, like so: $ mvn -P test-jgroups test 2.4. Executing a single test ---------------------------- A single test can be executed using the test property. The value is the short name (not the fully qualified package name) of the test. $ mvn -P test-XXX -Dtest=FqnTest test Alternatively, if there is more than one test with a given classname in your test suite, you could provide the path to the test. $ mvn -P test-XXX -Dtest=org/jboss/cache/multiplexer/SyncReplTxTest test 2.5. Executing all tests in a given package -------------------------------------------- This can be achieved by passing in the package name with a wildcard to the test parameter. $ mvn -P test-XXX -Dtest=org/jboss/cache/multiplexer/* test 2.6. Skipping the test run -------------------------- It is sometimes desirable to install the jboss cache package in your local repository without performing a full test run. To do this, simply use the maven.test.skip.exec property: $ mvn -Dmaven.test.skip.exec=true install Again, this is just a shortcut for local use. It SHOULD NEVER BE USED when releasing. Also, make sure "exec" is included in the property, if not the tests will not be built, which will prevent a test jar being produced (POJO Cache needs the Core Cache test jar). 2.7. Permutations ----------------- We use the term permutation to describe a group execution against a particular config. This allows us to test a variety of environments and configurations without rewriting the same basic test over and over again. For example, the jgroups-tcp permutation executes the jgroups group using the TCP config. Each permutation requires a maven profile which defines the various options, environmental variables, etc. The command to run the jgroups-tcp permutatin is: $ mvn -Pjgroups-tcp surefire-report:report Each permutation uses its own report directory, and its own html output file name. This allows you to execute multiple permutations without wiping the results from the previous run. Note that due to the way maven operates, only one permutation can be executed per mvn command. So automating multiple runs requires shell scripting, or some other execution framework to make multiple called to maven. 2.8. Running permutations manually or in an IDE ----------------------------------------------- Sometimes you want to run a test using settings other than the defaults (such as UDP for "jgroups" group tests or the DummyTransactionManager for "transaction" group tests). This can be achieved by referring to the Maven POM file to figure out which system properties are passed in to the test when doing something different. E.g., to run a "jgroups" group test in your IDE using TCP instead of the default UDP, set the following: -Djgroups.stack=tcp Or, to use JBoss JTA (Arjuna TM) instead of the DummyTransactionManager in a "transaction" group test, set: -Dorg.jboss.cache.test.tm=jboss-jta Please refer to the POM file for more properties and permutations. 2.9. Integration with CruiseControl / Hudson -------------------------------------------- CruiseControl should do the following: * Run "mvn clean site" - will clean and run tests, and then prepare reports. In addition to unit tests, this project is set up to run FindBugs, PMD, jxr, and a bunch of other code analysis tools and provide a report in target/site/project-reports.html - which should be linked from the CruiseControl summary page. jbosscache-core-2.2.2.GA/assembly/0000755000175000017500000000000011376173731016623 5ustar twernertwernerjbosscache-core-2.2.2.GA/assembly/bin.xml0000644000175000017500000000261410757176162020123 0ustar twernertwerner bin zip true target *.jar *test*.jar src/main/etc etc src/main/release **/*.txt lib ${artifactId}.${extension} false runtime apache-xerces:xml-apis apache-httpclient:commons-httpclient apache-slide:webdavlib jbosscache-core-2.2.2.GA/assembly/doc.xml0000644000175000017500000000134510660525510020104 0ustar twernertwerner doc zip true src/main/release *.txt **lib** license/* target/site/apidocs doc/apidocs target/docbook doc/ jbosscache-core-2.2.2.GA/assembly/all.xml0000644000175000017500000000425310757176162020124 0ustar twernertwerner all zip true target *.jar *test*.jar src/main/etc etc src/main/java src src/test/java test src/test/resources test src/main/release **/*.txt **/*.xml target/site/apidocs doc/apidocs target/docbook doc/ lib ${scope}/${artifactId}.${extension} false test apache-xerces:xml-apis apache-httpclient:commons-httpclient apache-slide:webdavlib jbosscache-core-2.2.2.GA/src/0000755000175000017500000000000011376173772015600 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/test/0000755000175000017500000000000011376173770016555 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/test/java/0000755000175000017500000000000011376173731017473 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/test/java/org/0000755000175000017500000000000011376173731020262 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/test/java/org/jboss/0000755000175000017500000000000011376173731021402 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/0000755000175000017500000000000011376173770022450 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/jmx/0000755000175000017500000000000011376173752023246 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/jmx/CacheJmxWrapperTestBase.java0000644000175000017500000001233510736672510030566 0ustar twernertwernerpackage org.jboss.cache.jmx; import org.jboss.cache.Cache; import org.jboss.cache.CacheFactory; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.config.Configuration; import static org.testng.AssertJUnit.assertFalse; import static org.testng.AssertJUnit.assertTrue; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.management.MBeanServer; import javax.management.MBeanServerFactory; import javax.management.MBeanServerInvocationHandler; import javax.management.MalformedObjectNameException; import javax.management.ObjectName; /** * Tests the JMX wrapper class around the cache. * * @author Manik Surtani * @author Brian Stansberry */ @Test(groups = "functional") public abstract class CacheJmxWrapperTestBase { public static final String CLUSTER_NAME = "CacheMBeanTest"; protected Cache cache; protected CacheJmxWrapperMBean jmxWrapper; protected MBeanServer mBeanServer; protected ObjectName mBeanName; protected String mBeanNameStr; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { mBeanServer = MBeanServerFactory.createMBeanServer("CacheMBeanTest"); mBeanNameStr = JmxUtil.PREFIX + CLUSTER_NAME; mBeanName = new ObjectName(mBeanNameStr); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { try { cleanup(); } finally { if (mBeanServer != null) { MBeanServerFactory.releaseMBeanServer(mBeanServer); mBeanServer = null; } } } protected CacheJmxWrapperMBean registerWrapper() throws Exception { if (cache == null) cache = createCache(createConfiguration()); return registerWrapper(cache); } protected CacheJmxWrapperMBean registerWrapper(Cache toWrap) throws Exception { CacheJmxWrapper wrapper = new CacheJmxWrapper(toWrap); return registerWrapper(wrapper); } protected CacheJmxWrapperMBean registerWrapper(Configuration config) throws Exception { CacheJmxWrapper wrapper = new CacheJmxWrapper(); wrapper.setConfiguration(config); return registerWrapper(wrapper); } @SuppressWarnings("unchecked") protected CacheJmxWrapperMBean registerWrapper(CacheJmxWrapperMBean wrapper) throws Exception { JmxUtil.registerCacheMBean(mBeanServer, wrapper, mBeanNameStr); jmxWrapper = (CacheJmxWrapperMBean) MBeanServerInvocationHandler.newProxyInstance(mBeanServer, mBeanName, CacheJmxWrapperMBean.class, false); return jmxWrapper; } protected void unregisterWrapper() throws Exception { mBeanServer.unregisterMBean(mBeanName); } protected CacheJmxWrapper createWrapper(Configuration config) { CacheJmxWrapper wrapper = new CacheJmxWrapper(); wrapper.setConfiguration(config); return wrapper; } protected Cache createCache(Configuration config) { CacheFactory factory = new DefaultCacheFactory(); cache = factory.createCache(config, false); return cache; } protected Configuration createConfiguration() { Configuration c = new Configuration(); c.setClusterName(CLUSTER_NAME); c.setExposeManagementStatistics(true); c.setCacheMode(Configuration.CacheMode.LOCAL); return c; } private void cleanup() throws Exception { if (cache != null) { try { cache.stop(); } catch (Exception ignored) { } cache = null; } if (jmxWrapper != null) { try { jmxWrapper.stop(); jmxWrapper.destroy(); } catch (Exception ignored) { } jmxWrapper = null; } if (mBeanServer != null && mBeanName != null && mBeanServer.isRegistered(mBeanName)) mBeanServer.unregisterMBean(mBeanName); } protected void interceptorRegistrationTest(boolean expectMbeans) throws MalformedObjectNameException, NullPointerException { interceptorRegistrationTest(mBeanNameStr, expectMbeans); } protected void interceptorRegistrationTest(String baseName, boolean expectMbeans) throws MalformedObjectNameException, NullPointerException { // should be 3 interceptor MBeans loaded: ObjectName[] interceptorMBeanNames = { new ObjectName(baseName + JmxUtil.INTERCEPTOR_KEY + "TxInterceptor"), new ObjectName(baseName + JmxUtil.INTERCEPTOR_KEY + "CacheMgmtInterceptor"), new ObjectName(baseName + JmxUtil.INTERCEPTOR_KEY + "InvocationContextInterceptor") }; for (ObjectName n : interceptorMBeanNames) { if (expectMbeans) assertTrue(n + " should be registered", mBeanServer.isRegistered(n)); else assertFalse(n + " should not be registered", mBeanServer.isRegistered(n)); } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/jmx/LegacyConfigurationTest.java0000644000175000017500000005462010712333755030706 0ustar twernertwerner/* * JBoss, Home of Professional Open Source. * Copyright 2006, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.cache.jmx; import org.jboss.cache.Version; import org.jboss.cache.config.*; import org.jboss.cache.config.BuddyReplicationConfig.BuddyLocatorConfig; import org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.eviction.*; import org.jboss.cache.loader.FileCacheLoader; import org.jboss.cache.loader.SingletonStoreCacheLoader; import org.jboss.cache.loader.jdbm.JdbmCacheLoader; import org.jboss.cache.lock.IsolationLevel; import org.jboss.cache.multiplexer.MultiplexerTestHelper; import org.jboss.cache.transaction.BatchModeTransactionManagerLookup; import org.jboss.cache.xml.XmlHelper; import org.jgroups.ChannelFactory; import org.jgroups.JChannelFactory; import org.jgroups.jmx.JChannelFactoryMBean; import static org.testng.AssertJUnit.*; import org.testng.annotations.Test; import org.w3c.dom.Element; import javax.management.MBeanServerInvocationHandler; import javax.management.ObjectName; import javax.transaction.TransactionManager; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.List; import java.util.Properties; /** * Test of the CacheLegacyJmxWrapper. * * @author Brian Stansberry * @version $Revision: 4719 $ */ public class LegacyConfigurationTest extends CacheJmxWrapperTestBase { @SuppressWarnings({"deprecation", "unchecked"}) public void testLocalCache() throws Exception { CacheJmxWrapperMBean wrapper = new CacheJmxWrapper(); registerWrapper(wrapper); wrapper = (CacheJmxWrapperMBean) MBeanServerInvocationHandler.newProxyInstance(mBeanServer, mBeanName, CacheJmxWrapperMBean.class, false); wrapper.setBuddyReplicationConfig(getBuddyReplicationConfig()); wrapper.setCacheLoaderConfig(getCacheLoaderConfig()); wrapper.setCacheMode("REPL_SYNC"); wrapper.setClusterName("LocalTest"); wrapper.setClusterConfig(getClusterConfig()); wrapper.setEvictionPolicyConfig(getEvictionPolicyConfig()); wrapper.setFetchInMemoryState(false); wrapper.setInitialStateRetrievalTimeout(100); wrapper.setInactiveOnStartup(true); wrapper.setNodeLockingScheme("OPTIMISTIC"); wrapper.setIsolationLevel("READ_UNCOMMITTED"); wrapper.setLockAcquisitionTimeout(200); wrapper.setReplicationVersion("1.0.1"); wrapper.setReplQueueInterval(15); wrapper.setReplQueueMaxElements(50); wrapper.setSyncReplTimeout(300); wrapper.setSyncCommitPhase(true); wrapper.setSyncRollbackPhase(true); wrapper.setTransactionManagerLookupClass(BatchModeTransactionManagerLookup.class.getName()); wrapper.setExposeManagementStatistics(false); wrapper.setUseRegionBasedMarshalling(true); wrapper.setUseReplQueue(true); Configuration c = wrapper.getConfiguration(); assertEquals("CacheMode", "REPL_SYNC", wrapper.getCacheMode()); assertEquals("CacheMode", CacheMode.REPL_SYNC, c.getCacheMode()); assertEquals("ClusterName", "LocalTest", wrapper.getClusterName()); assertEquals("ClusterName", "LocalTest", c.getClusterName()); assertEquals("FetchInMemoryState", false, wrapper.getFetchInMemoryState()); assertEquals("FetchInMemoryState", false, c.isFetchInMemoryState()); assertEquals("InitialStateRetrievalTimeout", 100, wrapper.getInitialStateRetrievalTimeout()); assertEquals("InitialStateRetrievalTimeout", 100, c.getStateRetrievalTimeout()); assertEquals("InactiveOnStartup", true, wrapper.isInactiveOnStartup()); assertEquals("InactiveOnStartup", true, c.isInactiveOnStartup()); assertEquals("NodeLockingScheme", "OPTIMISTIC", wrapper.getNodeLockingScheme()); assertEquals("NodeLockingScheme", NodeLockingScheme.OPTIMISTIC, c.getNodeLockingScheme()); assertEquals("IsolationLevel", "READ_UNCOMMITTED", wrapper.getIsolationLevel()); assertEquals("IsolationLevel", IsolationLevel.READ_UNCOMMITTED, c.getIsolationLevel()); assertEquals("LockAcquisitionTimeout", 200, wrapper.getLockAcquisitionTimeout()); assertEquals("LockAcquisitionTimeout", 200, c.getLockAcquisitionTimeout()); assertEquals("ReplicationVersion", "1.0.1", wrapper.getReplicationVersion()); assertEquals("ReplicationVersion", Version.getVersionShort("1.0.1"), c.getReplicationVersion()); assertEquals("ReplQueueInterval", 15, wrapper.getReplQueueInterval()); assertEquals("ReplQueueInterval", 15, c.getReplQueueInterval()); assertEquals("ReplQueueMaxElements", 50, wrapper.getReplQueueMaxElements()); assertEquals("ReplQueueMaxElements", 50, c.getReplQueueMaxElements()); assertEquals("SyncReplTimeout", 300, wrapper.getSyncReplTimeout()); assertEquals("SyncReplTimeout", 300, c.getSyncReplTimeout()); assertEquals("SyncCommitPhase", true, wrapper.getSyncCommitPhase()); assertEquals("SyncCommitPhase", true, c.isSyncCommitPhase()); assertEquals("SyncRollbackPhase", true, wrapper.getSyncRollbackPhase()); assertEquals("SyncRollbackPhase", true, c.isSyncRollbackPhase()); assertEquals("TransactionManagerLookupClass", BatchModeTransactionManagerLookup.class.getName(), wrapper.getTransactionManagerLookupClass()); assertEquals("TransactionManagerLookupClass", BatchModeTransactionManagerLookup.class.getName(), c.getTransactionManagerLookupClass()); assertEquals("ExposeManagementStatistics", false, wrapper.getExposeManagementStatistics()); assertEquals("ExposeManagementStatistics", false, c.getExposeManagementStatistics()); assertEquals("UseRegionBasedMarshalling", true, wrapper.getUseRegionBasedMarshalling()); assertEquals("UseRegionBasedMarshalling", true, c.isUseRegionBasedMarshalling()); assertEquals("UseReplQueue", true, wrapper.getUseReplQueue()); assertEquals("UseReplQueue", true, c.isUseReplQueue()); assertEquals("ClusterConfig", getClusterConfig().toString(), wrapper.getClusterConfig().toString()); assertEquals("BuddyReplicationConfig", getBuddyReplicationConfig().toString(), wrapper.getBuddyReplicationConfig().toString()); BuddyReplicationConfig brc = c.getBuddyReplicationConfig(); assertEquals("BR enabled", true, brc.isEnabled()); assertEquals("BR auto grav", false, brc.isAutoDataGravitation()); assertEquals("BR remove find", false, brc.isDataGravitationRemoveOnFind()); assertEquals("BR search backup", false, brc.isDataGravitationSearchBackupTrees()); assertEquals("BR comm timeout", 600000, brc.getBuddyCommunicationTimeout()); assertEquals("BR poolname", "testpool", brc.getBuddyPoolName()); BuddyLocatorConfig blc = brc.getBuddyLocatorConfig(); assertEquals("BR locator", "org.jboss.cache.buddyreplication.TestBuddyLocator", blc.getBuddyLocatorClass()); Properties props = blc.getBuddyLocatorProperties(); assertEquals("BR props", "2", props.get("numBuddies")); assertEquals("CacheLoaderConfig", getCacheLoaderConfig().toString(), wrapper.getCacheLoaderConfig().toString()); CacheLoaderConfig clc = c.getCacheLoaderConfig(); assertEquals("CL passivation", false, clc.isPassivation()); assertEquals("CL passivation", true, clc.isShared()); assertEquals("CL preload", "/foo", clc.getPreload()); List iclcs = clc.getIndividualCacheLoaderConfigs(); IndividualCacheLoaderConfig iclc = iclcs.get(0); assertEquals("CL0 class", FileCacheLoader.class.getName(), iclc.getClassName()); assertEquals("CL0 async", false, iclc.isAsync()); assertEquals("CL0 fetch", true, iclc.isFetchPersistentState()); assertEquals("CL0 ignore", true, iclc.isIgnoreModifications()); assertEquals("CL0 purge", true, iclc.isPurgeOnStartup()); assertEquals("CL0 singleton", true, iclc.getSingletonStoreConfig().isSingletonStoreEnabled()); assertEquals("CL0 singleton class", SingletonStoreCacheLoader.class.getName(), iclc.getSingletonStoreConfig().getSingletonStoreClass()); iclc = iclcs.get(1); assertEquals("CL1 class", JdbmCacheLoader.class.getName(), iclc.getClassName()); assertEquals("CL1 async", true, iclc.isAsync()); assertEquals("CL1 fetch", false, iclc.isFetchPersistentState()); assertEquals("CL1 ignore", false, iclc.isIgnoreModifications()); assertEquals("CL1 purge", false, iclc.isPurgeOnStartup()); assertEquals("CL1 singleton", false, iclc.getSingletonStoreConfig().isSingletonStoreEnabled()); assertEquals("CL1 singleton class", SingletonStoreCacheLoader.class.getName(), iclc.getSingletonStoreConfig().getSingletonStoreClass()); assertEquals("EvictionPolicyConfig", getEvictionPolicyConfig().toString(), wrapper.getEvictionPolicyConfig().toString()); EvictionConfig ec = c.getEvictionConfig(); assertEquals("EC queue size", 20000, ec.getDefaultEventQueueSize()); assertEquals("EC wakeup", 5, ec.getWakeupIntervalSeconds()); assertEquals("EC default pol", LRUPolicy.class.getName(), ec.getDefaultEvictionPolicyClass()); List ercs = ec.getEvictionRegionConfigs(); EvictionRegionConfig erc = ercs.get(0); assertEquals("ERC0 name", "/_default_", erc.getRegionName()); assertEquals("ERC0 queue size", 1000, erc.getEventQueueSize()); LRUConfiguration lru = (LRUConfiguration) erc.getEvictionPolicyConfig(); assertEquals("EPC0 pol", LRUPolicy.class.getName(), lru.getEvictionPolicyClass()); assertEquals("EPC0 maxnodes", 5000, lru.getMaxNodes()); assertEquals("EPC0 ttl", 1000, lru.getTimeToLiveSeconds()); erc = ercs.get(1); assertEquals("ERC1 name", "/org/jboss/data", erc.getRegionName()); assertEquals("ERC1 queue size", 20000, erc.getEventQueueSize()); FIFOConfiguration fifo = (FIFOConfiguration) erc.getEvictionPolicyConfig(); assertEquals("EPC1 pol", FIFOPolicy.class.getName(), fifo.getEvictionPolicyClass()); assertEquals("EPC1 maxnodes", 5000, fifo.getMaxNodes()); erc = ercs.get(2); assertEquals("ERC2 name", "/test", erc.getRegionName()); assertEquals("ERC2 queue size", 20000, erc.getEventQueueSize()); MRUConfiguration mru = (MRUConfiguration) erc.getEvictionPolicyConfig(); assertEquals("EPC2 pol", MRUPolicy.class.getName(), mru.getEvictionPolicyClass()); assertEquals("EPC2 maxnodes", 10000, mru.getMaxNodes()); erc = ercs.get(3); assertEquals("ERC3 name", "/maxAgeTest", erc.getRegionName()); assertEquals("ERC3 queue size", 20000, erc.getEventQueueSize()); lru = (LRUConfiguration) erc.getEvictionPolicyConfig(); assertEquals("EPC3 pol", LRUPolicy.class.getName(), lru.getEvictionPolicyClass()); assertEquals("EPC3 maxnodes", 10000, lru.getMaxNodes()); assertEquals("EPC3 maxage", 10, lru.getMaxAgeSeconds()); assertEquals("EPC3 ttl", 8, lru.getTimeToLiveSeconds()); } @SuppressWarnings("unchecked") public void testRuntimeConfig() throws Exception { CacheJmxWrapperMBean wrapper = new CacheJmxWrapper(); registerWrapper(wrapper); wrapper = (CacheJmxWrapperMBean) MBeanServerInvocationHandler.newProxyInstance(mBeanServer, mBeanName, CacheJmxWrapperMBean.class, false); // Fake a TM by making a bogus proxy TransactionManager tm = (TransactionManager) Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{TransactionManager.class}, new MockInvocationHandler()); wrapper.setTransactionManager(tm); ChannelFactory cf = new JChannelFactory(); wrapper.setMuxChannelFactory(cf); RuntimeConfig rc = wrapper.getConfiguration().getRuntimeConfig(); assertSame("Same TM", tm, wrapper.getTransactionManager()); assertSame("Same TM", tm, rc.getTransactionManager()); assertSame("Same ChannelFactory", cf, wrapper.getMuxChannelFactory()); assertSame("Same ChannelFactory", cf, rc.getMuxChannelFactory()); } @SuppressWarnings("unchecked") public void testLegacyMuxChannelCreation() throws Exception { CacheJmxWrapperMBean wrapper = new CacheJmxWrapper(); registerWrapper(wrapper); wrapper = (CacheJmxWrapperMBean) MBeanServerInvocationHandler.newProxyInstance(mBeanServer, mBeanName, CacheJmxWrapperMBean.class, false); wrapper.setMultiplexerStack(MultiplexerTestHelper.MUX_STACK); JChannelFactory factory = new JChannelFactory(); factory.setDomain("jbc.mux.test"); factory.setExposeChannels(false); factory.setMultiplexerConfig(MultiplexerTestHelper.getClusterConfigElement(getDefaultProperties())); ObjectName on = new ObjectName("jgroups:service=Mux"); mBeanServer.registerMBean(new org.jgroups.jmx.JChannelFactory(factory), on); wrapper.setMultiplexerService((JChannelFactoryMBean) MBeanServerInvocationHandler.newProxyInstance(mBeanServer, on, JChannelFactoryMBean.class, false)); wrapper.start(); RuntimeConfig rc = wrapper.getConfiguration().getRuntimeConfig(); assertNotNull("Channel created", rc.getChannel()); } protected static Element getBuddyReplicationConfig() throws Exception { String xmlString = "true\n" + " 600000\n" + " org.jboss.cache.buddyreplication.TestBuddyLocator\n" + " numBuddies = 2\n" + " testpool" + " false\n" + " false\n" + " false" + ""; return XmlHelper.stringToElement(xmlString); } protected static Element getCacheLoaderConfig() throws Exception { String xml = "\n" + "false\n" + "/foo\n" + "true\n" + "\n" + "org.jboss.cache.loader.FileCacheLoader\n" + "" + " location=/tmp\n" + "\n" + "false\n" + "true\n" + "true\n" + "true\n" + "" + "true" + "" + "\n" + "\n" + "org.jboss.cache.loader.jdbm.JdbmCacheLoader\n" + "" + " location=/home/bstansberry\n" + "\n" + "true\n" + "false\n" + "false\n" + "false\n" + "" + "false" + "" + "\n" + ""; return XmlHelper.stringToElement(xml); } protected static Element getEvictionPolicyConfig() throws Exception { String xml = "\n" + "5\n" + "20000\n" + "org.jboss.cache.eviction.LRUPolicy\n" + "\n" + " 5000\n" + " 1000\n" + "\n" + "\n" + " 5000\n" + "\n" + "\n" + " 10000\n" + "\n" + "\n" + " 10000\n" + " 8\n" + " 10\n" + "\n" + " \n"; return XmlHelper.stringToElement(xml); } protected static Element getClusterConfig() throws Exception { String xml = "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + ""; return XmlHelper.stringToElement(xml); } protected String getDefaultProperties() { return "UDP(mcast_addr=224.0.0.36;mcast_port=55566;ip_ttl=32;" + "mcast_send_buf_size=150000;mcast_recv_buf_size=80000):" + "PING(timeout=1000;num_initial_members=2):" + "MERGE2(min_interval=5000;max_interval=10000):" + "FD_SOCK:" + "VERIFY_SUSPECT(timeout=1500):" + "pbcast.NAKACK(gc_lag=50;max_xmit_size=8192;retransmit_timeout=600,1200,2400,4800):" + "UNICAST(timeout=600,1200,2400,4800):" + "pbcast.STABLE(desired_avg_gossip=20000):" + "FRAG(frag_size=8192;down_thread=false;up_thread=false):" + "pbcast.GMS(join_timeout=5000;join_retry_timeout=2000;" + "shun=false;print_local_addr=true):" + "pbcast.STATE_TRANSFER"; } class MockInvocationHandler implements InvocationHandler { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return null; } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/jmx/LifecycleNotificationTest.java0000644000175000017500000001016110665031725031207 0ustar twernertwerner/* * JBoss, Home of Professional Open Source. * Copyright 2006, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.cache.jmx; import static org.testng.AssertJUnit.assertEquals; import java.util.LinkedList; import java.util.List; import javax.management.AttributeChangeNotification; import javax.management.Notification; import javax.management.NotificationListener; /** * A LifecycleNotificationTest. * * @author Brian Stansberry * @version $Revision: 4444 $ */ public class LifecycleNotificationTest extends CacheJmxWrapperTestBase { @SuppressWarnings("unchecked") public void testGetStateAndStateNotification() throws Exception { CacheJmxWrapper wrapper = createWrapper(createConfiguration()); StateNotificationListener listener = new StateNotificationListener(); wrapper.addNotificationListener(listener, null, null); assertEquals("Correct state after instanitation", CacheJmxWrapperMBean.UNREGISTERED, wrapper.getState()); registerWrapper(wrapper); assertEquals("Correct state after registration", CacheJmxWrapperMBean.REGISTERED, wrapper.getState()); wrapper.create(); assertEquals("Correct state after create", CacheJmxWrapperMBean.CREATED, wrapper.getState()); wrapper.start(); assertEquals("Correct state after start", CacheJmxWrapperMBean.STARTED, wrapper.getState()); wrapper.stop(); assertEquals("Correct state after stop", CacheJmxWrapperMBean.STOPPED, wrapper.getState()); wrapper.destroy(); assertEquals("Correct state after destroy", CacheJmxWrapperMBean.DESTROYED, wrapper.getState()); unregisterWrapper(); assertEquals("Correct state after unregistration", CacheJmxWrapperMBean.UNREGISTERED, wrapper.getState()); System.out.println(listener.notifications); assertEquals("Correct number of notifications received", 4, listener.notifications.size()); assertEquals("Correct first notification", new Integer(CacheJmxWrapperMBean.STARTING), listener.notifications.get(0)); assertEquals("Correct second notification", new Integer(CacheJmxWrapperMBean.STARTED), listener.notifications.get(1)); assertEquals("Correct third notification", new Integer(CacheJmxWrapperMBean.STOPPING), listener.notifications.get(2)); assertEquals("Correct fourth notification", new Integer(CacheJmxWrapperMBean.STOPPED), listener.notifications.get(3)); } private static class StateNotificationListener implements NotificationListener { private List notifications = new LinkedList(); public void handleNotification(Notification msg, Object handback) { if (msg instanceof AttributeChangeNotification) { AttributeChangeNotification change = (AttributeChangeNotification) msg; String attrName = change.getAttributeName(); Object newValue = change.getNewValue(); if ("State".equals(attrName) && newValue != null && newValue instanceof Integer) { notifications.add((Integer) newValue); return; } } } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/jmx/CacheJmxWrapperTest.java0000644000175000017500000003431611017455042027766 0ustar twernertwernerpackage org.jboss.cache.jmx; import org.jboss.cache.Cache; import org.jboss.cache.CacheException; import org.jboss.cache.CacheStatus; import org.jboss.cache.Fqn; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.notifications.annotation.CacheListener; import org.jboss.cache.notifications.annotation.CacheStarted; import org.jboss.cache.notifications.annotation.CacheStopped; import org.jboss.cache.notifications.event.Event; import org.jboss.cache.transaction.DummyTransactionManagerLookup; import org.jgroups.Address; import org.jgroups.stack.IpAddress; import static org.testng.AssertJUnit.*; import org.testng.annotations.Test; import javax.management.ObjectName; import javax.transaction.TransactionManager; import java.util.List; /** * Tests the JMX wrapper class around the cache. * * @author Manik Surtani * @author Brian Stansberry */ @Test(groups = "functional") public class CacheJmxWrapperTest extends CacheJmxWrapperTestBase { public void testCacheMBeanBinding() throws Exception { registerWrapper(); assertTrue("Should be registered", mBeanServer.isRegistered(mBeanName)); } public void testSetCacheObjectName() throws Exception { ObjectName on = new ObjectName("jboss.cache:test=SetCacheObjectName"); boolean registered = false; try { CacheJmxWrapper wrapper = createWrapper(createConfiguration()); wrapper.setCacheObjectName(on.getCanonicalName()); // Register under the standard name registerWrapper(wrapper); // Should be registered under 'on' registered = mBeanServer.isRegistered(on); assertTrue("Registered with configured name", registered); assertEquals("Configured name retained", on.getCanonicalName(), wrapper.getCacheObjectName()); wrapper.create(); wrapper.start(); interceptorRegistrationTest(on.getCanonicalName(), true); wrapper.stop(); wrapper.destroy(); interceptorRegistrationTest(false); } finally { if (registered) mBeanServer.unregisterMBean(on); } } public void testGetCacheObjectName() throws Exception { ObjectName on = new ObjectName("jboss.cache:test=SetCacheObjectName"); String str = on.getCanonicalName(); CacheJmxWrapper wrapper = createWrapper(createConfiguration()); wrapper.setCacheObjectName(str); assertEquals("Setter and getter match", str, wrapper.getCacheObjectName()); // Go back to the default wrapper.setCacheObjectName(null); assertEquals("Got default ObjectName", JmxUtil.PREFIX + CLUSTER_NAME, wrapper.getCacheObjectName()); registerWrapper(wrapper); assertEquals("Returns standard name", mBeanName, new ObjectName(wrapper.getCacheObjectName())); } public void testGetConfiguration1() throws Exception { CacheJmxWrapperMBean wrapper = registerWrapper(); Configuration cfgFromJmx = wrapper.getConfiguration(); assertNotNull("Got a configuration", cfgFromJmx); assertSame(cache.getConfiguration(), cfgFromJmx); } public void testGetConfiguration2() throws Exception { Configuration cfg = createConfiguration(); CacheJmxWrapperMBean wrapper = registerWrapper(cfg); Configuration cfgFromJmx = wrapper.getConfiguration(); assertNotNull("Got a configuration", cfgFromJmx); assertSame(cfg, cfgFromJmx); } /** * Note that this is a bit of a 'white box' test as it assumes that the * returned String equals Configuration.toString(). That could change and * break this test; if it does, and there's nothing wrong with the * change, just modify the test. * * @throws Exception */ public void testPrintConfigurationAsString1() throws Exception { CacheJmxWrapperMBean wrapper = registerWrapper(); String cfgFromJmx = wrapper.printConfigurationAsString(); assertEquals(cache.getConfiguration().toString(), cfgFromJmx); } /** * Note that this is a bit of a 'white box' test as it assumes that the * returned String equals Configuration.toString(). That could change and * break this test; if it does, and there's nothing wrong with the * change, just modify the test. * * @throws Exception */ public void testPrintConfigurationAsString2() throws Exception { Configuration cfg = createConfiguration(); CacheJmxWrapperMBean wrapper = registerWrapper(cfg); wrapper.create(); wrapper.start(); String cfgFromJmx = wrapper.printConfigurationAsString(); assertEquals(wrapper.getCache().getConfiguration().toString(), cfgFromJmx); } /** * Note that this is a bit of a 'white box' test as it checks * the currently coded HTML format and assumes that the HTML content is * derived from Configuration.toString(). That could change and break * this test; if it does, and there's nothing wrong with the * change, just modify the test. * * @throws Exception */ public void testPrintConfigurationAsHtml1() throws Exception { CacheJmxWrapperMBean wrapper = registerWrapper(); String cfgFromJmx = wrapper.printConfigurationAsHtmlString(); assertEquals(CacheJmxWrapper.formatHtml(cache.getConfiguration().toString()), cfgFromJmx); checkHtml(cfgFromJmx, false); } /** * Note that this is a bit of a 'white box' test as it checks * the currently coded HTML format and assumes that the HTML content is * derived from Configuration.toString(). That could change and break * this test; if it does, and there's nothing wrong with the * change, just modify the test. * * @throws Exception */ public void testPrintConfigurationAsHtml2() throws Exception { Configuration cfg = createConfiguration(); CacheJmxWrapperMBean wrapper = registerWrapper(cfg); wrapper.create(); wrapper.start(); String cfgFromJmx = wrapper.printConfigurationAsHtmlString(); assertEquals(CacheJmxWrapper.formatHtml(wrapper.getCache().getConfiguration().toString()), cfgFromJmx); checkHtml(cfgFromJmx, false); } @SuppressWarnings("unchecked") public void testGetCache() throws Exception { registerWrapper(); // have to start the cache before we'll have a root cache.start(); Cache cacheJmx = (Cache) mBeanServer.getAttribute(mBeanName, "Cache"); cacheJmx.getRoot().put("key", "value"); assertEquals("value", cache.getRoot().get("key")); Fqn fqn = Fqn.fromString("/testing/jmx"); cache.put(fqn, "key", "value"); assertEquals("value", cacheJmx.get(fqn, "key")); } public void testPrintCacheDetails() throws Exception { printCacheDetailsTest(false); } /** * Note that this is a bit of a 'white box' test as it checks * the currently coded HTML format. That could change and break * this test; if it does, and there's nothing wrong with the * change, just modify the test. * * @throws Exception */ public void testPrintCacheDetailsAsHtml() throws Exception { String html = printCacheDetailsTest(true); checkHtml(html, true); } public void testPrintLockInfo() throws Exception { printLockInfoTest(false); } /** * Note that this is a bit of a 'white box' test as it checks * the currently coded HTML format. That could change and break * this test; if it does, and there's nothing wrong with the * change, just modify the test. * * @throws Exception */ public void testPrintLockInfoAsHtml() throws Exception { printLockInfoTest(true); } public void testGetLocalAddress() throws Exception { Configuration c = createConfiguration(); c.setCacheMode(CacheMode.REPL_ASYNC); CacheJmxWrapperMBean wrapper = registerWrapper(c); wrapper.start(); assertTrue("Got an IpAddress", wrapper.getLocalAddress() instanceof IpAddress); } public void testGetMembers() throws Exception { Configuration c = createConfiguration(); c.setCacheMode(CacheMode.REPL_ASYNC); CacheJmxWrapperMBean wrapper = registerWrapper(c); wrapper.start(); c = createConfiguration(); c.setCacheMode(CacheMode.REPL_ASYNC); Cache cache2 = null; try { cache2 = createCache(c); cache2.start(); Cache[] caches = new Cache[]{wrapper.getCache(), cache2}; TestingUtil.blockUntilViewsReceived(caches, 5000); Address addr = wrapper.getLocalAddress(); assertNotNull("Got an Address", addr); List members = wrapper.getMembers(); assertNotNull("Got members", addr); assertEquals("Got correct number of members", 2, members.size()); assertTrue("I am a member", members.contains(addr)); } finally { if (cache2 != null) { cache2.destroy(); } } } public void testDuplicateInvocation() throws Exception { CacheJmxWrapperMBean cache = registerWrapper(); cache.create(); cache.start(); cache.create(); cache.start(); cache.getCache().put(Fqn.fromString("/a/b/c"), null); assertTrue(cache.getNumberOfNodes() > 0); assertEquals(0, cache.getNumberOfAttributes()); System.out.println("cache locks before restart:\n" + cache.printLockInfo()); cache.destroy(); cache.start(); System.out.println("cache locks after restart:\n" + cache.printLockInfo()); assertEquals(0, cache.getNumberOfNodes()); assertEquals(0, cache.getNumberOfAttributes()); cache.stop(); cache.destroy(); cache.stop(); cache.destroy(); } public void testFailedStart() throws Exception { CacheJmxWrapper wrapper = new CacheJmxWrapper(createCache(createConfiguration())); registerWrapper(wrapper); assertEquals("Correct state", CacheStatus.INSTANTIATED, wrapper.getCacheStatus()); wrapper.create(); DisruptLifecycleListener listener = new DisruptLifecycleListener(); listener.setDisrupt(true); wrapper.getCache().addCacheListener(listener); assertEquals("Correct state", CacheStatus.CREATED, wrapper.getCacheStatus()); try { wrapper.start(); fail("Listener did not prevent start"); } catch (CacheException good) { } assertEquals("Correct state", CacheStatus.FAILED, wrapper.getCacheStatus()); listener.setDisrupt(false); wrapper.start(); assertEquals("Correct state", CacheStatus.STARTED, wrapper.getCacheStatus()); wrapper.getCache().put(Fqn.fromString("/a/b/c"), null); assertTrue(wrapper.getNumberOfNodes() > 0); assertEquals(0, wrapper.getNumberOfAttributes()); listener.setDisrupt(true); // need to re-add the listener since the failed start would have nullified the notifier. cache.addCacheListener(listener); try { wrapper.stop(); fail("Listener did not prevent stop"); } catch (CacheException good) { } assertEquals("Correct state", CacheStatus.FAILED, wrapper.getCacheStatus()); listener.setDisrupt(false); wrapper.stop(); assertEquals("Correct state", CacheStatus.STOPPED, wrapper.getCacheStatus()); wrapper.destroy(); assertEquals("Correct state", CacheStatus.DESTROYED, wrapper.getCacheStatus()); } private String printCacheDetailsTest(boolean html) throws Exception { CacheJmxWrapperMBean wrapper = registerWrapper(); // have to start the cache before we'll have a root cache.start(); Fqn fqn = Fqn.fromString("/testing/jmx"); cache.put(fqn, "foobar", "barfoo"); assertEquals("barfoo", cache.get(fqn, "foobar")); String details = html ? wrapper.printCacheDetailsAsHtml() : wrapper.printCacheDetails(); assertTrue("Details include testing", details.contains("testing")); assertTrue("Details include jmx", details.contains("jmx")); assertTrue("Details include foobar", details.contains("foobar")); assertTrue("Details include barfoo", details.contains("barfoo")); return details; } private String printLockInfoTest(boolean html) throws Exception { Configuration config = createConfiguration(); config.setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName()); Cache c = createCache(config); CacheJmxWrapperMBean wrapper = registerWrapper(c); // wrapper.setManageCacheLifecycle(true); wrapper.create(); wrapper.start(); TransactionManager tm = config.getRuntimeConfig().getTransactionManager(); tm.begin(); try { Fqn fqn = Fqn.fromString("/testing/jmx"); cache.put(fqn, "foobar", "barfoo"); String locks = html ? wrapper.printLockInfoAsHtml() : wrapper.printLockInfo(); assertTrue("Details include testing", locks.contains("testing")); assertTrue("Details include jmx", locks.contains("jmx")); return locks; } catch (Exception e) { tm.setRollbackOnly(); throw e; } finally { tm.commit(); } } private void checkHtml(String html, boolean checkBR) { if (checkBR) assertTrue("Has Brian Stansberry * @version $Revision: 4941 $ */ @Test(groups = "functional") public class InterceptorRegistrationTest extends CacheJmxWrapperTestBase { /** * Confirms interceptor mbeans are registered if the following events * occur: *

* cache.start(); * wrapper creation and registration. * * @throws Exception */ public void testInterceptorMBeans1() throws Exception { // have to start the cache to have any interceptors createCache(createConfiguration()); cache.start(); CacheJmxWrapperMBean wrapper = registerWrapper(cache); assertTrue("Should be registered", mBeanServer.isRegistered(mBeanName)); interceptorRegistrationTest(true); // These should be ignored because we // never did wrapper.create()/start() wrapper.stop(); wrapper.destroy(); // Should still be registered interceptorRegistrationTest(true); unregisterWrapper(); interceptorRegistrationTest(false); } /** * Confirms interceptor mbeans are registered if the following events * occur: *

* cache.start(); * wrapper creation and and start * wrapper registration. * * @throws Exception */ public void testInterceptorMBeans2() throws Exception { // have to start the cache to have any interceptors createCache(createConfiguration()); cache.start(); CacheJmxWrapperMBean wrapper = new CacheJmxWrapper(cache); wrapper.start(); wrapper = registerWrapper(wrapper); assertTrue("Should be registered", mBeanServer.isRegistered(mBeanName)); interceptorRegistrationTest(true); wrapper.stop(); wrapper.destroy(); // Should still no longer be registered interceptorRegistrationTest(false); unregisterWrapper(); interceptorRegistrationTest(false); } /** * Confirms interceptor mbeans are registered if the following events * occur: *

* Cache not injected * wrapper registered; * wrapper created and started. * * @throws Exception */ public void testInterceptorMBeans3() throws Exception { CacheJmxWrapperMBean wrapper = registerWrapper(createConfiguration()); assertTrue("Should be registered", mBeanServer.isRegistered(mBeanName)); // have to start the cache to have any interceptors wrapper.create(); wrapper.start(); interceptorRegistrationTest(true); wrapper.stop(); wrapper.destroy(); // Destroy should unregister if we are managing interceptorRegistrationTest(false); unregisterWrapper(); interceptorRegistrationTest(false); } /** * Confirms interceptor mbeans are registered if the following events * occur: *

* Cache not injected * wrapper created and started. * wrapper registered * * @throws Exception */ public void testInterceptorMBeans4() throws Exception { CacheJmxWrapper wrapper = createWrapper(createConfiguration()); // have to start the cache to have any interceptors wrapper.create(); wrapper.start(); registerWrapper(wrapper); assertTrue("Should be registered", mBeanServer.isRegistered(mBeanName)); interceptorRegistrationTest(true); wrapper.stop(); wrapper.destroy(); // Destroy should unregister if we are managing interceptorRegistrationTest(false); unregisterWrapper(); interceptorRegistrationTest(false); } /** * Confirms interceptor mbeans are registered if the following events * occur: *

* cache constructed; * wrapper constructed and registered with manageCacheLifecycle=true * wrapper created and started * * @throws Exception */ public void testInterceptorMBeans5() throws Exception { CacheJmxWrapperMBean wrapper = registerWrapper(); // wrapper.setManageCacheLifecycle(true); assertTrue("Should be registered", mBeanServer.isRegistered(mBeanName)); // have to start the cache to have any interceptors wrapper.create(); wrapper.start(); interceptorRegistrationTest(true); wrapper.stop(); wrapper.destroy(); // Destroy should unregister if we are managing interceptorRegistrationTest(false); unregisterWrapper(); interceptorRegistrationTest(false); } /** * Confirms interceptor mbeans are registered if the following events * occur: *

* cache constructed; * wrapper constructed and registered * wrapper created and started * * @throws Exception */ public void testInterceptorMBeans6() throws Exception { CacheJmxWrapperMBean wrapper = registerWrapper(); assertTrue("Should be registered", mBeanServer.isRegistered(mBeanName)); // have to start the cache to have any interceptors wrapper.create(); wrapper.start(); interceptorRegistrationTest(true); wrapper.stop(); wrapper.destroy(); interceptorRegistrationTest(false); unregisterWrapper(); interceptorRegistrationTest(false); } /** * Confirms interceptor mbeans are registered if the following events * occur: *

* cache constructed; * wrapper created and started * wrapper registered * * @throws Exception */ public void testInterceptorMBeans7() throws Exception { CacheJmxWrapperMBean wrapper = new CacheJmxWrapper(createCache(createConfiguration())); // have to start the cache to have any interceptors wrapper.create(); wrapper.start(); wrapper = registerWrapper(wrapper); assertTrue("Should be registered", mBeanServer.isRegistered(mBeanName)); interceptorRegistrationTest(true); wrapper.stop(); wrapper.destroy(); interceptorRegistrationTest(false); unregisterWrapper(); interceptorRegistrationTest(false); } /** * Tests that setting registerInterceptors=false disables interceptor * registration when the wrapper is registered before create/start * are called. * * @throws Exception */ public void testRegisterInterceptors1() throws Exception { CacheJmxWrapper wrapper = createWrapper(createConfiguration()); wrapper.setRegisterInterceptors(false); registerWrapper(wrapper); assertTrue("Should be registered", mBeanServer.isRegistered(mBeanName)); wrapper.create(); wrapper.start(); interceptorRegistrationTest(false); wrapper.stop(); wrapper.destroy(); interceptorRegistrationTest(false); unregisterWrapper(); interceptorRegistrationTest(false); } /** * Tests that setting registerInterceptors=false disables interceptor * registration when the wrapper is registered after create/start * are called. * * @throws Exception */ public void testRegisterInterceptors2() throws Exception { CacheJmxWrapper wrapper = createWrapper(createConfiguration()); wrapper.setRegisterInterceptors(false); wrapper.create(); wrapper.start(); registerWrapper(wrapper); assertTrue("Should be registered", mBeanServer.isRegistered(mBeanName)); interceptorRegistrationTest(false); wrapper.stop(); wrapper.destroy(); interceptorRegistrationTest(false); unregisterWrapper(); interceptorRegistrationTest(false); } public void testExposeManagementStatistics1() throws Exception { Configuration cfg = createConfiguration(); cfg.setExposeManagementStatistics(false); CacheJmxWrapper wrapper = createWrapper(cfg); registerWrapper(cfg); assertTrue("Should be registered", mBeanServer.isRegistered(mBeanName)); wrapper.create(); wrapper.start(); interceptorRegistrationTest(false); wrapper.stop(); wrapper.destroy(); interceptorRegistrationTest(false); unregisterWrapper(); interceptorRegistrationTest(false); } public void testExposeManagementStatistics2() throws Exception { Configuration cfg = createConfiguration(); cfg.setExposeManagementStatistics(false); CacheJmxWrapper wrapper = createWrapper(cfg); wrapper.create(); wrapper.start(); registerWrapper(wrapper); assertTrue("Should be registered", mBeanServer.isRegistered(mBeanName)); interceptorRegistrationTest(false); wrapper.stop(); wrapper.destroy(); interceptorRegistrationTest(false); unregisterWrapper(); interceptorRegistrationTest(false); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/jmx/OptimisticNotificationTest.java0000644000175000017500000000060410620514330031422 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.jmx; /** * @author Manik Surtani (manik@jboss.org) */ public class OptimisticNotificationTest extends NotificationTest { public OptimisticNotificationTest() { optimistic = true; } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/jmx/NotificationTest.java0000644000175000017500000004035510732752176027404 0ustar twernertwernerpackage org.jboss.cache.jmx; import org.jboss.cache.CacheFactory; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.factories.UnitTestCacheConfigurationFactory; import org.jboss.cache.factories.XmlConfigurationParser; import org.jboss.cache.loader.CacheLoader; import org.jboss.cache.xml.XmlHelper; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import org.w3c.dom.Element; import javax.management.MBeanServer; import javax.management.MBeanServerFactory; import javax.management.Notification; import javax.management.NotificationListener; import javax.management.ObjectName; import java.util.EnumSet; import java.util.HashMap; /** * Functional tests for CacheJmxWrapper broadcast of cache event notifications * * @author Jerry Gauthier * @version $Id: NotificationTest.java 4912 2007-12-21 14:58:06Z manik.surtani@jboss.com $ */ @Test(groups = {"functional"}) public class NotificationTest { protected static final String CLUSTER_NAME = "NotificationTestCluster"; protected static final String CAPITAL = "capital"; protected static final String CURRENCY = "currency"; protected static final String POPULATION = "population"; protected static final String EUROPE_NODE = "Europe"; public enum Type { STARTED, STOPPED, PRECREATE, POSTCREATE, PREEVICT, POSTEVICT, PRELOAD, POSTLOAD, PREREMOVE, POSTREMOVE, PREVISIT, POSTVISIT, PREMODIFY, POSTMODIFY, PREACTIVATE, POSTACTIVATE, PREPASSIVATE, POSTPASSIVATE, VIEWCHANGE } protected MBeanServer m_server; protected EnumSet events = EnumSet.noneOf(Type.class); protected CacheSPI cache = null; protected boolean optimistic = false; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { m_server = MBeanServerFactory.createMBeanServer(); Object cacheMBean = createCacheAndJmxWrapper(); // bind manually for now. ObjectName mgmt = getWrapperObjectName(); m_server.registerMBean(cacheMBean, mgmt); } protected Object createCacheAndJmxWrapper() throws Exception { cache = createCache(CLUSTER_NAME); return new CacheJmxWrapper(cache); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { try { cleanup(); } finally { // make sure we stop the mbean server if (m_server != null) MBeanServerFactory.releaseMBeanServer(m_server); } } protected void cleanup() throws Exception { events.clear(); destroyCache(); if (m_server != null) { ObjectName mgmt = getWrapperObjectName(); if (m_server.isRegistered(mgmt)) m_server.unregisterMBean(mgmt); } } protected void destroyCache() { if (cache != null) { // stop the cache before the listener is unregistered //cache1.stop(); cache.destroy(); cache = null; } } protected ObjectName getWrapperObjectName() throws Exception { return new ObjectName(JmxUtil.PREFIX + CLUSTER_NAME); } public void testNotifications() throws Exception { assertNotNull("MBeanServer is null.", m_server); assertNotNull("Cache is null.", cache); ObjectName mgmt = getWrapperObjectName(); MyListener listener = new MyListener(mgmt); m_server.addNotificationListener(mgmt, listener, null, null); // start the cache after registering listener - this will trigger CacheStarted // since cache is defined with cluster, thiswill also trigger ViewChange cache.start(); // add a node - this will trigger NodeCreated, NodeModify(pre/post) and NodeModified HashMap albania = new HashMap(4); albania.put(CAPITAL, "Tirana"); albania.put(CURRENCY, "Lek"); cache.put("Europe/Albania", albania); // modify a node - this will trigger NodeModified and NodeModify(pre/post) cache.put("Europe/Albania", POPULATION, 3563112); // retrieve an attribute - this will trigger NodeVisited Fqn key = Fqn.fromString("Europe/Albania"); assertNotNull("Retrieval error: expected to retrieve " + CURRENCY + " for " + key, cache.get(key, CURRENCY)); // evict the node - this will trigger NodePassivate, NodeEvicted and NodeEvict(pre/post) cache.evict(key); // retrieve the attribute again - this will trigger NodeVisited and NodeActivate assertNotNull("Retrieval error: expected to retrieve " + CURRENCY + " for " + key, cache.get(key, CURRENCY)); // remove the node - this will trigger NodeRemoved and NodeRemove(pre/post) cache.removeNode(key); // clean up before stopping the cache CacheLoader cl = cache.getCacheLoaderManager().getCacheLoader(); cl.remove(Fqn.fromString(EUROPE_NODE)); // stop the cache cache.stop(); m_server.removeNotificationListener(mgmt, listener); // run the tests assertTrue("Expected CacheStarted notification", events.contains(Type.STARTED)); assertTrue("Expected CacheStopped notification", events.contains(Type.STOPPED)); assertTrue("Expected NodeCreated notification", events.contains(Type.PRECREATE)); assertTrue("Expected NodeCreated notification", events.contains(Type.POSTCREATE)); assertTrue("Expected NodeEvicted notification", events.contains(Type.PREEVICT)); assertTrue("Expected NodeEvicted notification", events.contains(Type.POSTEVICT)); assertTrue("Expected NodeLoaded notification", events.contains(Type.PRELOAD)); assertTrue("Expected NodeLoaded notification", events.contains(Type.POSTLOAD)); assertTrue("Expected NodeVisited notification", events.contains(Type.PREVISIT)); assertTrue("Expected NodeVisited notification", events.contains(Type.POSTVISIT)); assertTrue("Expected NodeActivated notification", events.contains(Type.PREACTIVATE)); assertTrue("Expected NodeActivated notification", events.contains(Type.POSTACTIVATE)); assertTrue("Expected NodeModified notification", events.contains(Type.PREMODIFY)); assertTrue("Expected NodeModified notification", events.contains(Type.POSTMODIFY)); assertTrue("Expected NodePassivated notification", events.contains(Type.PREPASSIVATE)); assertTrue("Expected NodePassivated notification", events.contains(Type.POSTPASSIVATE)); assertTrue("Expected NodeRemoved notification", events.contains(Type.PREREMOVE)); assertTrue("Expected NodeRemoved notification", events.contains(Type.POSTREMOVE)); assertTrue("Expected ViewChange notification", events.contains(Type.VIEWCHANGE)); validateHealthyListener(listener); } public void testEarlyRegistration() throws Exception { // undo setup cleanup(); CacheJmxWrapper wrapper = new CacheJmxWrapper(); ObjectName mgmt = getWrapperObjectName(); m_server.registerMBean(wrapper, mgmt); MyListener listener = new MyListener(mgmt); m_server.addNotificationListener(mgmt, listener, null, null); cache = createCache(CLUSTER_NAME); wrapper.setCache(cache); cache.start(); try { assertTrue("Expected CacheStarted notification", events.contains(Type.STARTED)); validateHealthyListener(listener); } finally { cache.stop(); } } public void testLateRegistration() throws Exception { assertNotNull("MBeanServer is null.", m_server); assertNotNull("Cache is null.", cache); // start the cache before registering listener cache.start(); try { ObjectName mgmt = getWrapperObjectName(); MyListener listener = new MyListener(mgmt); m_server.addNotificationListener(mgmt, listener, null, null); // add a node - this will trigger NodeCreated, NodeModify(pre/post) and NodeModified HashMap albania = new HashMap(4); albania.put(CAPITAL, "Tirana"); albania.put(CURRENCY, "Lek"); cache.put("Europe/Albania", albania); // run the tests assertTrue("Expected NodeModified notification", events.contains(Type.PREMODIFY)); assertTrue("Expected NodeModified notification", events.contains(Type.POSTMODIFY)); validateHealthyListener(listener); } finally { cache.stop(); } } public void testListenerRemoval() throws Exception { assertNotNull("MBeanServer is null.", m_server); assertNotNull("Cache is null.", cache); ObjectName mgmt = getWrapperObjectName(); MyListener listener = new MyListener(mgmt); m_server.addNotificationListener(mgmt, listener, null, null); // start the cache after registering listener - this will trigger CacheStarted // since cache is defined with cluster, thiswill also trigger ViewChange cache.start(); boolean ok = false; try { assertTrue("Expected CacheStarted notification", events.contains(Type.STARTED)); m_server.removeNotificationListener(mgmt, listener); ok = true; } finally { cache.stop(); if (ok) { assertFalse("Expected no CacheStopped notification", events.contains(Type.STOPPED)); validateHealthyListener(listener); } } } private CacheSPI createCache(String clusterName) throws Exception { Configuration config = createConfiguration(clusterName); CacheFactory factory = new DefaultCacheFactory(); CacheSPI cache = (CacheSPI) factory.createCache(config, false); cache.create(); // start the cache after the listener has been registered //cache.start(); return cache; } protected Configuration createConfiguration(String clusterName) throws Exception { Configuration config = UnitTestCacheConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC); config.setCacheMode(Configuration.CacheMode.REPL_SYNC); config.setCacheLoaderConfig(getCacheLoaderConfig("location=" + getTempDir())); config.setExposeManagementStatistics(true); config.setClusterName(clusterName); if (optimistic) { config.setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); config.setNodeLockingScheme("OPTIMISTIC"); } return config; } private static String getTempDir() { return System.getProperty("java.io.tempdir", "/tmp"); } private static boolean getPre(Object data) { assertNotNull("User data is null, should be Object[]", data); assertTrue("User data is " + data.getClass().getName() + ", should be Object[]", data instanceof Object[]); Object[] parms = (Object[]) data; assertTrue("Parameter is " + parms[1].getClass().getName() + ", should be Boolean", parms[1] instanceof Boolean); return (Boolean) parms[1]; } protected static CacheLoaderConfig getCacheLoaderConfig(String properties) throws Exception { String xml = "\n" + "true\n" + "\n" + "true\n" + "\n" + "org.jboss.cache.loader.FileCacheLoader\n" + "" + properties + "\n" + "false\n" + "false\n" + "false\n" + "\n" + ""; Element element = XmlHelper.stringToElement(xml); return XmlConfigurationParser.parseCacheLoaderConfig(element); } private static void validateHealthyListener(MyListener listener) { if (listener.failure != null) throw listener.failure; if (listener.exception != null) throw listener.exception; } private class MyListener implements NotificationListener { private RuntimeException exception; private AssertionError failure; private final String emitterObjectName; MyListener(ObjectName emitter) { this.emitterObjectName = emitter.getCanonicalName(); } public void handleNotification(Notification notification, Object handback) { try { String type = notification.getType(); Object userData = notification.getUserData(); if (type.equals(CacheNotificationBroadcaster.NOTIF_CACHE_STARTED)) { events.add(Type.STARTED); assertEquals("Correct object name in start notification", emitterObjectName, userData); } else if (type.equals(CacheNotificationBroadcaster.NOTIF_CACHE_STOPPED)) { events.add(Type.STOPPED); assertEquals("Correct object name in stop notification", emitterObjectName, userData); } else if (type.equals(CacheNotificationBroadcaster.NOTIF_NODE_CREATED)) { if (getPre(userData)) { events.add(Type.PRECREATE); } else { events.add(Type.POSTCREATE); } } else if (type.equals(CacheNotificationBroadcaster.NOTIF_NODE_EVICTED)) { if (getPre(userData)) { events.add(Type.PREEVICT); } else { events.add(Type.POSTEVICT); } } else if (type.equals(CacheNotificationBroadcaster.NOTIF_NODE_LOADED)) { if (getPre(userData)) { events.add(Type.PRELOAD); } else { events.add(Type.POSTLOAD); } } else if (type.equals(CacheNotificationBroadcaster.NOTIF_NODE_REMOVED)) { if (getPre(userData)) { events.add(Type.PREREMOVE); } else { events.add(Type.POSTREMOVE); } } else if (type.equals(CacheNotificationBroadcaster.NOTIF_NODE_VISITED)) { if (getPre(userData)) { events.add(Type.PREVISIT); } else { events.add(Type.POSTVISIT); } } else if (type.equals(CacheNotificationBroadcaster.NOTIF_VIEW_CHANGED)) { events.add(Type.VIEWCHANGE); } else if (type.equals(CacheNotificationBroadcaster.NOTIF_NODE_ACTIVATED)) { if (getPre(userData)) { events.add(Type.PREACTIVATE); } else { events.add(Type.POSTACTIVATE); } } else if (type.equals(CacheNotificationBroadcaster.NOTIF_NODE_MODIFIED)) { if (getPre(userData)) { events.add(Type.PREMODIFY); } else { events.add(Type.POSTMODIFY); } } else if (type.equals(CacheNotificationBroadcaster.NOTIF_NODE_PASSIVATED)) { if (getPre(userData)) { events.add(Type.PREPASSIVATE); } else { events.add(Type.POSTPASSIVATE); } } } catch (RuntimeException e) { // Store so the test can rethrow exception = e; } catch (AssertionError e) { // Store so the test can rethrow failure = e; } } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/replicated/0000755000175000017500000000000011376173766024571 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/replicated/SyncReplTest.java0000644000175000017500000001167611017455042030024 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.replicated; import org.jboss.cache.Cache; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.Node; import org.jboss.cache.NodeSPI; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.config.Option; import org.jboss.cache.factories.UnitTestCacheConfigurationFactory; import org.jboss.cache.invocation.CacheInvocationDelegate; import org.jboss.cache.util.TestingUtil; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * @author Manik Surtani (manik@jboss.org) */ @Test(groups = {"functional", "jgroups"}) public class SyncReplTest { private Cache[] caches; @BeforeMethod(alwaysRun = true) public void setUp() { System.out.println("*** In setUp()"); caches = new Cache[2]; caches[0] = new DefaultCacheFactory().createCache(UnitTestCacheConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC)); caches[1] = new DefaultCacheFactory().createCache(UnitTestCacheConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC)); TestingUtil.blockUntilViewsReceived(caches, 5000); System.out.println("*** Finished setUp()"); } @AfterMethod(alwaysRun = true) public void tearDown() { if (caches != null) TestingUtil.killCaches(caches); } @SuppressWarnings("unchecked") public void testBasicOperation() { assertClusterSize("Should only be 2 caches in the cluster!!!", 2); assertInvocationContextInitState(); Fqn f = Fqn.fromString("/test/data"); String k = "key", v = "value"; assertNull("Should be null", caches[0].getRoot().getChild(f)); assertNull("Should be null", caches[1].getRoot().getChild(f)); Node node = caches[0].getRoot().addChild(f); assert ((NodeSPI) node).getCache() instanceof CacheInvocationDelegate; assertNotNull("Should not be null", node); node.put(k, v); assertEquals(v, node.get(k)); assertEquals(v, caches[0].get(f, k)); assertEquals("Should have replicated", v, caches[1].get(f, k)); } @SuppressWarnings("unchecked") public void testSyncRepl() { assertClusterSize("Should only be 2 caches in the cluster!!!", 2); assertInvocationContextInitState(); Fqn fqn = Fqn.fromString("/JSESSIONID/1010.10.5:3000/1234567890/1"); caches[0].getConfiguration().setSyncCommitPhase(true); caches[1].getConfiguration().setSyncCommitPhase(true); caches[0].put(fqn, "age", 38); assertEquals("Value should be set", 38, caches[0].get(fqn, "age")); assertEquals("Value should have replicated", 38, caches[1].get(fqn, "age")); } /** * Checks for expected propagation of removeNode calls. */ @SuppressWarnings("unchecked") public void testNodeConvenienceNodeRemoval() { // this fqn is relative, but since it is from the root it may as well be absolute Fqn fqn = Fqn.fromString("/test/fqn"); caches[0].getRoot().addChild(fqn); assertTrue(caches[0].getRoot().hasChild(fqn)); assertTrue(caches[1].getRoot().hasChild(fqn)); assertEquals(true, caches[0].removeNode(fqn)); assertFalse(caches[0].getRoot().hasChild(fqn)); assertFalse(caches[1].getRoot().hasChild(fqn)); assertEquals(false, caches[0].removeNode(fqn)); // Confirm it works as expected if the removed node has a child Fqn child = Fqn.fromString("/test/fqn/child"); caches[0].getRoot().addChild(child); assertTrue(caches[0].getRoot().hasChild(child)); assertTrue(caches[1].getRoot().hasChild(child)); assertEquals(true, caches[0].removeNode(fqn)); assertFalse(caches[0].getRoot().hasChild(fqn)); assertFalse(caches[1].getRoot().hasChild(fqn)); assertEquals(false, caches[0].removeNode(fqn)); } private void assertClusterSize(String message, int size) { for (Cache c : caches) { assertClusterSize(message, size, c); } } private void assertClusterSize(String message, int size, Cache c) { assertEquals(message, size, c.getMembers().size()); } private void assertInvocationContextInitState() { for (Cache c : caches) { assertInvocationContextInitState(c); } } private void assertInvocationContextInitState(Cache c) { InvocationContext ctx = c.getInvocationContext(); InvocationContext control = null; try { control = ctx.clone(); } catch (CloneNotSupportedException e) { throw new RuntimeException(e); } control.reset(); control.setOptionOverrides(new Option()); assertEquals("Should be equal", control, ctx); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/replicated/SyncReplTxTest.java0000644000175000017500000012575111017455042030340 0ustar twernertwerner/* * * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.replicated; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.Cache; import org.jboss.cache.CacheException; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.config.Configuration; import org.jboss.cache.lock.IsolationLevel; import org.jboss.cache.lock.TimeoutException; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.notifications.annotation.CacheListener; import org.jboss.cache.notifications.annotation.NodeModified; import org.jboss.cache.notifications.event.NodeEvent; import org.jboss.cache.transaction.TransactionSetup; import org.jboss.cache.util.CachePrinter; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.NotSupportedException; import javax.transaction.RollbackException; import javax.transaction.Status; import javax.transaction.Synchronization; import javax.transaction.SystemException; import javax.transaction.Transaction; import javax.transaction.TransactionManager; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Semaphore; /** * Replicated unit test for sync transactional CacheImpl * Note: we use DummyTransactionManager for Tx purpose instead of relying on * jta. * * @version $Revision: 5906 $ */ @Test(groups = {"functional", "jgroups", "transaction"}) public class SyncReplTxTest { private static Log log = LogFactory.getLog(SyncReplTxTest.class); private CacheSPI cache1; private CacheSPI cache2; Semaphore lock; private Throwable t1_ex; private Throwable t2_ex; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { t1_ex = t2_ex = null; lock = new Semaphore(1); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { TransactionSetup.cleanup(); destroyCaches(); } private TransactionManager beginTransaction() throws SystemException, NotSupportedException { return beginTransaction(cache1); } private TransactionManager beginTransaction(CacheSPI c) throws SystemException, NotSupportedException { TransactionManager mgr = c.getConfiguration().getRuntimeConfig().getTransactionManager(); mgr.begin(); return mgr; } private void initCaches(Configuration.CacheMode caching_mode) throws Exception { cache1 = (CacheSPI) new DefaultCacheFactory().createCache(false); cache2 = (CacheSPI) new DefaultCacheFactory().createCache(false); cache1.getConfiguration().setCacheMode(caching_mode); cache2.getConfiguration().setCacheMode(caching_mode); cache1.getConfiguration().setIsolationLevel(IsolationLevel.SERIALIZABLE); cache2.getConfiguration().setIsolationLevel(IsolationLevel.SERIALIZABLE); cache1.getConfiguration().setTransactionManagerLookupClass(TransactionSetup.getManagerLookup()); cache2.getConfiguration().setTransactionManagerLookupClass(TransactionSetup.getManagerLookup()); cache1.getConfiguration().setLockAcquisitionTimeout(5000); cache2.getConfiguration().setLockAcquisitionTimeout(5000); configureMultiplexer(cache1); configureMultiplexer(cache2); cache1.start(); cache2.start(); validateMultiplexer(cache1); validateMultiplexer(cache2); } /** * Provides a hook for multiplexer integration. This default implementation * is a no-op; subclasses that test mux integration would override * to integrate the given cache with a multiplexer. *

* param cache a cache that has been configured but not yet created. * * @param cache cache * @throws Exception exception */ protected void configureMultiplexer(Cache cache) throws Exception { // default does nothing } /** * Provides a hook to check that the cache's channel came from the * multiplexer, or not, as expected. This default impl asserts that * the channel did not come from the multiplexer. * * @param cache a cache that has already been started */ protected void validateMultiplexer(Cache cache) { assertFalse("Cache is not using multiplexer", cache.getConfiguration().isUsingMultiplexer()); } private void destroyCaches() { if (cache1 != null) { cache1.stop(); } if (cache2 != null) { cache2.stop(); } cache1 = null; cache2 = null; } public void testLockRemoval() throws Exception { initCaches(Configuration.CacheMode.REPL_SYNC); cache1.getConfiguration().setSyncCommitPhase(true); TestingUtil.extractLockManager(cache1).unlockAll(cache1.getRoot()); TransactionManager tm = beginTransaction(); cache1.put("/bela/ban", "name", "Bela Ban"); assertEquals(3, cache1.getNumberOfLocksHeld()); assertEquals(0, cache2.getNumberOfLocksHeld()); tm.commit(); assertEquals(0, cache1.getNumberOfLocksHeld()); assertEquals(0, cache2.getNumberOfLocksHeld()); } public void testSyncRepl() throws Exception { Integer age; Transaction tx; initCaches(Configuration.CacheMode.REPL_SYNC); cache1.getConfiguration().setSyncCommitPhase(true); cache2.getConfiguration().setSyncCommitPhase(true); TransactionManager mgr = beginTransaction(); cache1.put("/a/b/c", "age", 38); tx = mgr.suspend(); assertNull("age on cache2 must be null as the TX has not yet been committed", cache2.get("/a/b/c", "age")); log.debug("cache1: locks held before commit: " + CachePrinter.printCacheLockingInfo(cache1)); log.debug("cache2: locks held before commit: " + CachePrinter.printCacheLockingInfo(cache2)); mgr.resume(tx); mgr.commit(); log.debug("cache1: locks held after commit: " + CachePrinter.printCacheLockingInfo(cache1)); log.debug("cache2: locks held after commit: " + CachePrinter.printCacheLockingInfo(cache2)); // value on cache2 must be 38 age = (Integer) cache2.get("/a/b/c", "age"); assertNotNull("\"age\" obtained from cache2 must be non-null ", age); assertTrue("\"age\" must be 38", age == 38); } public void testSimplePut() throws Exception { initCaches(Configuration.CacheMode.REPL_SYNC); cache1.put("/JSESSION/localhost/192.168.1.10:32882/Courses/0", "Instructor", "Ben Wang"); cache1.put("/JSESSION/localhost/192.168.1.10:32882/1", "Number", 10); } public void testSimpleTxPut() throws Exception { TransactionManager tm; final Fqn NODE1 = Fqn.fromString("/one/two/three"); initCaches(Configuration.CacheMode.REPL_SYNC); tm = beginTransaction(); cache1.put(NODE1, "age", 38); System.out.println("TransactionTable for cache1 after cache1.put():\n" + cache1.getTransactionTable().toString(true)); tm.commit(); /* tx=beginTransaction(); cache1.put(NODE1, "age", new Integer(38)); cache1.put(NODE2, "name", "Ben of The Far East"); cache1.put(NODE3, "key", "UnknowKey"); System.out.println("TransactionTable for cache1 after cache1.put():\n" + cache1.getTransactionTable().toString(true)); tx.commit(); */ /* tx=beginTransaction(); cache1.put(NODE1, "age", new Integer(38)); cache1.put(NODE1, "AOPInstance", new AOPInstance()); cache1.put(NODE2, "AOPInstance", new AOPInstance()); cache1.put(NODE1, "AOPInstance", new AOPInstance()); tx.commit(); System.out.println("TransactionTable for cache1 after cache1.put():\n" + cache1.getTransactionTable().toString(true)); */ } public void testSyncReplWithModficationsOnBothCaches() throws Exception { TransactionManager tm; final Fqn NODE1 = Fqn.fromString("/one/two/three"); final Fqn NODE2 = Fqn.fromString("/eins/zwei/drei"); initCaches(Configuration.CacheMode.REPL_SYNC); // create roots first cache1.put("/one/two", null); cache2.put("/eins/zwei", null); cache1.getConfiguration().setSyncCommitPhase(true); cache2.getConfiguration().setSyncCommitPhase(true); tm = beginTransaction(); cache1.put(NODE1, "age", 38); System.out.println("TransactionTable for cache1 after cache1.put():\n" + cache1.getTransactionTable().toString(true)); cache2.put(NODE2, "age", 39); System.out.println("TransactionTable for cache2 after cache2.put():\n" + cache2.getTransactionTable().toString(true)); System.out.println("cache1 before commit:\n" + CachePrinter.printCacheLockingInfo(cache1)); System.out.println("cache2 before commit:\n" + CachePrinter.printCacheLockingInfo(cache2)); try { tm.commit(); fail("Should not succeed with SERIALIZABLE semantics"); } catch (Exception e) { //should be a classic deadlock here. } System.out.println("cache1 after commit:\n" + CachePrinter.printCacheLockingInfo(cache1)); System.out.println("cache2 after commit:\n" + CachePrinter.printCacheLockingInfo(cache2)); /* assertTrue(cache1.exists(NODE1)); assertTrue(cache1.exists(NODE2)); assertTrue(cache1.exists(NODE1)); assertTrue(cache2.exists(NODE2)); age = (Integer) cache1.get(NODE1, "age"); assertNotNull("\"age\" obtained from cache1 for " + NODE1 + " must be non-null ", age); assertTrue("\"age\" must be 38", age == 38); age = (Integer) cache2.get(NODE1, "age"); assertNotNull("\"age\" obtained from cache2 for " + NODE1 + " must be non-null ", age); assertTrue("\"age\" must be 38", age == 38); age = (Integer) cache1.get(NODE2, "age"); assertNotNull("\"age\" obtained from cache1 for " + NODE2 + " must be non-null ", age); assertTrue("\"age\" must be 39", age == 39); age = (Integer) cache2.get(NODE2, "age"); assertNotNull("\"age\" obtained from cache2 for " + NODE2 + " must be non-null ", age); assertTrue("\"age\" must be 39", age == 39); */ assertEquals(0, cache1.getNumberOfLocksHeld()); assertEquals(0, cache2.getNumberOfLocksHeld()); System.out.println("TransactionTable for cache1:\n" + cache1.getTransactionTable().toString(true)); System.out.println("TransactionTable for cache2:\n" + cache2.getTransactionTable().toString(true)); } public void testSyncReplWithModficationsOnBothCachesSameData() throws Exception { TransactionManager tm; final Fqn NODE = Fqn.fromString("/one/two/three"); initCaches(Configuration.CacheMode.REPL_SYNC); tm = beginTransaction(); cache1.put(NODE, "age", 38); System.out.println("TransactionTable for cache1 after cache1.put():\n" + cache1.getTransactionTable().toString(true)); cache2.put(NODE, "age", 39); System.out.println("TransactionTable for cache2 after cache2.put():\n" + cache2.getTransactionTable().toString(true)); System.out.println("cache1 before commit:\n" + CachePrinter.printCacheLockingInfo(cache1)); System.out.println("cache2 before commit:\n" + CachePrinter.printCacheLockingInfo(cache2)); try { tm.commit(); fail("commit should throw a RollbackException, we should not get here"); } catch (RollbackException rollback) { System.out.println("Transaction was rolled back, this is correct"); } System.out.println("cache1 after commit:\n" + CachePrinter.printCacheLockingInfo(cache1)); System.out.println("cache2 after commit:\n" + CachePrinter.printCacheLockingInfo(cache2)); assertEquals(0, cache1.getNumberOfLocksHeld()); assertEquals(0, cache2.getNumberOfLocksHeld()); assertEquals(0, cache1.getNumberOfNodes()); assertEquals(0, cache2.getNumberOfNodes()); } public void testSyncReplWithModficationsOnBothCachesWithRollback() throws Exception { TransactionManager tm; final Fqn fqn1 = Fqn.fromString("/one/two/three"); final Fqn fqn2 = Fqn.fromString("/eins/zwei/drei"); initCaches(Configuration.CacheMode.REPL_SYNC); cache1.getConfiguration().setSyncRollbackPhase(true); cache2.getConfiguration().setSyncRollbackPhase(true); tm = beginTransaction(); cache1.put(fqn1, "age", 38); cache2.put(fqn2, "age", 39); System.out.println("cache1 (before commit):\n" + CachePrinter.printCacheLockingInfo(cache1)); System.out.println("cache2 (before commit):\n" + CachePrinter.printCacheLockingInfo(cache2)); // this will rollback the transaction Transaction tx = tm.getTransaction(); tx.registerSynchronization(new TransactionAborter(tx)); try { tm.commit(); fail("commit should throw a RollbackException, we should not get here"); } catch (RollbackException rollback) { System.out.println("Transaction was rolled back, this is correct"); } System.out.println("cache1 (after rollback):\n" + CachePrinter.printCacheLockingInfo(cache1)); System.out.println("cache2 (after rollback):\n" + CachePrinter.printCacheLockingInfo(cache2)); assertEquals(0, cache1.getNumberOfLocksHeld()); assertEquals(0, cache2.getNumberOfLocksHeld()); assertEquals(0, cache1.getNumberOfNodes()); assertEquals(0, cache2.getNumberOfNodes()); } /** * Test for JBCACHE-359 -- does a callback into cache from a listener * interfere with transaction rollback. * * @throws Exception */ // Is this test still valid after JBCACHE-1022 ? // public void testSyncReplWithRollbackAndListener() throws Exception // { // Transaction tx; // final Fqn NODE1 = Fqn.fromString("/one/two/three"); // // initCaches(Configuration.CacheMode.REPL_SYNC); // // cache1.getConfiguration().setSyncRollbackPhase(true); // cache2.getConfiguration().setSyncRollbackPhase(true); // // // Test with a rollback on the sending side // // CallbackListener cbl1 = new CallbackListener(cache1, "age"); // CallbackListener cbl2 = new CallbackListener(cache2, "age"); // // tx = beginTransaction(); // cache1.put(NODE1, "age", 38); // // System.out.println("cache1 (before commit):\n" + CachePrinter.printCacheLockingInfo(cache1)); // System.out.println("cache2 (before commit):\n" + CachePrinter.printCacheLockingInfo(cache2)); // // // this will rollback the transaction // tx.registerSynchronization(new TransactionAborter(tx)); // // try // { // tx.commit(); // fail("commit should throw a RollbackException, we should not get here"); // } // catch (RollbackException rollback) // { // rollback.printStackTrace(); // System.out.println("Transaction was rolled back, this is correct"); // } // // // Sleep, as the rollback call to cache2 is async // TestingUtil.sleepThread(1000); // // System.out.println("cache1 (after rollback):\n" + CachePrinter.printCacheLockingInfo(cache1)); // System.out.println("cache2 (after rollback):\n" + CachePrinter.printCacheLockingInfo(cache2)); // // assertNull(cbl1.getCallbackException()); // assertNull(cbl2.getCallbackException()); // // assertEquals(0, cache1.getNumberOfLocksHeld()); // assertEquals(0, cache2.getNumberOfLocksHeld()); // // assertEquals(0, cache1.getNumberOfNodes()); // assertEquals(0, cache2.getNumberOfNodes()); // // // Test with a rollback on the receiving side // // cache2.getNotifier().removeCacheListener(cbl2); // // listener aborts any active tx // cbl2 = new TransactionAborterCallbackListener(cache2, "age"); // // tx = beginTransaction(); // cache1.put(NODE1, "age", 38); // // System.out.println("cache1 (before commit):\n" + CachePrinter.printCacheLockingInfo(cache1)); // System.out.println("cache2 (before commit):\n" + CachePrinter.printCacheLockingInfo(cache2)); // // tx.commit(); // // // Sleep, as the commit call to cache2 is async // TestingUtil.sleepThread(1000); // // System.out.println("cache1 (after rollback):\n" + CachePrinter.printCacheLockingInfo(cache1)); // System.out.println("cache2 (after rollback):\n" + CachePrinter.printCacheLockingInfo(cache2)); // // assertNull(cbl1.getCallbackException()); // assertNull(cbl2.getCallbackException()); // // assertEquals(0, cache1.getNumberOfLocksHeld()); // assertEquals(0, cache2.getNumberOfLocksHeld()); // // // cache1 didn't fail, so should have 3 nodes // assertEquals(3, cache1.getNumberOfNodes()); // assertEquals(0, cache2.getNumberOfNodes()); // // } /** * Test for JBCACHE-361 -- does marking a tx on the remote side * rollback-only cause a rollback on the originating side? */ public void testSyncReplWithRemoteRollback() throws Exception { TransactionManager tm; final Fqn NODE1 = Fqn.fromString("/one/two/three"); initCaches(Configuration.CacheMode.REPL_SYNC); cache1.getConfiguration().setSyncRollbackPhase(true); cache2.getConfiguration().setSyncRollbackPhase(true); // Test with a rollback on the remote side // listener aborts any active tx //TransactionAborterListener tal = new TransactionAborterListener(cache2); tm = beginTransaction(); cache1.put(NODE1, "age", 38); System.out.println("cache1 (before commit):\n" + CachePrinter.printCacheLockingInfo(cache1)); System.out.println("cache2 (before commit):\n" + CachePrinter.printCacheLockingInfo(cache2)); // instead of a listener lets just get a WL on ROOT on cache2. And hold on to it. Transaction tx = tm.suspend(); tm.begin(); cache2.getRoot().put("x", "y"); Transaction tx2 = cache2.getTransactionManager().suspend(); System.out.println("cache2 (before commit):\n" + CachePrinter.printCacheLockingInfo(cache2)); tm.resume(tx); try { tm.commit(); fail("commit should throw a RollbackException, we should not get here"); } catch (RollbackException rollback) { System.out.println("Transaction was rolled back, this is correct"); } finally { tm.resume(tx2); tm.rollback(); } // Sleep, as the commit call to cache2 is async TestingUtil.sleepThread(1000); System.out.println("cache1 (after rollback):\n" + CachePrinter.printCacheLockingInfo(cache1)); System.out.println("cache2 (after rollback):\n" + CachePrinter.printCacheLockingInfo(cache2)); //assertNull(tal.getCallbackException()); assertEquals(0, cache1.getNumberOfLocksHeld()); assertEquals(0, cache2.getNumberOfLocksHeld()); assertEquals(0, cache1.getNumberOfNodes()); assertEquals(0, cache2.getNumberOfNodes()); } public void testASyncRepl() throws Exception { Integer age; TransactionManager tm; initCaches(Configuration.CacheMode.REPL_ASYNC); tm = beginTransaction(); cache1.put("/a/b/c", "age", 38); Thread.sleep(1000); assertNull("age on cache2 must be null as the TX has not yet been committed", cache2.get("/a/b/c", "age")); tm.commit(); Thread.sleep(1000); // value on cache2 must be 38 age = (Integer) cache2.get("/a/b/c", "age"); assertNotNull("\"age\" obtained from cache2 is null ", age); assertTrue("\"age\" must be 38", age == 38); } /** * Tests concurrent modifications: thread1 succeeds and thread2 is blocked until thread1 is done, and then succeeds * too. However, this is flawed with the introduction of interceptors, here's why.
*

    *
  • Thread1 acquires the lock for /bela/ban on cache1 *
  • Thread2 blocks on Thread1 to release the lock *
  • Thread1 commits: this means the TransactionInterceptor and the ReplicationInterceptor are called in * the sequence in which they registered. Unfortunately, the TransactionInterceptor registered first. In the * PREPARE phase, the ReplicationInterceptor calls prepare() in cache2 synchronously. The TxInterceptor * does nothing. The the COMMIT phase, the TxInterceptor commits the data by releasing the locks locally and * then the ReplicationInterceptor sends an asynchronous COMMIT to cache2. *
  • Because the TxInterceptor for Thread1 releases the locks locally before sending the async COMMIT, * Thread2 is able to acquire the lock for /bela/ban in cache1 and then starts the PREPARE phase by sending a * synchronous PREPARE to cache2. If this PREPARE arrives at cache2 before the COMMIT from Thread1, * the PREPARE will block because it attempts to acquire a lock on /bela/ban on cache2 still held by Thread1 * (which would be released by Thread1's COMMIT). This results in deadlock, which is resolved by Thread2 running * into a timeout with subsequent rollback and Thread1 succeeding.
    *
* There are 3 solutions to this: *
    *
  1. Do nothing. This is standard behavior for concurrent access to the same data. Same thing if the 2 threads * operated on the same data in separate caches, e.g. Thread1 on /bela/ban in cache1 and Thread2 on * /bela/ban in cache2. The semantics of Tx commit as handled by the interceptors is: after tx1.commit() returns * the locks held by tx1 are release and a COMMIT message is on the way (if sent asynchronously). *
  2. Force an order over TxInterceptor and ReplicationInterceptor. This would require ReplicationInterceptor * to always be fired first on TX commit. Downside: the interceptors have an implicit dependency, which is not * nice. *
  3. Priority-order requests at the receiver; e.g. a COMMIT could release a blocked PREPARE. This is bad because * it violates JGroups' FIFO ordering guarantees. *
* I'm currently investigating solution #2, ie. creating an OrderedSynchronizationHandler, which allows other * SynchronizationHandlers to register (atHead, atTail), and the OrderedSynchronizationHandler would call the * SynchronizationHandler in the order in which they are defined. * * @throws Exception exception */ public void testConcurrentPuts() throws Exception { initCaches(Configuration.CacheMode.REPL_SYNC); cache1.getConfiguration().setSyncCommitPhase(true); Thread t1 = new Thread("Thread1") { TransactionManager tm; public void run() { try { tm = beginTransaction(); cache1.put("/bela/ban", "name", "Bela Ban"); TestingUtil.sleepThread(2000);// Thread2 will be blocked until we commit tm.commit(); System.out.println("[Thread1] ** LOCK INFO cache1: " + CachePrinter.printCacheLockingInfo(cache1)); System.out.println("[Thread1] ** LOCK INFO cache2: " + CachePrinter.printCacheLockingInfo(cache2)); } catch (Throwable ex) { ex.printStackTrace(); t1_ex = ex; } } }; Thread t2 = new Thread("Thread2") { TransactionManager tm; public void run() { try { TestingUtil.sleepThread(1000);// give Thread1 time to acquire the lock tm = beginTransaction(); System.out.println("[Thread2] ** LOCK INFO cache1: " + CachePrinter.printCacheLockingInfo(cache1)); System.out.println("[Thread2] ** LOCK INFO cache2: " + CachePrinter.printCacheLockingInfo(cache2)); cache1.put("/bela/ban", "name", "Michelle Ban"); System.out.println("[Thread2] ** LOCK INFO cache1: " + CachePrinter.printCacheLockingInfo(cache1)); System.out.println("[Thread2] ** LOCK INFO cache2: " + CachePrinter.printCacheLockingInfo(cache2)); tm.commit(); System.out.println("[Thread2] ** LOCK INFO cache1: " + CachePrinter.printCacheLockingInfo(cache1)); System.out.println("[Thread2] ** LOCK INFO cache2: " + CachePrinter.printCacheLockingInfo(cache2)); } catch (Throwable ex) { ex.printStackTrace(); t2_ex = ex; } } }; // Let the game start t1.start(); t2.start(); // Wait for threads to die t1.join(); t2.join(); if (t1_ex != null) { fail("Thread1 failed: " + t1_ex); } if (t2_ex != null) { fail("Thread2 failed: " + t2_ex); } assertEquals("Michelle Ban", cache1.get("/bela/ban", "name")); } /** * Should reproduce JBCACHE-32 problem (http://jira.jboss.com/jira/browse/JBCACHE-32) */ public void testConcurrentCommitsWith1Thread() throws Exception { _testConcurrentCommits(1); } /** * Should reproduce JBCACHE-32 problem (http://jira.jboss.com/jira/browse/JBCACHE-32) */ public void testConcurrentCommitsWith5Threads() throws Exception { _testConcurrentCommits(5); } /** * Should reproduce JBCACHE-32 problem (http://jira.jboss.com/jira/browse/JBCACHE-32) */ private void _testConcurrentCommits(int num_threads) { Object myMutex = new Object(); final CacheSPI c1 = (CacheSPI) new DefaultCacheFactory().createCache(false); final CacheSPI c2 = (CacheSPI) new DefaultCacheFactory().createCache(false); c1.getConfiguration().setClusterName("TempCluster"); c2.getConfiguration().setClusterName("TempCluster"); c1.getConfiguration().setCacheMode(Configuration.CacheMode.REPL_SYNC); c2.getConfiguration().setCacheMode(Configuration.CacheMode.REPL_SYNC); c1.getConfiguration().setSyncCommitPhase(true); c2.getConfiguration().setSyncCommitPhase(true); c1.getConfiguration().setSyncRollbackPhase(true); c2.getConfiguration().setSyncRollbackPhase(true); c1.getConfiguration().setIsolationLevel(IsolationLevel.REPEATABLE_READ); c2.getConfiguration().setIsolationLevel(IsolationLevel.REPEATABLE_READ); c1.getConfiguration().setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); c2.getConfiguration().setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); c1.getConfiguration().setLockAcquisitionTimeout(5000); c2.getConfiguration().setLockAcquisitionTimeout(5000); c1.start(); c2.start(); final List exceptions = new ArrayList(); class MyThread extends Thread { final Object mutex; public MyThread(String name, Object mutex) { super(name); this.mutex = mutex; } public void run() { TransactionManager tm = null; try { tm = beginTransaction(c1); c1.put("/thread/" + getName(), null); System.out.println("Thread " + getName() + " after put(): " + c1.toString()); System.out.println("Thread " + getName() + " waiting on mutex"); synchronized (mutex) { mutex.wait(); } System.out.println("Thread " + getName() + " committing"); tm.commit(); System.out.println("Thread " + getName() + " committed successfully"); } catch (Exception e) { exceptions.add(e); } finally { try { if (tm != null) tm.rollback(); } catch (Exception e) { // do nothing } } } } MyThread[] threads = new MyThread[num_threads]; for (int i = 0; i < threads.length; i++) { threads[i] = new MyThread("#" + i, myMutex); } for (int i = 0; i < threads.length; i++) { MyThread thread = threads[i]; System.out.println("starting thread #" + i); thread.start(); } TestingUtil.sleepThread(6000); synchronized (myMutex) { System.out.println("cache is " + CachePrinter.printCacheLockingInfo(c1)); System.out.println("******************* SIGNALLING THREADS ********************"); myMutex.notifyAll(); } for (MyThread thread : threads) { try { thread.join(); System.out.println("Joined thread " + thread.getName()); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("FINAL c1:\n" + CachePrinter.printCacheDetails(c1) + "\nlocks:\n" + CachePrinter.printCacheLockingInfo(c1)); assertEquals(0, c1.getNumberOfLocksHeld()); assertEquals(0, c2.getNumberOfLocksHeld()); c1.stop(); c2.stop(); // if(ex != null) // { // ex.printStackTrace(); // fail("Thread failed: " + ex); // } // we can only expect 1 thread to succeed. The others will fail. So, threads.length -1 exceptions. // this is a timing issue - 2 threads still may succeed on a multi cpu system // assertEquals(threads.length - 1, exceptions.size()); for (Exception exception : exceptions) assertEquals(TimeoutException.class, exception.getClass()); } /** * Conncurrent put on 2 different instances. */ public void testConcurrentPutsOnTwoInstances() throws Exception { initCaches(Configuration.CacheMode.REPL_SYNC); final CacheSPI c1 = this.cache1; final CacheSPI c2 = this.cache2; Thread t1 = new Thread() { TransactionManager tm; public void run() { try { tm = beginTransaction(); c1.put("/ben/wang", "name", "Ben Wang"); TestingUtil.sleepThread(8000); tm.commit();// This should go thru } catch (Throwable ex) { ex.printStackTrace(); t1_ex = ex; } } }; Thread t2 = new Thread() { TransactionManager tm; public void run() { try { TestingUtil.sleepThread(1000);// give Thread1 time to acquire the lock tm = beginTransaction(); c2.put("/ben/wang", "name", "Ben Jr."); tm.commit();// This will time out and rollback first because Thread1 has a tx going as well. } catch (RollbackException rollback_ex) { System.out.println("received rollback exception as expected"); } catch (Throwable ex) { ex.printStackTrace(); t2_ex = ex; } } }; // Let the game start t1.start(); t2.start(); // Wait for thread to die but put an insurance of 5 seconds on it. t1.join(); t2.join(); if (t1_ex != null) { fail("Thread1 failed: " + t1_ex); } if (t2_ex != null) { fail("Thread2 failed: " + t2_ex); } assertEquals("Ben Wang", c1.get("/ben/wang", "name")); } public void testPut() throws Exception { initCaches(Configuration.CacheMode.REPL_SYNC); final CacheSPI c1 = this.cache1; Thread t1 = new Thread() { public void run() { try { lock.acquire(); System.out.println("-- t1 has lock"); c1.put("/a/b/c", "age", 38); System.out.println("[Thread1] set value to 38"); System.out.println("-- t1 releases lock"); lock.release(); TestingUtil.sleepThread(300); Thread.yield(); lock.acquire(); System.out.println("-- t1 has lock"); c1.put("/a/b/c", "age", 39); System.out.println("[Thread1] set value to 39"); System.out.println("-- t1 releases lock"); lock.release(); assertEquals(39, c1.get("/a/b/c", "age")); } catch (Throwable ex) { ex.printStackTrace(); t1_ex = ex; } finally { lock.release(); } } }; Thread t2 = new Thread() { public void run() { try { TestingUtil.sleepThread(100); Thread.yield(); lock.acquire(); System.out.println("-- t2 has lock"); // Should replicate the value right away. Integer val = (Integer) cache2.get("/a/b/c", "age"); System.out.println("[Thread2] value is " + val); assertEquals(new Integer(38), val); System.out.println("-- t2 releases lock"); lock.release(); TestingUtil.sleepThread(300); Thread.yield(); TestingUtil.sleepThread(500); lock.acquire(); System.out.println("-- t2 has lock"); val = (Integer) cache2.get("/a/b/c", "age"); System.out.println("-- t2 releases lock"); lock.release(); assertEquals(new Integer(39), val); } catch (Throwable ex) { ex.printStackTrace(); t2_ex = ex; } finally { lock.release(); } } }; // Let the game start t1.start(); t2.start(); // Wait for thread to die but put an insurance of 5 seconds on it. t1.join(); t2.join(); if (t1_ex != null) { fail("Thread1 failed: " + t1_ex); } if (t2_ex != null) { fail("Thread2 failed: " + t2_ex); } } /** * Test replicated cache with transaction. Idea is to have two threads running * a local cache each that is replicating. Depending on whether cache1 commit/rollback or not, * the cache2.get will get different values. * Note that we have used sleep to interpose thread execution sequence. * Although it's not fool proof, it is rather simple and intuitive. */ public void testPutTx() throws Exception { TransactionManager tm; try { initCaches(Configuration.CacheMode.REPL_SYNC); cache1.getConfiguration().setSyncCommitPhase(true); cache2.getConfiguration().setSyncCommitPhase(true); tm = beginTransaction(); cache1.put("/a/b/c", "age", 38); cache1.put("/a/b/c", "age", 39); Object val = cache2.get("/a/b/c", "age");// must be null as not yet committed assertNull(val); tm.commit(); tm = beginTransaction(); assertEquals(39, cache2.get("/a/b/c", "age"));// must not be null tm.commit(); } catch (Throwable t) { t.printStackTrace(); t1_ex = t; } finally { lock.release(); } } /** * Have both cache1 and cache2 do add and commit. cache1 commit should time out * since it can't obtain the lock when trying to replicate cache2. On the other hand, * cache2 commit will succeed since now that cache1 is rollbacked and lock is * released. */ public void testPutTx1() throws Exception { initCaches(Configuration.CacheMode.REPL_SYNC); final CacheSPI c1 = this.cache1; Thread t1 = new Thread() { public void run() { TransactionManager tm; try { lock.acquire(); tm = beginTransaction(); c1.put("/a/b/c", "age", 38); c1.put("/a/b/c", "age", 39); lock.release(); TestingUtil.sleepThread(300); lock.acquire(); try { tm.commit(); } catch (RollbackException ex) { System.out.println("[Thread1] received RollbackException, as expected. Rolling back changes"); } finally { lock.release(); } } catch (Throwable ex) { ex.printStackTrace(); t1_ex = ex; } finally { lock.release(); } } }; Thread t2 = new Thread() { public void run() { TransactionManager tm; try { sleep(200); Thread.yield(); lock.acquire(); tm = beginTransaction(); assertNull(cache2.get("/a/b/c", "age"));// must be null as not yet committed cache2.put("/a/b/c", "age", 40); lock.release(); TestingUtil.sleepThread(300); lock.acquire(); assertEquals(40, cache2.get("/a/b/c", "age"));// must not be null tm.commit(); lock.release(); TestingUtil.sleepThread(1000); tm = beginTransaction(); assertEquals("After cache2 commit", 40, cache2.get("/a/b/c", "age")); tm.commit(); } catch (Throwable ex) { ex.printStackTrace(); t2_ex = ex; } finally { lock.release(); } } }; // Let the game start t1.start(); t2.start(); t1.join(); t2.join(); if (t1_ex != null) { fail("Thread1 failed: " + t1_ex); } if (t2_ex != null) { fail("Thread2 failed: " + t2_ex); } } public void testPutTxWithRollback() throws Exception { initCaches(Configuration.CacheMode.REPL_SYNC); final CacheSPI c2 = this.cache1; Thread t1 = new Thread() { public void run() { TransactionManager tm; try { lock.acquire(); tm = beginTransaction(); c2.put("/a/b/c", "age", 38); c2.put("/a/b/c", "age", 39); lock.release(); TestingUtil.sleepThread(100); lock.acquire(); tm.rollback(); lock.release(); } catch (Throwable ex) { ex.printStackTrace(); t1_ex = ex; } finally { lock.release(); } } }; Thread t2 = new Thread() { public void run() { TransactionManager tm; try { sleep(200); Thread.yield(); lock.acquire(); tm = beginTransaction(); assertNull(cache2.get("/a/b/c", "age"));// must be null as not yet committed lock.release(); TestingUtil.sleepThread(100); lock.acquire(); assertNull(cache2.get("/a/b/c", "age"));// must be null as rolledback tm.commit(); lock.release(); } catch (Throwable ex) { ex.printStackTrace(); t2_ex = ex; } finally { lock.release(); } } }; // Let the game start t1.start(); t2.start(); // Wait for thread to die but put an insurance of 5 seconds on it. t1.join(); t2.join(); if (t1_ex != null) { fail("Thread1 failed: " + t1_ex); } if (t2_ex != null) { fail("Thread2 failed: " + t2_ex); } } static class TransactionAborter implements Synchronization { Transaction ltx = null; public TransactionAborter(Transaction ltx) { this.ltx = ltx; } public void beforeCompletion() { try { ltx.setRollbackOnly(); } catch (SystemException e) { // who cares } } public void afterCompletion(int status) { } } @CacheListener static class CallbackListener { CacheSPI callbackCache; Object callbackKey; Exception ex; final Object mutex = new Object(); CallbackListener(CacheSPI cache, Object callbackKey) { this.callbackCache = cache; this.callbackKey = callbackKey; cache.getNotifier().addCacheListener(this); } @NodeModified public void nodeModified(NodeEvent e) { if (!e.isPre()) { // Lock on a mutex so test can't check for an exception // until the get call completes synchronized (mutex) { try { callbackCache.get(e.getFqn(), callbackKey); } catch (CacheException exc) { exc.printStackTrace(); ex = exc; } } } } Exception getCallbackException() { synchronized (mutex) { return ex; } } } static class TransactionAborterCallbackListener extends CallbackListener { TransactionManager callbackTM; TransactionAborterCallbackListener(CacheSPI cache, Object callbackKey) { super(cache, callbackKey); callbackTM = callbackCache.getTransactionManager(); } @NodeModified public void nodeModified(NodeEvent ne) { if (!ne.isPre()) { try { Transaction tx = callbackTM.getTransaction(); if (tx != null && tx.getStatus() == Status.STATUS_ACTIVE) { // this will rollback the transaction tx.registerSynchronization(new TransactionAborter(tx)); } else { super.nodeModified(ne); } } catch (Exception e) { e.printStackTrace(); if (ex == null) { ex = e; } } } } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/replicated/AsyncReplTest.java0000755000175000017500000001513411017457124030164 0ustar twernertwerner/* * * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.replicated; import org.jboss.cache.Cache; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.util.internals.ReplicationListener; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.factories.UnitTestCacheConfigurationFactory; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.TransactionManager; /** * Unit test for replicated async CacheSPI. Use locking and multiple threads to test * concurrent access to the tree. * * @version $Revision: 5908 $ */ @Test(groups = {"functional", "jgroups"}) public class AsyncReplTest { private CacheSPI cache1, cache2; private ReplicationListener replListener1, replListener2; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { log("creating cache1"); cache1 = createCache("CacheGroup"); replListener1 = new ReplicationListener(cache1); log("creating cache2"); cache2 = createCache("CacheGroup"); replListener2 = new ReplicationListener(cache2); } private CacheSPI createCache(String name) throws Exception { CacheSPI cache = (CacheSPI) new DefaultCacheFactory().createCache(UnitTestCacheConfigurationFactory.createConfiguration(CacheMode.REPL_ASYNC), false); cache.getConfiguration().setClusterName(name); // Call the hook that allows mux integration configureMultiplexer(cache); cache.create(); cache.start(); validateMultiplexer(cache); return cache; } /** * Provides a hook for multiplexer integration. This default implementation * is a no-op; subclasses that test mux integration would override * to integrate the given cache with a multiplexer. *

* param cache a cache that has been configured but not yet created. */ protected void configureMultiplexer(Cache cache) throws Exception { // default does nothing } /** * Provides a hook to check that the cache's channel came from the * multiplexer, or not, as expected. This default impl asserts that * the channel did not come from the multiplexer. * * @param cache a cache that has already been started */ protected void validateMultiplexer(Cache cache) { assertFalse("Cache is not using multiplexer", cache.getConfiguration().isUsingMultiplexer()); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { TestingUtil.killCaches(cache1, cache2); } public void testTxCompletion() throws Exception { // test a very simple replication. Fqn fqn = Fqn.fromString("/a"); String key = "key"; replListener2.expectAny(); cache1.put(fqn, key, "value1"); // allow for replication replListener2.waitForReplicationToOccur(500); assertEquals("value1", cache1.get(fqn, key)); assertEquals("value1", cache2.get(fqn, key)); TransactionManager mgr = cache1.getTransactionManager(); mgr.begin(); replListener2.expectAny(); cache1.put(fqn, key, "value2"); assertEquals("value2", cache1.get(fqn, key)); assertEquals("value1", cache2.get(fqn, key)); mgr.commit(); replListener2.waitForReplicationToOccur(500); assertEquals("value2", cache1.get(fqn, key)); assertEquals("value2", cache2.get(fqn, key)); mgr.begin(); cache1.put(fqn, key, "value3"); assertEquals("value3", cache1.get(fqn, key)); assertEquals("value2", cache2.get(fqn, key)); mgr.rollback(); assertEquals("value2", cache1.get(fqn, key)); assertEquals("value2", cache2.get(fqn, key)); } public void testPutShouldNotReplicateToDifferentCluster() { CacheSPI cache3 = null, cache4 = null; try { cache3 = createCache("DifferentGroup"); cache4 = createCache("DifferentGroup"); replListener2.expectAny(); cache1.put("/a/b/c", "age", 38); // because we use async repl, modfication may not yet have been propagated to cache2, so // we have to wait a little replListener2.waitForReplicationToOccur(500); assertNull("Should not have replicated", cache3.get("/a/b/c", "age")); } catch (Exception e) { fail(e.toString()); } finally { if (cache3 != null) { cache3.stop(); } if (cache4 != null) { cache4.stop(); } } } public void testStateTransfer() { CacheSPI cache4 = null; try { cache1.put("a/b/c", "age", 38); cache4 = createCache("CacheGroup"); System.out.println("" + cache4.getMembers()); assertEquals(3, cache4.getMembers().size());// cache1, cache2 and cache4 assertEquals("\"age\" should be 38", 38, cache4.get("/a/b/c", "age")); } catch (Exception e) { fail(e.toString()); } finally { if (cache4 != null) { System.out.println("cache4's view: " + cache4.getMembers()); cache4.stop(); } } } public void testAsyncReplDelay() { Integer age; try { cache1.put("/a/b/c", "age", 38); // value on cache2 may be 38 or not yet replicated age = (Integer) cache2.get("/a/b/c", "age"); log("attr \"age\" of \"/a/b/c\" on cache2=" + age); assertTrue("should be either null or 38", age == null || age == 38); } catch (Exception e) { fail(e.toString()); } } public void testAsyncReplTxDelay() { Integer age; try { TransactionManager tm = cache1.getTransactionManager(); tm.begin(); cache1.put("/a/b/c", "age", 38); tm.commit(); // value on cache2 may be 38 or not yet replicated age = (Integer) cache2.get("/a/b/c", "age"); log("attr \"age\" of \"/a/b/c\" on cache2=" + age); assertTrue("should be either null or 38", age == null || age == 38); } catch (Exception e) { fail(e.toString()); } } private void log(String msg) { System.out.println("-- [" + Thread.currentThread() + "]: " + msg); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/replicated/ReplicationExceptionTest.java0000644000175000017500000001233510745367774032433 0ustar twernertwerner/* * * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.replicated; import org.jboss.cache.CacheFactory; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.config.Configuration; import org.jboss.cache.lock.IsolationLevel; import org.jboss.cache.transaction.DummyTransactionManager; import static org.testng.AssertJUnit.assertNotNull; import static org.testng.AssertJUnit.fail; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.naming.Context; import javax.transaction.NotSupportedException; import javax.transaction.RollbackException; import javax.transaction.SystemException; import javax.transaction.TransactionManager; import java.io.NotSerializableException; import java.io.Serializable; /** * Teting of replication exception for a Nonerislizable object * * @author Ben Wang * @version $Revision: 5189 $ */ @Test(groups = {"functional"}) public class ReplicationExceptionTest { private CacheSPI cache1, cache2; String old_factory = null; final String FACTORY = "org.jboss.cache.transaction.DummyContextFactory"; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { old_factory = System.getProperty(Context.INITIAL_CONTEXT_FACTORY); System.setProperty(Context.INITIAL_CONTEXT_FACTORY, FACTORY); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { DummyTransactionManager.destroy(); destroyCaches(); if (old_factory != null) { System.setProperty(Context.INITIAL_CONTEXT_FACTORY, old_factory); old_factory = null; } } private TransactionManager beginTransaction() throws SystemException, NotSupportedException { TransactionManager mgr = cache1.getConfiguration().getRuntimeConfig().getTransactionManager(); mgr.begin(); return mgr; } private void initCaches(Configuration.CacheMode caching_mode) { CacheFactory instance = new DefaultCacheFactory(); cache1 = (CacheSPI) instance.createCache(false); cache2 = (CacheSPI) instance.createCache(false); cache1.getConfiguration().setCacheMode(caching_mode); cache2.getConfiguration().setCacheMode(caching_mode); cache1.getConfiguration().setIsolationLevel(IsolationLevel.SERIALIZABLE); cache2.getConfiguration().setIsolationLevel(IsolationLevel.SERIALIZABLE); cache1.getConfiguration().setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); cache2.getConfiguration().setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); /* cache1.setTransactionManagerLookupClass("org.jboss.cache.transaction.GenericTransactionManagerLookup"); cache2.setTransactionManagerLookupClass("org.jboss.cache.transaction.GenericTransactionManagerLookup"); */ cache1.getConfiguration().setLockAcquisitionTimeout(5000); cache2.getConfiguration().setLockAcquisitionTimeout(5000); cache1.start(); cache2.start(); } void destroyCaches() throws Exception { if (cache1 != null) { cache1.stop(); } if (cache2 != null) { cache2.stop(); } cache1 = null; cache2 = null; } public void testNonSerializableRepl() throws Exception { try { initCaches(Configuration.CacheMode.REPL_SYNC); cache1.put("/a/b/c", "test", new ContainerData()); // We should not come here. assertNotNull("NonSerializableData should not be null on cache2", cache2.get("/a/b/c", "test")); } catch (RuntimeException runtime) { Throwable t = runtime.getCause(); if (t instanceof NotSerializableException) { System.out.println("received NotSerializableException - as expected"); } else { throw runtime; } } } public void testNonSerializableReplWithTx() throws Exception { TransactionManager tm; try { initCaches(Configuration.CacheMode.REPL_SYNC); tm = beginTransaction(); cache1.put("/a/b/c", "test", new ContainerData()); tm.commit(); // We should not come here. assertNotNull("NonSerializableData should not be null on cache2", cache2.get("/a/b/c", "test")); } catch (RollbackException rollback) { System.out.println("received RollbackException - as expected"); } catch (Exception e) { // We should also examine that it is indeed throwing a NonSerilaizable exception. fail(e.toString()); } } static class NonSerializabeData { int i; } static class ContainerData implements Serializable { int i; NonSerializabeData non_serializable_data; private static final long serialVersionUID = -8322197791060897247L; public ContainerData() { i = 99; non_serializable_data = new NonSerializabeData(); } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/replicated/ExceptionTest.java0000644000175000017500000000625111017455042030214 0ustar twernertwernerpackage org.jboss.cache.replicated; import org.jboss.cache.Cache; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.UnitTestCacheConfigurationFactory; import org.jboss.cache.lock.TimeoutException; import org.jboss.cache.transaction.DummyTransactionManagerLookup; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.Transaction; import javax.transaction.TransactionManager; /** * Tests the type of exceptions thrown for Lock Acquisition Timeouts versus Sync Repl Timeouts * * @author Manik Surtani * @since 2.1.0 */ @Test(groups = {"functional"}) public class ExceptionTest { private Cache cache1; private Cache cache2; private Fqn fqn = Fqn.fromString("/a"); @BeforeMethod public void setUp() { cache1 = new DefaultCacheFactory().createCache(false); cache2 = new DefaultCacheFactory().createCache(false); cache1.getConfiguration().setSyncCommitPhase(true); cache1.getConfiguration().setSyncRollbackPhase(true); cache1.getConfiguration().setCacheMode(Configuration.CacheMode.REPL_SYNC); cache1.getConfiguration().setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName()); cache2.getConfiguration().setSyncCommitPhase(true); cache2.getConfiguration().setSyncRollbackPhase(true); cache2.getConfiguration().setCacheMode(Configuration.CacheMode.REPL_SYNC); cache2.getConfiguration().setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName()); } @AfterMethod public void tearDown() { if (cache1 != null) cache1.stop(); if (cache2 != null) cache2.stop(); } @Test(groups = {"functional"}, expectedExceptions = {TimeoutException.class}) public void testSyncReplTimeout() { cache1.getConfiguration().setSyncReplTimeout(1); // 1ms. this is *bound* to fail. cache2.getConfiguration().setSyncReplTimeout(1); String s = UnitTestCacheConfigurationFactory.getClusterConfigFromFile(cache1.getConfiguration().getDefaultClusterConfig()); String newCfg = UnitTestCacheConfigurationFactory.injectDelay(s, 100, 100); cache1.getConfiguration().setClusterConfig(newCfg); cache2.getConfiguration().setClusterConfig(newCfg); cache1.start(); cache2.start(); TestingUtil.blockUntilViewsReceived(60000, cache1, cache2); cache1.put(fqn, "k", "v"); } @Test(groups = {"functional"}, expectedExceptions = {TimeoutException.class}) public void testLockAcquisitionTimeout() throws Exception { cache2.getConfiguration().setLockAcquisitionTimeout(1); cache1.start(); cache2.start(); TestingUtil.blockUntilViewsReceived(10000, cache1, cache2); // get a lock on cache 2 and hold on to it. TransactionManager tm = cache2.getConfiguration().getRuntimeConfig().getTransactionManager(); tm.begin(); cache2.put(fqn, "block", "block"); Transaction t = tm.suspend(); cache1.put(fqn, "k", "v"); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/replicated/SyncCacheListenerTest.java0000644000175000017500000001771111000260265031620 0ustar twernertwerner/* * * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.replicated; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.CacheException; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.config.Configuration; import org.jboss.cache.lock.IsolationLevel; import org.jboss.cache.notifications.annotation.CacheListener; import org.jboss.cache.notifications.annotation.NodeModified; import org.jboss.cache.notifications.annotation.NodeRemoved; import org.jboss.cache.notifications.event.NodeEvent; import org.jboss.cache.transaction.DummyTransactionManager; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.naming.Context; import javax.transaction.Transaction; import javax.transaction.TransactionManager; import java.util.HashMap; import java.util.Map; import java.util.Set; /** * Test out the TreeCacheListener * * @version $Revision: 5546 $ */ @Test(groups = {"functional"}) public class SyncCacheListenerTest { private CacheSPI cache1, cache2; private final static Log log_ = LogFactory.getLog(SyncCacheListenerTest.class); private String old_factory = null; private final static String FACTORY = "org.jboss.cache.transaction.DummyContextFactory"; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { System.out.println("*** starting setUp()"); old_factory = System.getProperty(Context.INITIAL_CONTEXT_FACTORY); System.setProperty(Context.INITIAL_CONTEXT_FACTORY, FACTORY); initCaches(); System.out.println("*** finished setUp()"); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { System.out.println("*** starting tearDown()"); DummyTransactionManager.destroy(); destroyCaches(); if (old_factory != null) { System.setProperty(Context.INITIAL_CONTEXT_FACTORY, old_factory); old_factory = null; } System.out.println("*** finished tearDown()"); } private void initCaches() { cache1 = (CacheSPI) new DefaultCacheFactory().createCache(false); cache2 = (CacheSPI) new DefaultCacheFactory().createCache(false); cache1.getConfiguration().setCacheMode(Configuration.CacheMode.REPL_SYNC); cache2.getConfiguration().setCacheMode(Configuration.CacheMode.REPL_SYNC); cache1.getConfiguration().setIsolationLevel(IsolationLevel.SERIALIZABLE); cache2.getConfiguration().setIsolationLevel(IsolationLevel.SERIALIZABLE); cache1.getConfiguration().setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); cache2.getConfiguration().setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); /* cache1.setTransactionManagerLookupClass("org.jboss.cache.transaction.GenericTransactionManagerLookup"); cache2.setTransactionManagerLookupClass("org.jboss.cache.transaction.GenericTransactionManagerLookup"); */ cache1.getConfiguration().setLockAcquisitionTimeout(5000); cache2.getConfiguration().setLockAcquisitionTimeout(5000); cache1.start(); cache2.start(); } private void destroyCaches() { if (cache1 != null) { cache1.stop(); } if (cache2 != null) { cache2.stop(); } } public void testSyncTxRepl() throws Exception { Integer age; TransactionManager tm = cache1.getTransactionManager(); tm.begin(); Transaction tx = tm.getTransaction(); Listener lis = new Listener(); cache1.getNotifier().addCacheListener(lis); lis.put("/a/b/c", "age", 38); tm.suspend(); assertNull("age on cache2 must be null as the TX has not yet been committed", cache2.get("/a/b/c", "age")); tm.resume(tx); tm.commit(); // value on cache2 must be 38 age = (Integer) cache2.get("/a/b/c", "age"); assertNotNull("\"age\" obtained from cache2 must be non-null ", age); assertTrue("\"age\" must be 38", age == 38); } public void testRemoteCacheListener() throws Exception { Integer age; RemoteListener lis = new RemoteListener(); cache2.getNotifier().addCacheListener(lis); cache1.put("/a/b/c", "age", 38); // value on cache2 must be 38 age = (Integer) cache2.get("/a/b/c", "age"); assertNotNull("\"age\" obtained from cache2 must be non-null ", age); assertTrue("\"age\" must be 38", age == 38); cache1.remove("/a/b/c", "age"); } public void testSyncRepl() throws Exception { Integer age; Listener lis = new Listener(); cache1.addCacheListener(lis); lis.put("/a/b/c", "age", 38); // value on cache2 must be 38 age = (Integer) cache2.get("/a/b/c", "age"); assertNotNull("\"age\" obtained from cache2 must be non-null ", age); assertTrue("\"age\" must be 38", age == 38); } public void testSyncTxReplMap() throws Exception { Integer age; TransactionManager tm = cache1.getConfiguration().getRuntimeConfig().getTransactionManager(); tm.begin(); Transaction tx = tm.getTransaction(); Listener lis = new Listener(); cache1.getNotifier().addCacheListener(lis); Map map = new HashMap(); map.put("age", 38); map.put("name", "Ben"); lis.put("/a/b/c", map); tm.suspend(); assertNull("age on cache2 must be null as the TX has not yet been committed", cache2.get("/a/b/c", "age")); tm.resume(tx); tm.commit(); // value on cache2 must be 38 age = (Integer) cache2.get("/a/b/c", "age"); assertNotNull("\"age\" obtained from cache2 must be non-null ", age); assertTrue("\"age\" must be 38", age == 38); } public void testSyncReplMap() throws Exception { Integer age; Listener lis = new Listener(); cache1.getNotifier().addCacheListener(lis); Map map = new HashMap(); map.put("age", 38); map.put("name", "Ben"); lis.put("/a/b/c", map); // value on cache2 must be 38 age = (Integer) cache2.get("/a/b/c", "age"); assertNotNull("\"age\" obtained from cache2 must be non-null ", age); assertTrue("\"age\" must be 38", age == 38); } @CacheListener public class Listener { Object key_ = null; public void put(String fqn, Object key, Object val) { key_ = key; cache1.put(fqn, key, val); } public void put(String fqn, Map map) { if (map.size() == 0) fail("put(): map size can't be 0"); Set set = map.keySet(); key_ = set.iterator().next();// take anyone cache1.put(fqn, map); } @NodeModified public void nodeModified(NodeEvent ne) { if (!ne.isPre()) { log_.debug("nodeModified visited with fqn: " + ne.getFqn()); try { // test out if we can get the read lock since there is a write lock going as well. cache1.get(ne.getFqn(), key_); } catch (CacheException e) { e.printStackTrace();//To change body of catch statement use File | Settings | File Templates. fail("nodeModified: test failed with exception: " + e); } } } } @CacheListener public class RemoteListener { @NodeRemoved @NodeModified public void callback(NodeEvent e) { System.out.println("Callback got event " + e); log_.debug("Callback got event " + e); assertFalse("node was removed on remote cache so isLocal should be false", e.isOriginLocal()); } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/marshall/0000755000175000017500000000000011376173757024260 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/marshall/MarshalledValueTest.java0000644000175000017500000003161311017455042031016 0ustar twernertwernerpackage org.jboss.cache.marshall; import org.jboss.cache.CacheException; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.InvocationContext; import org.jboss.cache.NodeSPI; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.commands.write.PutDataMapCommand; import org.jboss.cache.commands.write.PutKeyValueCommand; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.ComponentRegistry; import org.jboss.cache.factories.UnitTestCacheConfigurationFactory; import org.jboss.cache.interceptors.MarshalledValueInterceptor; import org.jboss.cache.interceptors.base.CommandInterceptor; import org.jboss.cache.invocation.CacheInvocationDelegate; import org.jboss.cache.loader.DummyInMemoryCacheLoader; import org.jboss.cache.notifications.annotation.CacheListener; import org.jboss.cache.notifications.annotation.NodeModified; import org.jboss.cache.notifications.event.NodeModifiedEvent; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.Externalizable; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectInputStream; import java.io.ObjectOutput; import java.io.ObjectOutputStream; import java.util.Map; /** * Tests implicit marshalled values * * @author Manik Surtani (manik@jboss.org) * @since 2.1.0 */ @Test(groups = "functional") public class MarshalledValueTest { private CacheSPI cache1, cache2; private MarshalledValueListenerInterceptor mvli; @BeforeMethod public void setUp() throws CloneNotSupportedException { cache1 = (CacheSPI) new DefaultCacheFactory().createCache(UnitTestCacheConfigurationFactory.createConfiguration(Configuration.CacheMode.REPL_SYNC, false), false); if (cache1.getConfiguration().getBuddyReplicationConfig() != null) cache1.getConfiguration().setBuddyReplicationConfig(null); cache1.getConfiguration().setUseLazyDeserialization(true); cache2 = (CacheSPI) new DefaultCacheFactory().createCache(cache1.getConfiguration().clone(), false); cache1.start(); cache2.start(); assert TestingUtil.findInterceptor(cache1, MarshalledValueInterceptor.class) != null : "Marshalled value interceptor not in chain!"; assert TestingUtil.findInterceptor(cache2, MarshalledValueInterceptor.class) != null : "Marshalled value interceptor not in chain!"; mvli = new MarshalledValueListenerInterceptor(); cache1.addInterceptor(mvli, MarshalledValueInterceptor.class); } @AfterMethod public void tearDown() { TestingUtil.killCaches(cache1, cache2); Pojo.serializationCount = 0; Pojo.deserializationCount = 0; } private void assertOnlyOneRepresentationExists(MarshalledValue mv) { assert (mv.instance != null && mv.raw == null) || (mv.instance == null && mv.raw != null) : "Only instance or raw representations should exist in a MarshalledValue; never both"; } private void assertSerialized(MarshalledValue mv) { assert mv.raw != null : "Should be serialized"; } private void assertDeserialized(MarshalledValue mv) { assert mv.instance != null : "Should be deserialized"; } private void assertSerializationCounts(int serializationCount, int deserializationCount) { assert Pojo.serializationCount == serializationCount : "Serialization count: expected " + serializationCount + " but was " + Pojo.serializationCount; assert Pojo.deserializationCount == deserializationCount : "Deserialization count: expected " + deserializationCount + " but was " + Pojo.deserializationCount; } public void testNonSerializable() { try { cache1.put("/a", "Hello", new Object()); assert false : "Should have failed"; } catch (CacheException expected) { } assert mvli.invocationCount == 0 : "Call should not have gone beyond the MarshalledValueInterceptor"; try { cache1.put("/a", new Object(), "Hello"); assert false : "Should have failed"; } catch (CacheException expected) { } assert mvli.invocationCount == 0 : "Call should not have gone beyond the MarshalledValueInterceptor"; } public void testNodeReleaseObjectValueReferences() { Pojo value = new Pojo(); cache1.put("/a", "key", value); assertSerializationCounts(1, 0); NodeSPI node = cache1.getNode("/a"); Object o = node.getDirect("key"); assert o instanceof MarshalledValue; MarshalledValue mv = (MarshalledValue) o; assertDeserialized(mv); assert node.get("key").equals(value); assertDeserialized(mv); assertSerializationCounts(1, 0); node.releaseObjectReferences(false); assertSerializationCounts(2, 0); assertOnlyOneRepresentationExists(mv); assertSerialized(mv); // now on cache 2 node = cache2.getNode("/a"); o = node.getDirect("key"); assert o instanceof MarshalledValue; mv = (MarshalledValue) o; assertSerialized(mv); // this proves that unmarshalling on the recipient cache instance is lazy assert node.get("key").equals(value); assertDeserialized(mv); assertSerializationCounts(2, 1); node.releaseObjectReferences(false); assertSerializationCounts(2, 1); assertOnlyOneRepresentationExists(mv); assertSerialized(mv); } public void testNodeReleaseObjectKeyReferences() throws IOException, ClassNotFoundException { Pojo key = new Pojo(); cache1.put("/a", key, "value"); assertSerializationCounts(1, 0); NodeSPI node = cache1.getNode("/a"); Object o = node.getKeysDirect().iterator().next(); assert o instanceof MarshalledValue; MarshalledValue mv = (MarshalledValue) o; assertDeserialized(mv); assert node.get(key).equals("value"); assertDeserialized(mv); assertSerializationCounts(1, 0); node.releaseObjectReferences(false); assertSerializationCounts(2, 0); assertOnlyOneRepresentationExists(mv); assertSerialized(mv); // now on cache 2 node = cache2.getNode("/a"); o = node.getKeysDirect().iterator().next(); assert o instanceof MarshalledValue; mv = (MarshalledValue) o; assertSerialized(mv); assert node.get(key).equals("value"); assertSerializationCounts(2, 1); assertDeserialized(mv); node.releaseObjectReferences(false); assertOnlyOneRepresentationExists(mv); assertSerialized(mv); assertSerializationCounts(2, 1); } public void testEqualsAndHashCode() throws Exception { Pojo pojo = new Pojo(); MarshalledValue mv = new MarshalledValue(pojo); assertDeserialized(mv); int oldHashCode = mv.hashCode(); mv.serialize(); assertSerialized(mv); assert oldHashCode == mv.hashCode(); MarshalledValue mv2 = new MarshalledValue(pojo); assertSerialized(mv); assertDeserialized(mv2); assert mv2.hashCode() == oldHashCode; assert mv.equals(mv2); } public void assertUseOfMagicNumbers() throws Exception { Pojo pojo = new Pojo(); MarshalledValue mv = new MarshalledValue(pojo); Configuration c = new Configuration(); ComponentRegistry cr = new ComponentRegistry(c, new CacheInvocationDelegate()); Marshaller marshaller = new CacheMarshaller210(); cr.registerComponent(marshaller, Marshaller.class); // Wire the marshaller cr.start(); // start the test ByteArrayOutputStream bout = new ByteArrayOutputStream(); ObjectOutputStream out = new ObjectOutputStream(bout); marshaller.objectToObjectStream(mv, out); out.close(); bout.close(); // check that the rest just contains a byte stream which a MarshalledValue will be able to deserialize. ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray()); ObjectInputStream in = new ObjectInputStream(bin); assert in.read() == CacheMarshaller200.MAGICNUMBER_MARSHALLEDVALUE; MarshalledValue recreated = new MarshalledValue(); recreated.readExternal(in); // there should be nothing more assert in.available() == 0; in.close(); bin.close(); assertSerialized(recreated); assert recreated.equals(mv); // since both objects being compared are serialized, the equals() above should just compare byte arrays. assertSerialized(recreated); assertOnlyOneRepresentationExists(recreated); } public void testCacheLoaders() throws CloneNotSupportedException { tearDown(); cache1 = (CacheSPI) new DefaultCacheFactory().createCache(UnitTestCacheConfigurationFactory.createConfiguration(Configuration.CacheMode.REPL_SYNC), false); cache2 = (CacheSPI) new DefaultCacheFactory().createCache(UnitTestCacheConfigurationFactory.createConfiguration(Configuration.CacheMode.REPL_SYNC), false); CacheLoaderConfig clc = new CacheLoaderConfig(); CacheLoaderConfig.IndividualCacheLoaderConfig iclc = new CacheLoaderConfig.IndividualCacheLoaderConfig(); iclc.setClassName(DummyInMemoryCacheLoader.class.getName()); clc.addIndividualCacheLoaderConfig(iclc); cache1.getConfiguration().setCacheLoaderConfig(clc); cache2.getConfiguration().setCacheLoaderConfig(clc.clone()); cache1.getConfiguration().setUseLazyDeserialization(true); cache2.getConfiguration().setUseLazyDeserialization(true); cache1.start(); cache2.start(); TestingUtil.blockUntilViewsReceived(60000, cache1, cache2); Pojo pojo = new Pojo(); cache1.put("/a", "key", pojo); assertSerializationCounts(1, 0); cache2.get("/a", "key"); assertSerializationCounts(1, 1); } public void testCallbackValues() { Listener l = new Listener(); cache1.addCacheListener(l); Pojo pojo = new Pojo(); cache1.put("/a", "key", pojo); assert l.modData.size() == 1; assert l.modData.get("key") instanceof Pojo; assertSerializationCounts(1, 0); } public void testRemoteCallbackValues() { Listener l = new Listener(); cache2.addCacheListener(l); Pojo pojo = new Pojo(); cache1.put("/a", "key", pojo); assert l.modData.size() == 1; pojo = (Pojo) l.modData.get("key"); assert pojo != null; assertSerializationCounts(1, 1); } @CacheListener public static class Listener { Map modData; @NodeModified public void nodeModified(NodeModifiedEvent e) { if (!e.isPre()) modData = e.getData(); } } class MarshalledValueListenerInterceptor extends CommandInterceptor { int invocationCount = 0; public Object visitPutDataMapCommand(InvocationContext ctx, PutDataMapCommand command) throws Throwable { invocationCount++; Object retval = invokeNextInterceptor(ctx, command); if (retval instanceof MarshalledValue) assertOnlyOneRepresentationExists((MarshalledValue) retval); return retval; } public Object visitPutKeyValueCommand(InvocationContext ctx, PutKeyValueCommand command) throws Throwable { invocationCount++; if (command.getKey() instanceof MarshalledValue) assertOnlyOneRepresentationExists((MarshalledValue) command.getKey()); if (command.getValue() instanceof MarshalledValue) assertOnlyOneRepresentationExists((MarshalledValue) command.getValue()); Object retval = invokeNextInterceptor(ctx, command); if (retval instanceof MarshalledValue) assertOnlyOneRepresentationExists((MarshalledValue) retval); return retval; } } public static class Pojo implements Externalizable { int i; boolean b; static int serializationCount, deserializationCount; public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Pojo pojo = (Pojo) o; if (b != pojo.b) return false; if (i != pojo.i) return false; return true; } public int hashCode() { int result; result = i; result = 31 * result + (b ? 1 : 0); return result; } public void writeExternal(ObjectOutput out) throws IOException { out.writeInt(i); out.writeBoolean(b); serializationCount++; } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { i = in.readInt(); b = in.readBoolean(); deserializationCount++; } } } ././@LongLink0000000000000000000000000000015000000000000011561 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/marshall/AbstractVersionAwareMarshallerTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/marshall/AbstractVersionAwareMarshallerTest.j0000644000175000017500000000210211010626047033346 0ustar twernertwernerpackage org.jboss.cache.marshall; import org.jboss.cache.CacheStatus; import org.jboss.cache.RegionManager; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.ComponentRegistry; /** * @author Manik Surtani (manik@jboss.org) * @since 2.1.0 */ public abstract class AbstractVersionAwareMarshallerTest { protected ComponentRegistry cr; protected VersionAwareMarshaller createVAMandRestartCache(String replVersion) { Configuration c = cr.getComponent(Configuration.class); c.setReplVersionString(replVersion); return createVAMandRestartCache(new RegionManager()); } protected VersionAwareMarshaller createVAMandRestartCache(RegionManager rm) { if (cr.getState() == CacheStatus.STARTED) cr.stop(); cr.registerComponent(rm, RegionManager.class); cr.create(); cr.rewire(); // force cache mode VersionAwareMarshaller m = (VersionAwareMarshaller) cr.getComponent(Marshaller.class); m.init(); m.initReplicationVersions(); return m; } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/marshall/SyncReplTest.java0000644000175000017500000003633011020007335027475 0ustar twernertwerner/* * * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.marshall; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.Region; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.factories.UnitTestCacheConfigurationFactory; import org.jboss.cache.marshall.data.Address; import org.jboss.cache.marshall.data.Person; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.NotSupportedException; import javax.transaction.SystemException; import javax.transaction.TransactionManager; import java.lang.reflect.Method; import java.util.HashMap; /** * Test case for marshalling using Sync mode. * * @author Ben Wang * @version $Revision: 5919 $ */ @Test(groups = {"functional", "jgroups"}) public class SyncReplTest extends RegionBasedMarshallingTestBase { CacheSPI cache1, cache2; String props = null; Person ben_; Address addr_; Throwable ex_; private Fqn aop = Fqn.fromString("/aop"); protected boolean useMarshalledValues = false; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { log("creating cache1"); cache1 = createCache("TestCache"); log("creating cache2"); cache2 = createCache("TestCache"); addr_ = new Address(); addr_.setCity("San Jose"); ben_ = new Person(); ben_.setName("Ben"); ben_.setAddress(addr_); // Pause to give caches time to see each other TestingUtil.blockUntilViewsReceived(new CacheSPI[]{cache1, cache2}, 60000); } private CacheSPI createCache(String name) { CacheSPI cache = (CacheSPI) new DefaultCacheFactory().createCache(UnitTestCacheConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC), false); cache.getConfiguration().setClusterName(name); // Use marshaller cache.getConfiguration().setUseLazyDeserialization(useMarshalledValues); cache.getConfiguration().setUseRegionBasedMarshalling(!useMarshalledValues); cache.create(); cache.start(); return cache; } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { cache1.removeNode(Fqn.ROOT); if (cache1 != null) { log("stopping cache1"); cache1.stop(); } if (cache2 != null) { log("stopping cache2"); cache2.stop(); } resetContextClassLoader(); } public void testPlainPut() throws Exception { cache1.put(aop, "person", ben_); Person ben2 = (Person) cache2.get(aop, "person"); assertNotNull("Person from 2nd cache should not be null ", ben2); assertEquals(ben_.toString(), ben2.toString()); } public void testCCE() throws Exception { ClassLoader c1 = getClassLoader(); ClassLoader c2 = getClassLoader(); if (!useMarshalledValues) { Region r1 = cache1.getRegion(aop, false) == null ? cache1.getRegion(aop, true) : cache1.getRegion(aop, false); r1.registerContextClassLoader(c1); Region r2 = cache2.getRegion(aop, false) == null ? cache2.getRegion(aop, true) : cache2.getRegion(aop, false); r2.registerContextClassLoader(c2); } if (useMarshalledValues) Thread.currentThread().setContextClassLoader(c1); cache1.put(aop, "person", ben_); if (useMarshalledValues) resetContextClassLoader(); if (useMarshalledValues) Thread.currentThread().setContextClassLoader(getFailingClassLoader()); try { if (useMarshalledValues) Thread.currentThread().setContextClassLoader(c2); @SuppressWarnings("unused") Person person = (Person) cache2.get(aop, "person"); } catch (ClassCastException ex) { // That's ok. return; } fail("Should have thrown an exception"); } public void testPut() throws Exception { ClassLoader cla = getClassLoader(); ClassLoader clb = getClassLoader(); if (!useMarshalledValues) { Region r1 = cache1.getRegion(aop, false) == null ? cache1.getRegion(aop, true) : cache1.getRegion(aop, false); r1.registerContextClassLoader(cla); Region r2 = cache2.getRegion(aop, false) == null ? cache2.getRegion(aop, true) : cache2.getRegion(aop, false); r2.registerContextClassLoader(clb); } if (useMarshalledValues) Thread.currentThread().setContextClassLoader(cla); cache1.put(aop, "person", ben_); Object ben2; // Can't cast it to Person. CCE will result. if (useMarshalledValues) Thread.currentThread().setContextClassLoader(clb); ben2 = cache2.get(aop, "person"); assertEquals(ben_.toString(), ben2.toString()); } public void testCLSet() throws Exception { ClassLoader cla = getClassLoader(); ClassLoader clb = getClassLoader(); if (!useMarshalledValues) { Region r1 = cache1.getRegion(aop, false) == null ? cache1.getRegion(aop, true) : cache1.getRegion(aop, false); r1.registerContextClassLoader(cla); Region r2 = cache2.getRegion(aop, false) == null ? cache2.getRegion(aop, true) : cache2.getRegion(aop, false); r2.registerContextClassLoader(clb); } if (useMarshalledValues) Thread.currentThread().setContextClassLoader(cla); cache1.put(aop, "person", ben_); Object ben2; // Can't cast it to Person. CCE will result. if (useMarshalledValues) Thread.currentThread().setContextClassLoader(clb); ben2 = cache2.get(aop, "person"); assertEquals(ben_.toString(), ben2.toString()); Class claz = clb.loadClass(ADDRESS_CLASSNAME); Object add = claz.newInstance(); Method setValue = claz.getMethod("setCity", String.class); setValue.invoke(add, "Sunnyvale"); Class clasz1 = clb.loadClass(PERSON_CLASSNAME); setValue = clasz1.getMethod("setAddress", claz); setValue.invoke(ben2, add); } /** * Test replication with classloaders. * * @throws Exception */ public void testCLSet2() throws Exception { ClassLoader cla = getClassLoader(); ClassLoader clb = getClassLoader(); if (!useMarshalledValues) { Region r1 = cache1.getRegion(aop, false) == null ? cache1.getRegion(aop, true) : cache1.getRegion(aop, false); r1.registerContextClassLoader(cla); Region r2 = cache2.getRegion(aop, false) == null ? cache2.getRegion(aop, true) : cache2.getRegion(aop, false); r2.registerContextClassLoader(clb); } if (useMarshalledValues) Thread.currentThread().setContextClassLoader(cla); cache1.put(aop, "person", ben_); Object ben2; // Can't cast it to Person. CCE will result. if (useMarshalledValues) Thread.currentThread().setContextClassLoader(clb); ben2 = cache2.get(aop, "person"); assertEquals(ben_.toString(), ben2.toString()); Class claz = clb.loadClass(ADDRESS_CLASSNAME); Object add = claz.newInstance(); Method setValue = claz.getMethod("setCity", String.class); setValue.invoke(add, "Sunnyvale"); Class clasz1 = clb.loadClass(PERSON_CLASSNAME); setValue = clasz1.getMethod("setAddress", claz); setValue.invoke(ben2, add); // Set it back to the cache // Can't cast it to Person. CCE will resutl. cache2.put(aop, "person", ben2); if (useMarshalledValues) Thread.currentThread().setContextClassLoader(cla); Object ben3 = cache1.get(aop, "person"); assertEquals(ben2.toString(), ben3.toString()); } public void testPuts() throws Exception { ClassLoader cla = getClassLoader(); ClassLoader clb = getClassLoader(); if (!useMarshalledValues) { Region r1 = cache1.getRegion(aop, false) == null ? cache1.getRegion(aop, true) : cache1.getRegion(aop, false); r1.registerContextClassLoader(cla); Region r2 = cache2.getRegion(aop, false) == null ? cache2.getRegion(aop, true) : cache2.getRegion(aop, false); r2.registerContextClassLoader(clb); } // Create an empty Person loaded by this classloader Object scopedBen1 = getPersonFromClassloader(cla); // Create another empty Person loaded by this classloader Object scopedBen2 = getPersonFromClassloader(clb); if (useMarshalledValues) Thread.currentThread().setContextClassLoader(cla); cache1.put(Fqn.fromString("/aop/1"), "person", ben_); cache1.put(Fqn.fromString("/aop/2"), "person", scopedBen1); if (useMarshalledValues) resetContextClassLoader(); Object ben2; // Can't cast it to Person. CCE will resutl. if (useMarshalledValues) Thread.currentThread().setContextClassLoader(clb); ben2 = cache2.get(Fqn.fromString("/aop/1"), "person"); if (useMarshalledValues) resetContextClassLoader(); assertEquals(ben_.toString(), ben2.toString()); if (useMarshalledValues) Thread.currentThread().setContextClassLoader(clb); ben2 = cache2.get(Fqn.fromString("/aop/2"), "person"); if (useMarshalledValues) resetContextClassLoader(); assertFalse("cache2 deserialized with scoped classloader", ben2 instanceof Person); assertFalse("cache2 deserialized with cache2 classloader", scopedBen1.equals(ben2)); assertEquals("scopedBen deserialized properly", scopedBen2, ben2); } public void testMethodCall() throws Exception { ClassLoader cla = getClassLoader(); ClassLoader clb = getClassLoader(); if (!useMarshalledValues) { Region r1 = cache1.getRegion(aop, false) == null ? cache1.getRegion(aop, true) : cache1.getRegion(aop, false); r1.registerContextClassLoader(cla); Region r2 = cache2.getRegion(aop, false) == null ? cache2.getRegion(aop, true) : cache2.getRegion(aop, false); r2.registerContextClassLoader(clb); } if (useMarshalledValues) Thread.currentThread().setContextClassLoader(cla); cache1.put(Fqn.fromString("/aop/1"), "person", ben_); cache1.remove(Fqn.fromString("/aop/1"), "person"); HashMap map = new HashMap(); map.put("1", "1"); map.put("2", "2"); cache1.put(Fqn.fromString("/aop/2"), map); cache1.removeNode(Fqn.fromString("/aop/2")); if (useMarshalledValues) resetContextClassLoader(); // TestingUtil.sleepThread(1000); } public void testTxMethodCall() throws Exception { ClassLoader cla = getClassLoader(); ClassLoader clb = getClassLoader(); if (!useMarshalledValues) { Region r1 = cache1.getRegion(aop, false) == null ? cache1.getRegion(aop, true) : cache1.getRegion(aop, false); r1.registerContextClassLoader(cla); Region r2 = cache2.getRegion(aop, false) == null ? cache2.getRegion(aop, true) : cache2.getRegion(aop, false); r2.registerContextClassLoader(clb); } if (useMarshalledValues) Thread.currentThread().setContextClassLoader(cla); TransactionManager tm = beginTransaction(); cache1.put(Fqn.fromString("/aop/1"), "person", ben_); cache1.remove(Fqn.fromString("/aop/1"), "person"); HashMap map = new HashMap(); map.put("1", "1"); map.put("2", "2"); cache1.put(Fqn.fromString("/aop/2"), map); cache1.removeNode(Fqn.fromString("/aop/2")); tm.commit(); // TestingUtil.sleepThread(1000); if (useMarshalledValues) resetContextClassLoader(); } public void testTxCLSet2() throws Exception { ClassLoader cla = getClassLoader(); if (!useMarshalledValues) { Region r1 = cache1.getRegion(aop, false) == null ? cache1.getRegion(aop, true) : cache1.getRegion(aop, false); r1.registerContextClassLoader(cla); } ClassLoader clb = getClassLoader(); if (!useMarshalledValues) { Region r2 = cache2.getRegion(aop, false) == null ? cache2.getRegion(aop, true) : cache2.getRegion(aop, false); r2.registerContextClassLoader(clb); } TransactionManager tm = beginTransaction(); if (useMarshalledValues) Thread.currentThread().setContextClassLoader(cla); cache1.put(aop, "person", ben_); tm.commit(); if (useMarshalledValues) resetContextClassLoader(); Object ben2; // Can't cast it to Person. CCE will resutl. if (useMarshalledValues) Thread.currentThread().setContextClassLoader(clb); ben2 = cache2.get(aop, "person"); if (useMarshalledValues) resetContextClassLoader(); assertEquals(ben_.toString(), ben2.toString()); Class claz = clb.loadClass(ADDRESS_CLASSNAME); Object add = claz.newInstance(); Method setValue = claz.getMethod("setCity", String.class); setValue.invoke(add, "Sunnyvale"); Class clasz1 = clb.loadClass(PERSON_CLASSNAME); setValue = clasz1.getMethod("setAddress", claz); setValue.invoke(ben2, add); // Set it back to the cache // Can't cast it to Person. CCE will resutl. if (useMarshalledValues) Thread.currentThread().setContextClassLoader(clb); cache2.put(aop, "person", ben2); if (useMarshalledValues) resetContextClassLoader(); if (useMarshalledValues) Thread.currentThread().setContextClassLoader(cla); Object ben3 = cache1.get(aop, "person"); if (useMarshalledValues) resetContextClassLoader(); assertEquals(ben2.toString(), ben3.toString()); } public void testStateTransfer() throws Exception { // Need to test out if app is not registered with beforehand?? } @SuppressWarnings("unchecked") public void testCustomFqn() throws Exception { FooClassLoader cl1 = new FooClassLoader(Thread.currentThread().getContextClassLoader()); Region r1 = cache1.getRegion(aop, false) == null ? cache1.getRegion(aop, true) : cache1.getRegion(aop, false); r1.registerContextClassLoader(cl1); FooClassLoader cl2 = new FooClassLoader(Thread.currentThread().getContextClassLoader()); Region r2 = cache2.getRegion(aop, false) == null ? cache2.getRegion(aop, true) : cache2.getRegion(aop, false); r2.registerContextClassLoader(cl2); Class clazz = cl1.loadFoo(); Object custom1 = clazz.newInstance(); clazz = cl2.loadFoo(); Object custom2 = clazz.newInstance(); cache1.put(Fqn.fromElements("aop", custom1), "key", "value"); try { Object val = cache2.get(Fqn.fromElements("aop", custom2), "key"); assertEquals("value", val); } catch (Exception ex) { fail("Test fails with exception " + ex); } } private TransactionManager beginTransaction() throws SystemException, NotSupportedException { TransactionManager mgr = cache1.getConfiguration().getRuntimeConfig().getTransactionManager(); mgr.begin(); return mgr; } protected Object getPersonFromClassloader(ClassLoader cl) throws Exception { Class clazz = cl.loadClass(PERSON_CLASSNAME); return clazz.newInstance(); } private void log(String msg) { System.out.println("-- [" + Thread.currentThread() + "]: " + msg); } } ././@LongLink0000000000000000000000000000016000000000000011562 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/marshall/ReturnValueMarshallingMarshalledValuesTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/marshall/ReturnValueMarshallingMarshalledValu0000644000175000017500000000063410745240616033455 0ustar twernertwernerpackage org.jboss.cache.marshall; import org.testng.annotations.Test; /** * @author Manik Surtani (manik@jboss.org) * @since 2.1.0 */ @Test(groups = {"functional", "jgroups"}) public class ReturnValueMarshallingMarshalledValuesTest extends ReturnValueMarshallingTest { public ReturnValueMarshallingMarshalledValuesTest() { useMarshalledValues = true; } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/marshall/data/0000755000175000017500000000000011376173757025171 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/marshall/data/Debug.java0000644000175000017500000000621711031017377027047 0ustar twernertwernerpackage org.jboss.cache.marshall.data; import java.lang.reflect.Method; import java.net.URL; import java.security.CodeSource; import java.security.ProtectionDomain; /** * Various debugging utility methods available for use in unit tests * * @author Scott.Stark@jboss.org */ public class Debug { /** * Format a string buffer containing the Class, Interfaces, CodeSource, * and ClassLoader information for the given object clazz. * * @param clazz the Class * @param results the buffer to write the info to */ public static void displayClassInfo(Class clazz, StringBuilder results) { displayClassInfo(clazz, results, true); } @SuppressWarnings("null") public static void displayClassInfo(Class clazz, StringBuilder results, boolean showParentClassLoaders) { ClassLoader cl = clazz.getClassLoader(); results.append("\n" + clazz.getName() + "(" + Integer.toHexString(clazz.hashCode()) + ").ClassLoader=" + cl); ClassLoader parent = cl; while (parent != null) { results.append("\n.." + parent); URL[] urls = getClassLoaderURLs(parent); int length = urls != null ? urls.length : 0; for (int u = 0; u < length; u++) { results.append("\n...." + urls[u]); } if (showParentClassLoaders == false) { break; } parent = parent.getParent(); } CodeSource clazzCS = clazz.getProtectionDomain().getCodeSource(); if (clazzCS != null) { results.append("\n++++CodeSource: " + clazzCS); } else { results.append("\n++++Null CodeSource"); } results.append("\nImplemented Interfaces:"); Class[] ifaces = clazz.getInterfaces(); for (int i = 0; i < ifaces.length; i++) { Class iface = ifaces[i]; results.append("\n++" + iface + "(" + Integer.toHexString(iface.hashCode()) + ")"); ClassLoader loader = ifaces[i].getClassLoader(); results.append("\n++++ClassLoader: " + loader); ProtectionDomain pd = ifaces[i].getProtectionDomain(); CodeSource cs = pd.getCodeSource(); if (cs != null) { results.append("\n++++CodeSource: " + cs); } else { results.append("\n++++Null CodeSource"); } } } /** * Use reflection to access a URL[] getURLs or ULR[] getAllURLs method so * that non-URLClassLoader class loaders, or class loaders that override * getURLs to return null or empty, can provide the true classpath info. */ public static URL[] getClassLoaderURLs(ClassLoader cl) { URL[] urls = {}; try { Class returnType = urls.getClass(); Class[] parameterTypes = {}; Method getURLs = cl.getClass().getMethod("getURLs", parameterTypes); if (returnType.isAssignableFrom(getURLs.getReturnType())) { Object[] args = {}; urls = (URL[]) getURLs.invoke(cl, args); } } catch (Exception ignore) { } return urls; } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/marshall/data/Address.java0000644000175000017500000000334010727545014027405 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.marshall.data; import java.io.Serializable; /** */ public class Address implements Serializable { private static final long serialVersionUID = 5943073369866339615L; String street = null; String city = "San Jose"; int zip = 0; public String getStreet() { return street; } public void setStreet(String street) { this.street = street; } public String getCity() { // System.out.println("********* getting city of " +city); return city; } public void setCity(String city) { // System.out.println("********* setting city of " +city); this.city = city; } public int getZip() { return zip; } public void setZip(int zip) { this.zip = zip; } public String toString() { return "street=" + getStreet() + ", city=" + getCity() + ", zip=" + getZip(); } // public Object writeReplace() { // return this; // } public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; final Address address = (Address) o; if (zip != address.zip) return false; if (city != null ? !city.equals(address.city) : address.city != null) return false; if (street != null ? !street.equals(address.street) : address.street != null) return false; return true; } public int hashCode() { int result; result = (street != null ? street.hashCode() : 0); result = 29 * result + (city != null ? city.hashCode() : 0); result = 29 * result + zip; return result; } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/marshall/data/Person.java0000644000175000017500000000273111031017377027264 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.marshall.data; import java.io.Serializable; public class Person implements Serializable { private static final long serialVersionUID = -885384294556845285L; String name = null; Address address; public String getName() { return name; } public void setName(String name) { this.name = name; } public void setName(Object obj) { this.name = (String) obj; } public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } public String toString() { StringBuilder sb = new StringBuilder(); sb.append("name=").append(getName()).append(" Address= ").append(address); return sb.toString(); } public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; final Person person = (Person) o; if (address != null ? !address.equals(person.address) : person.address != null) return false; if (name != null ? !name.equals(person.name) : person.name != null) return false; return true; } public int hashCode() { int result; result = (name != null ? name.hashCode() : 0); result = 29 * result + (address != null ? address.hashCode() : 0); return result; } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/marshall/Foo.notjava0000644000175000017500000000166510535375655026374 0ustar twernertwernerpackage org.jboss.cache.marshall; import java.io.Serializable; import java.security.SecureRandom; import java.util.Random; /** * This is the java code used to create the Foo.clazz file. File deliberately * doesn't end in .java, as we don't want a Foo.class on the classpath, * only the Foo.clazz file that FooClassLoader can load. * * @author Brian Stansberry * @version $Revision: 3095 $ */ public class Foo implements Serializable { /** The serialVersionUID */ private static final long serialVersionUID = 1L; public boolean equals(Object obj) { return obj instanceof Foo; } public int hashCode() { return 1; } public String toString() { Random random = new SecureRandom(); StringBuffer sb=new StringBuffer("org.jboss.cache.marshall.Foo[random="); sb.append(random.nextInt()).append("]"); return sb.toString(); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/marshall/RedeploymentEmulationTest.java0000644000175000017500000001210510732232543032267 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.marshall; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.Cache; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.Region; import org.jboss.cache.config.Configuration; import org.jgroups.Global; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.io.File; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; /** * Unit test demonstrating usability of marshalling for application redeployment in application server. * * @author Galder Zamarreno */ @Test(groups = {"functional"}, enabled = false) // this relies on an old project structure that no longer exists public class RedeploymentEmulationTest { private Cache cache; private static final String INSTANCE_LIBRARY = "jgroups-all.jar"; private static final String INSTANCE_CLASS_NAME = "org.jgroups.Global"; private static final String USER_DIR = ".";//System.getProperty("user.dir"); private static final String FILE_SEPARATOR = File.separator;//System.getProperty("file.separator"); private static final String LIB_DIR_NAME = "lib"; private static final String LIB_DIR = USER_DIR + FILE_SEPARATOR + LIB_DIR_NAME + FILE_SEPARATOR; private static final String LIB_DIR_SP = System.getProperty("lib.dir");//"lib"; private static final Log log = LogFactory.getLog(RedeploymentEmulationTest.class); @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { cache = new DefaultCacheFactory().createCache(false); cache.getConfiguration().setCacheMode(Configuration.CacheMode.LOCAL); cache.getConfiguration().setUseRegionBasedMarshalling(true); } @AfterMethod(alwaysRun = true) public void tearDown() { log.info("**** IN TEAR DOWN ***"); cache.stop(); } public void testClassCastException() throws Exception { cache.start(); URLClassLoader ucl1 = createOrphanClassLoader(); Thread.currentThread().setContextClassLoader(ucl1); Class clazz1 = ucl1.loadClass(INSTANCE_CLASS_NAME); cache.put(fqn("/a"), "key", clazz1.newInstance()); Thread.currentThread().setContextClassLoader(ClassLoader.getSystemClassLoader()); try { @SuppressWarnings("unused") Global object = (Global) cache.get(fqn("/a"), "key"); fail("Should have produced a ClassCastException"); } catch (ClassCastException cce) { System.out.println("Expecting: " + INSTANCE_CLASS_NAME); System.out.println("Got: " + cce.getMessage()); assertTrue(cce.getMessage().startsWith(INSTANCE_CLASS_NAME)); } } public void testRegisterUnregister() throws Exception { cache.start(); URLClassLoader ucl1 = createOrphanClassLoader(); Thread.currentThread().setContextClassLoader(ucl1); Region region = cache.getRegion(fqn("/"), true); region.registerContextClassLoader(Thread.currentThread().getContextClassLoader()); region.activate(); Class clazz1 = ucl1.loadClass(INSTANCE_CLASS_NAME); cache.put(fqn("/a"), "key", clazz1.newInstance()); region.deactivate(); region.unregisterContextClassLoader(); Thread.currentThread().setContextClassLoader(ClassLoader.getSystemClassLoader()); region.registerContextClassLoader(Thread.currentThread().getContextClassLoader()); try { Global object = (Global) cache.get(fqn("/a"), "key"); assertNull(object); } catch (ClassCastException cce) { // cce.printStackTrace(); fail("Should not have produced a ClassCastException"); } region.deactivate(); region.unregisterContextClassLoader(); } @SuppressWarnings("deprecation") private URLClassLoader createOrphanClassLoader() throws MalformedURLException { File f; if (LIB_DIR_SP == null) { /* lib.dir system property is null, so we assume this test is being run * inside an IDE, where the user dir is the root of JBossCache. We know * JGroups lib is located in lib/jgroups.jar */ f = new File(USER_DIR + FILE_SEPARATOR + LIB_DIR + FILE_SEPARATOR); } else { /* lib.dir is set, so we assume that you are running from the build.xml * which means that the user dir might be a completely different one. lib.dir * system property allows us to know where the lib directory is independently * of the user dir*/ f = new File(LIB_DIR_SP); } URL context = f.toURL(); URL jar = new URL(context, INSTANCE_LIBRARY); URLClassLoader ucl1 = new URLClassLoader(new URL[]{jar}, null); return ucl1; } private static Fqn fqn(String fqn) { return Fqn.fromString(fqn); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/marshall/CacheMarshallerTestBase.java0000644000175000017500000001712411015045001031543 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.marshall; import org.jboss.cache.Fqn; import org.jboss.cache.RegionManager; import org.jboss.cache.commands.ReplicableCommand; import org.jboss.cache.commands.remote.ReplicateCommand; import org.jboss.cache.commands.write.PutKeyValueCommand; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.ComponentRegistry; import org.jboss.cache.invocation.CacheInvocationDelegate; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertTrue; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.ArrayList; import java.util.List; @Test(groups = "functional") public abstract class CacheMarshallerTestBase extends AbstractVersionAwareMarshallerTest { protected String currentVersion; protected int currentVersionShort; protected Class expectedMarshallerClass, latestMarshallerClass = CacheMarshaller210.class; protected VersionAwareMarshaller marshaller; protected RegionManager regionManager; protected Configuration c; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { c = new Configuration(); c.setUseRegionBasedMarshalling(false); c.setInactiveOnStartup(false); c.setReplVersionString(currentVersion); cr = new ComponentRegistry(c, new CacheInvocationDelegate()); marshaller = createVAMandRestartCache(new RegionManager()); regionManager = cr.getComponent(RegionManager.class); } @AfterMethod(alwaysRun = true) public void tearDown() { marshaller = null; } protected void assertObjectArraysAreEqual(Object[] a1, Object[] a2) { assertEquals("Number of args should match", a1.length, a2.length); for (int i = 0; i < a1.length; i++) { if (a1[i] instanceof List && a2[i] instanceof List) { Object[] a1Elements = ((List) a1[i]).toArray(); Object[] a2Elements = ((List) a2[i]).toArray(); assertObjectArraysAreEqual(a1Elements, a2Elements); } else { assertEquals("Argument # " + i + " should be equal", a1[i], a2[i]); } } } public void testGetMarshaller() { assertEquals("Only one marshaller should be in the map by this stage", 1, marshaller.marshallers.size()); assertEquals(expectedMarshallerClass, marshaller.getMarshaller(currentVersionShort).getClass()); // defaultMarshaller is used for outgoing streams assert marshaller.defaultMarshaller.getClass().equals(expectedMarshallerClass); assertEquals(latestMarshallerClass, marshaller.getMarshaller(15).getClass()); assertEquals(latestMarshallerClass, marshaller.getMarshaller(1).getClass()); assertEquals(latestMarshallerClass, marshaller.getMarshaller(-1).getClass()); assertEquals(latestMarshallerClass, marshaller.getMarshaller(0).getClass()); assertEquals(CacheMarshaller200.class, marshaller.getMarshaller(20).getClass()); assertEquals(CacheMarshaller210.class, marshaller.getMarshaller(21).getClass()); assert marshaller.marshallers.size() == 2 : "Should have 2 marshallers now"; } public void testStringBasedFqn() throws Exception { Fqn fqn = Fqn.fromElements("JSESSIONID", "1010.10.5:3000", "1234567890", "1"); byte[] asBytes = marshaller.objectToByteBuffer(fqn); System.out.println("Marshalled to " + asBytes.length + " bytes"); Object o2 = marshaller.objectFromByteBuffer(asBytes); assertEquals(fqn, o2); } public void testNonStringBasedFqn() throws Exception { Fqn fqn = Fqn.fromElements(3, false); byte[] asBytes = marshaller.objectToByteBuffer(fqn); Object o2 = marshaller.objectFromByteBuffer(asBytes); assertEquals(fqn, o2); } public void testMethodCall() throws Exception { Fqn fqn = Fqn.fromElements(3, false); ReplicableCommand cmd = new PutKeyValueCommand(null, fqn, "key", "value"); byte[] asBytes = marshaller.objectToByteBuffer(cmd); Object o2 = marshaller.objectFromByteBuffer(asBytes); assertTrue("Unmarshalled object should be a method call", o2 instanceof ReplicableCommand); ReplicableCommand cmd2 = (ReplicableCommand) o2; assertEquals(cmd, cmd2); } public void testNestedMethodCall() throws Exception { Fqn fqn = Fqn.fromElements(3, false); ReplicableCommand cmd = new PutKeyValueCommand(null, fqn, "key", "value"); ReplicableCommand replicateCmd = new ReplicateCommand(cmd); byte[] asBytes = marshaller.objectToByteBuffer(replicateCmd); Object o2 = marshaller.objectFromByteBuffer(asBytes); assertTrue("Unmarshalled object should be a method call", o2 instanceof ReplicableCommand); ReplicableCommand cmd2 = (ReplicableCommand) o2; assertEquals(replicateCmd, cmd2); } public void testLargeString() throws Exception { doLargeStringTest(32767, false); } public void testLargerString() throws Exception { doLargeStringTest(32768, false); } public void test64KString() throws Exception { doLargeStringTest((2 << 15) - 10, false); doLargeStringTest((2 << 15) + 10, false); } public void test128KString() throws Exception { doLargeStringTest((2 << 16) - 10, false); doLargeStringTest((2 << 16) + 10, false); } public void testLargeStringMultiByte() throws Exception { doLargeStringTest(32767, true); } public void testLargerStringMultiByte() throws Exception { doLargeStringTest(32768, true); } public void test64KStringMultiByte() throws Exception { doLargeStringTest((2 << 15) - 10, true); doLargeStringTest((2 << 15) + 10, true); } public void test128KStringMultiByte() throws Exception { doLargeStringTest((2 << 16) - 10, true); doLargeStringTest((2 << 16) + 10, true); } protected void doLargeStringTest(int stringSize, boolean multiByteChars) throws Exception { StringBuilder sb = new StringBuilder(); int startingChar = multiByteChars ? 210 : 65; for (int i = 0; i < stringSize; i++) sb.append((char) (startingChar + (i % 26))); String largeString = sb.toString(); assertEquals(stringSize, largeString.length()); byte[] buf = marshaller.objectToByteBuffer(largeString); assertEquals(largeString, marshaller.objectFromByteBuffer(buf)); } public void testReplicationQueue() throws Exception { doReplicationQueueTest(); } public void testReplicationQueueWithRegionBasedMarshalling() throws Exception { c.setUseRegionBasedMarshalling(true); marshaller.init(); doReplicationQueueTest(); } protected void doReplicationQueueTest() throws Exception { // replication queue takes a list of replicate() MethodCalls and wraps them in a single replicate call. List calls = new ArrayList(); Fqn f = Fqn.fromElements("BlahBlah", 3, false); String k = "key", v = "value"; ReplicableCommand cmd = new PutKeyValueCommand(null, f, k, v); ReplicableCommand replCmd = new ReplicateCommand(cmd); calls.add(replCmd); cmd = new PutKeyValueCommand(null, f, k, v); replCmd = new ReplicateCommand(cmd); calls.add(replCmd); ReplicableCommand replAllCmd = new ReplicateCommand(calls); byte[] buf = marshaller.objectToByteBuffer(replAllCmd); assertEquals(replAllCmd, marshaller.objectFromByteBuffer(buf)); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/marshall/RegionManagerTest.java0000644000175000017500000001525711017455042030471 0ustar twernertwernerpackage org.jboss.cache.marshall; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.Region; import org.jboss.cache.RegionManager; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.config.Configuration; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; /** * Test on ERegionManager class, from a marshalling perspective. */ @Test(groups = {"functional"}) public class RegionManagerTest { private final Fqn DEFAULT_REGION = Fqn.ROOT; private RegionManager r; private Configuration c; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { CacheSPI cache = (CacheSPI) new DefaultCacheFactory().createCache(); r = cache.getRegionManager(); c = cache.getConfiguration(); } @AfterMethod(alwaysRun = false) public void tearDown() throws Exception { TestingUtil.killCaches(r.getCache()); r = null; } public void testGetAllMarshallingRegions() { Fqn fqn1 = Fqn.fromString("/a/b/c"); Fqn fqn2 = Fqn.fromString("/a/b"); Fqn fqn3 = Fqn.fromString("/aop"); List expected = new ArrayList(4); Region region = r.getRegion(DEFAULT_REGION, true); region.registerContextClassLoader(getClass().getClassLoader()); assertEquals(DEFAULT_REGION, region.getFqn()); expected.add(region); region = r.getRegion(fqn1, true); region.registerContextClassLoader(getClass().getClassLoader()); assertEquals(fqn1, region.getFqn()); expected.add(region); region = r.getRegion(fqn2, true); region.registerContextClassLoader(getClass().getClassLoader()); assertEquals(fqn2, region.getFqn()); expected.add(region); region = r.getRegion(fqn3, true); region.registerContextClassLoader(getClass().getClassLoader()); assertEquals(fqn3, region.getFqn()); expected.add(region); // should sort these now ... Collections.sort(expected); Iterator expectedRegions = expected.iterator(); for (Region reg : r.getAllRegions(Region.Type.MARSHALLING)) { assertSame("Unexpected region " + reg, expectedRegions.next(), reg); } assertFalse("Should not be expecting any more regions", expectedRegions.hasNext()); } public void testNoDefaultRegion() { Fqn fqn1 = Fqn.fromString("/a/b/c"); Fqn fqn2 = Fqn.fromString("/a/b/"); r.getRegion(fqn1, true); r.getRegion(fqn2, true); Region region = null; try { region = r.getRegion("/a", false); } catch (Exception e) { fail("If we don't set the default region, it still should be ok!"); } assertNull("Default region is not null!", region); } public void testGetParentRegion() { String fqn1 = "/a/b/c"; String fqn2 = "/a/b"; String fqn3 = "/a"; r.getRegion(fqn1, true); r.getRegion(fqn3, true); Region region = r.getRegion(fqn2, false); assertEquals("Should be the same region as in " + fqn3, r.getRegion(fqn3, false), region); } public void testRemoveRegion() { String fqn1 = "/a"; String fqn2 = "/a/b"; String fqn3 = "/a/b/c"; Region r1 = r.getRegion(fqn1, true); Region r2 = r.getRegion(fqn2, true); Region r3 = r.getRegion(fqn3, true); assertEquals("Expecting 3 regions", 3, r.getAllRegions(Region.Type.ANY).size()); // test that removal doesn't affect parent traversal. assertEquals(r3, r.getRegion(fqn3, false)); r.removeRegion(Fqn.fromString(fqn3)); assertEquals("Expecting 2 regions", 2, r.getAllRegions(Region.Type.ANY).size()); // test that removal doesn't affect parent traversal. assertEquals("Should have retrieved parent region", r2, r.getRegion(fqn3, false)); r.removeRegion(Fqn.fromString(fqn2)); assertEquals("Expecting 1 region", 1, r.getAllRegions(Region.Type.ANY).size()); // test that removal doesn't affect parent traversal. assertEquals("Should have retrieved parent region", r1, r.getRegion(fqn3, false)); r.removeRegion(Fqn.fromString(fqn1)); assertEquals("Expecting 0 regions", 0, r.getAllRegions(Region.Type.ANY).size()); } public void testGetRegionsMethods() { String f1 = "/a", f2 = "/b", f3 = "/c", f4 = "/d"; r.setDefaultInactive(true); @SuppressWarnings("unused") Region r1 = r.getRegion(f1, true), r2 = r.getRegion(f2, true), r3 = r.getRegion(f3, true), r4 = r.getRegion(f4, true); assertEquals("4 regions should exist", 4, r.getAllRegions(Region.Type.ANY).size()); assertEquals("None of the regions should marshalling or active", 0, r.getAllRegions(Region.Type.MARSHALLING).size()); r3.registerContextClassLoader(getClass().getClassLoader()); r3.activate(); assertEquals("r3 should be marshalling and active", 1, r.getAllRegions(Region.Type.MARSHALLING).size()); assertSame("r3 should be marshalling and active", r3, r.getAllRegions(Region.Type.MARSHALLING).get(0)); r4.activate();// but don't se a class loader assertEquals("r3 should be marshalling and active", 1, r.getAllRegions(Region.Type.MARSHALLING).size()); assertSame("r3 should be marshalling and active", r3, r.getAllRegions(Region.Type.MARSHALLING).get(0)); r2.registerContextClassLoader(getClass().getClassLoader());// but don't activate assertEquals("r3 should be marshalling and active", 1, r.getAllRegions(Region.Type.MARSHALLING).size()); assertSame("r3 should be marshalling and active", r3, r.getAllRegions(Region.Type.MARSHALLING).get(0)); r2.activate(); assertEquals("r2 + r3 should be marshalling and active", 2, r.getAllRegions(Region.Type.MARSHALLING).size()); assertSame("r2 should be marshalling and active", r2, r.getAllRegions(Region.Type.MARSHALLING).get(0)); assertSame("r3 should be marshalling and active", r3, r.getAllRegions(Region.Type.MARSHALLING).get(1)); r4.registerContextClassLoader(getClass().getClassLoader()); assertEquals("r2 + r3 + r4 should be marshalling and active", 3, r.getAllRegions(Region.Type.MARSHALLING).size()); assertSame("r2 should be marshalling and active", r2, r.getAllRegions(Region.Type.MARSHALLING).get(0)); assertSame("r3 should be marshalling and active", r3, r.getAllRegions(Region.Type.MARSHALLING).get(1)); assertSame("r4 should be marshalling and active", r4, r.getAllRegions(Region.Type.MARSHALLING).get(2)); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/marshall/AsyncReplMarshalledValuesTest.java0000644000175000017500000000071210745367774033043 0ustar twernertwernerpackage org.jboss.cache.marshall; import org.testng.annotations.Test; /** * @author Manik Surtani (manik@jboss.org) * @since 2.1.0 */ @Test(groups = {"functional", "jgroups"}) public class AsyncReplMarshalledValuesTest extends AsyncReplTest { public AsyncReplMarshalledValuesTest() { useMarshalledValues = true; } @Override public void testCustomFqn() { // don't test this case } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/marshall/CacheMarshaller210Test.java0000644000175000017500000000715511015045001031176 0ustar twernertwernerpackage org.jboss.cache.marshall; import org.jboss.cache.Fqn; import org.jboss.cache.commands.remote.ReplicateCommand; import org.jboss.cache.commands.write.PutKeyValueCommand; import static org.testng.AssertJUnit.assertEquals; import org.testng.annotations.Test; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.HashMap; import java.util.Map; @Test(groups = {"functional"}) public class CacheMarshaller210Test extends CacheMarshaller200Test { public CacheMarshaller210Test() { currentVersion = "2.1.0.GA"; currentVersionShort = 21; expectedMarshallerClass = CacheMarshaller210.class; } protected void doMapTest(int size) throws Exception { Map map = createMap(size); Fqn fqn = Fqn.fromString("/my/stuff"); String key = "key"; PutKeyValueCommand putCommand = new PutKeyValueCommand(null, fqn, key, map); ReplicateCommand replicateCommand = new ReplicateCommand(putCommand); byte[] buf = marshaller.objectToByteBuffer(replicateCommand); assertEquals(replicateCommand, marshaller.objectFromByteBuffer(buf)); } protected Map createMap(int size) { Map map = new HashMap(size); for (int i = 0; i < size; i++) map.put("key-" + i, "value-" + i); return map; } public void testLargeNumberOfObjectReferences() throws Exception { doMapTest(500000); } public void testVInts() throws IOException { CacheMarshaller210 cm210 = (CacheMarshaller210) marshaller.defaultMarshaller; CacheMarshaller200 cm200 = (CacheMarshaller200) marshaller.getMarshaller(20); int[] ints = {2, 100, 500, 12000, 20000, 500000, 2000000, Integer.MAX_VALUE}; for (int i : ints) { System.out.println("CM200: Number of bytes (i=" + i + ") : " + getAndTestSize(cm200, i)); System.out.println("CM210: Number of bytes (i=" + i + ") : " + getAndTestSize(cm210, i)); } } public void testVLongs() throws IOException { CacheMarshaller210 cm210 = (CacheMarshaller210) marshaller.defaultMarshaller; CacheMarshaller200 cm200 = (CacheMarshaller200) marshaller.getMarshaller(20); long[] ints = {2, 100, 500, 12000, 20000, 500000, 2000000, Integer.MAX_VALUE, (long) Integer.MAX_VALUE + 500000L, Long.MAX_VALUE}; for (long i : ints) { System.out.println("CM200: Number of bytes (i=" + i + ") : " + getAndTestSize(cm200, i)); System.out.println("CM210: Number of bytes (i=" + i + ") : " + getAndTestSize(cm210, i)); } } private int getAndTestSize(CacheMarshaller200 m, int i) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); m.writeUnsignedInt(oos, i); oos.flush(); oos.close(); baos.flush(); baos.close(); byte[] bytes = baos.toByteArray(); int byteL = bytes.length; assert i == m.readUnsignedInt(new ObjectInputStream(new ByteArrayInputStream(bytes))); return byteL; } private int getAndTestSize(CacheMarshaller200 m, long i) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); m.writeUnsignedLong(oos, i); oos.flush(); oos.close(); baos.flush(); baos.close(); byte[] bytes = baos.toByteArray(); int byteL = bytes.length; assert i == m.readUnsignedLong(new ObjectInputStream(new ByteArrayInputStream(bytes))); return byteL; } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/marshall/CacheLoaderMarshallingTest.java0000644000175000017500000001263711034466673032301 0ustar twernertwernerpackage org.jboss.cache.marshall; import org.jboss.cache.Cache; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.Node; import org.jboss.cache.Region; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.EvictionConfig; import org.jboss.cache.config.EvictionRegionConfig; import org.jboss.cache.eviction.LRUConfiguration; import org.jboss.cache.eviction.LRUPolicy; import org.jboss.cache.loader.FileCacheLoaderConfig; import org.jboss.cache.util.TestingUtil; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertNotNull; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.io.File; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * Tests marshalling/unmarshalling during cache loader operations involving types * not visible to the cache's default classloader. * * @author Brian Stansberry * @author Galder Zamarreno * @since 2.1.0 */ @Test(groups = "functional") public class CacheLoaderMarshallingTest extends RegionBasedMarshallingTestBase { private static final String tmpDir = System.getProperty("java.io.tmpdir") + File.separatorChar + "CacheLoaderMarshallingTest"; private Cache cache; private Fqn fqn = Fqn.fromString("/a"); @BeforeMethod(alwaysRun = true) protected void setUp() throws Exception { originalClassLoader = Thread.currentThread().getContextClassLoader(); } @AfterMethod(alwaysRun = true) protected void tearDown() { resetContextClassLoader(); TestingUtil.killCaches(cache); File f = new File(tmpDir); if (f.exists()) if (!f.delete()) f.deleteOnExit(); } public void testCacheLoaderMarshalling() throws Exception { cacheLoaderMarshallingTest(false); } public void testCacheLoaderRegionBasedMarshalling() throws Exception { cacheLoaderMarshallingTest(true); } public void testLoadNodesAtRootOfRegion() throws Exception { String rootRegionName = "/myregion"; String hereFqn = rootRegionName + "/here"; cache = createCache(true); cache.start(); Region r = cache.getRegion(Fqn.fromString(rootRegionName), true); r.registerContextClassLoader(Thread.currentThread().getContextClassLoader()); r.activate(); cache.put(rootRegionName, "a key", "a value"); cache.put(hereFqn, "another key", "another value"); r.deactivate(); r.unregisterContextClassLoader(); cache.stop(); cache.start(); r = cache.getRegion(Fqn.fromString(rootRegionName), true); r.registerContextClassLoader(Thread.currentThread().getContextClassLoader()); r.activate(); Node rootRegionNode = cache.getNode(rootRegionName); Node hereNode = cache.getNode(hereFqn); assertNotNull(rootRegionNode); assertNotNull(hereNode); assertEquals(hereNode.get("another key"), "another value"); assertEquals(rootRegionNode.get("a key"), "a value"); } private void cacheLoaderMarshallingTest(boolean useRegionBased) throws Exception { cache = createCache(useRegionBased); cache.start(); FooClassLoader loader = new FooClassLoader(originalClassLoader); if (useRegionBased) { Region r = cache.getRegion(Fqn.ROOT, true); r.registerContextClassLoader(loader); r.activate(); } Class clazz = loader.loadFoo(); Object obj = clazz.newInstance(); Thread.currentThread().setContextClassLoader(loader); cache.put(fqn, "key", obj); this.resetContextClassLoader(); cache.evict(fqn); Thread.currentThread().setContextClassLoader(loader); assertEquals(obj, cache.get(fqn, "key")); } private Cache createCache(boolean useRegionBased) { Cache cache = new DefaultCacheFactory().createCache(false); Configuration config = cache.getConfiguration(); config.setUseRegionBasedMarshalling(useRegionBased); config.setInactiveOnStartup(useRegionBased); EvictionConfig ec = new EvictionConfig(); ec.setDefaultEvictionPolicyClass(LRUPolicy.class.getName()); ec.setWakeupIntervalSeconds(1000); // a long time; really disabled EvictionRegionConfig erc = new EvictionRegionConfig(); erc.setRegionFqn(Fqn.ROOT); erc.setRegionName("_default_"); LRUConfiguration epc = new LRUConfiguration(); epc.setMaxNodes(1000); epc.setTimeToLiveSeconds(1000); erc.setEvictionPolicyConfig(epc); List ercs = new ArrayList(); ercs.add(erc); ec.setEvictionRegionConfigs(ercs); config.setEvictionConfig(ec); CacheLoaderConfig clc = new CacheLoaderConfig(); clc.setPassivation(true); clc.setShared(false); FileCacheLoaderConfig fclc = new FileCacheLoaderConfig(); fclc.setLocation(tmpDir); clc.setIndividualCacheLoaderConfigs(Collections.singletonList(fclc)); config.setCacheLoaderConfig(clc); return cache; } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/marshall/ActiveInactiveTest.java0000644000175000017500000001325111017455042030641 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.marshall; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.Region; import org.jboss.cache.RegionManager; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.commands.ReplicableCommand; import org.jboss.cache.commands.remote.ReplicateCommand; import org.jboss.cache.commands.write.PutKeyValueCommand; import org.jboss.cache.config.Configuration; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * Tests the "activate/deactivate" functionality of LegacyTreeCacheMarshaller. * * @author Brian Stansberry * @version $Revision$ */ @Test(groups = "functional") public class ActiveInactiveTest extends AbstractVersionAwareMarshallerTest { RegionManager rman; CacheSPI cache; Configuration c; Fqn A = Fqn.fromString("/a"); Fqn I = Fqn.fromString("/i"); Fqn A_B = Fqn.fromRelativeElements(A, "b"); @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { cache = (CacheSPI) new DefaultCacheFactory().createCache(false); c = cache.getConfiguration(); c.setUseRegionBasedMarshalling(true); c.setFetchInMemoryState(false); cache.start(); cr = TestingUtil.extractComponentRegistry(cache); rman = TestingUtil.extractComponentRegistry(cache).getComponent(RegionManager.class); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { TestingUtil.killCaches(cache); } public void testDefaultActive() throws Exception { rman.setDefaultInactive(false); assertFalse("Root is not active", rman.isInactive(Fqn.ROOT)); rman.deactivate(A); assertFalse("Root is not active after inactivating subtree", rman.isInactive(Fqn.ROOT)); rman.activate(A); assertFalse("Root is not active after activating subtree", rman.isInactive(Fqn.ROOT)); rman.activate(A_B); rman.deactivate(Fqn.ROOT); assertTrue("Root is active", rman.isInactive(Fqn.ROOT)); } public void testDefaultInactive() throws Exception { rman.setDefaultInactive(true); assertTrue("Root is not inactive", rman.isInactive(Fqn.ROOT)); rman.activate(A); assertTrue("Root is not inactive after activating subtree", rman.isInactive(Fqn.ROOT)); rman.deactivate(A); assertTrue("Root is not inactive after inactivating subtree", rman.isInactive(Fqn.ROOT)); rman.deactivate(A_B); rman.activate(Fqn.ROOT); assertFalse("Root is not active", rman.isInactive(Fqn.ROOT)); } public void testActivate() throws Exception { rman.setDefaultInactive(false); rman.activate(A); assertFalse("/a is not active after activating", rman.isInactive(A)); rman.deactivate(A); rman.activate(A); assertFalse("/a is not active after reactivating", rman.isInactive(A)); rman.reset(); rman.setDefaultInactive(true); rman.activate(I); assertFalse("/i is not active after activating", rman.isInactive(I)); assertFalse("/i/k is not active after activating /i", rman.isInactive(Fqn.fromString("/i/k"))); rman.deactivate(I); rman.activate(I); assertFalse("/i is not active after reactivating", rman.isInactive(I)); } public void testInactivate() throws Exception { rman.setDefaultInactive(true); rman.deactivate(I); assertTrue("/i is not inactive after inactivating", rman.isInactive(I)); rman.activate(I); rman.deactivate(I); assertTrue("/i is not inactive after re-inactivating", rman.isInactive(I)); rman.reset(); rman.setDefaultInactive(false); rman.deactivate(A); assertTrue("/a is not inactive after inactivating", rman.isInactive(A)); assertTrue("/a/b is not inactive after inactivating /a", rman.isInactive(A_B)); rman.activate(A); rman.deactivate(A); assertTrue("/a is not inactive after re-inactivating", rman.isInactive(A)); } public void testObjectFromByteBuffer() throws Exception { PutKeyValueCommand put = new PutKeyValueCommand(null, A_B, "name", "Joe"); ReplicateCommand replicate = new ReplicateCommand(put); rman.setDefaultInactive(true); // register A as an inactive marshalling region Region region_A = rman.getRegion(A, true); region_A.registerContextClassLoader(this.getClass().getClassLoader()); assertFalse("New regions created should be inactive by default", region_A.isActive()); cache.stop(); c.setUseRegionBasedMarshalling(true); c.setInactiveOnStartup(true); VersionAwareMarshaller testee = createVAMandRestartCache(rman); byte[] callBytes = testee.objectToByteBuffer(replicate); try { testee.objectFromByteBuffer(callBytes); fail("Expected to fail since region is inactive"); } catch (Exception e) { // expected to fail since region is inactive } rman.activate(A); assertTrue(rman.hasRegion(A, Region.Type.MARSHALLING)); ReplicableCommand result = (ReplicableCommand) testee.objectFromByteBuffer(callBytes); assertEquals("Did not get replicate method when passing" + " call for active node", ReplicateCommand.class, result.getClass()); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/marshall/ReplicateToInactiveRegionTest.java0000644000175000017500000000545011017455042033007 0ustar twernertwernerpackage org.jboss.cache.marshall; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.Region; import org.jboss.cache.util.TestingUtil; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; @Test(groups = {"functional", "jgroups"}) public class ReplicateToInactiveRegionTest { CacheSPI[] caches; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { caches = new CacheSPI[]{createCache(), createCache()}; TestingUtil.blockUntilViewsReceived(caches, 10000); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { destroyCache(caches[0]); destroyCache(caches[1]); caches = null; } private void destroyCache(CacheSPI c) { c.stop(); } private CacheSPI createCache() { CacheSPI c = (CacheSPI) new DefaultCacheFactory().createCache(false); c.getConfiguration().setCacheMode("REPL_SYNC"); c.getConfiguration().setUseRegionBasedMarshalling(true); c.start(); return c; } @SuppressWarnings("unchecked") public void testTransferToInactiveRegion() { Fqn f = Fqn.fromString("/a/b"); caches[0].put(f, "k", "v"); assertEquals("v", caches[0].get(f, "k")); assertEquals("v", caches[1].get(f, "k")); // create the region on cache 0, make sure it is marked as a MARSHALLING region by attaching a class loader to it. Region region0 = caches[0].getRegionManager().getRegion(f, true); region0.registerContextClassLoader(this.getClass().getClassLoader()); // just to make sure this is recognised as a marshalling region. assertTrue("Should be active by default", region0.isActive()); // make sure this newly created region is "recognised" as a marshalling region. assertTrue(caches[0].getRegionManager().getAllRegions(Region.Type.MARSHALLING).contains(region0)); // now create a region on cache 1, as above. Region region1 = caches[1].getRegionManager().getRegion(f, true); region1.registerContextClassLoader(this.getClass().getClassLoader()); // just to make sure this is recognised as a marshalling region. assertTrue("Should be active by default", region1.isActive()); // make sure this newly created region is "recognised" as a marshalling region. assertTrue(caches[1].getRegionManager().getAllRegions(Region.Type.MARSHALLING).contains(region1)); // now deactivate the region on cache 1. region1.deactivate(); assertFalse("Should be have deactivated", region1.isActive()); caches[0].put(f, "k", "v2"); assertEquals("v2", caches[0].get(f, "k")); assertNull(caches[1].get(f, "k")); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/marshall/CustomCollectionTest.java0000644000175000017500000002247010745240616031242 0ustar twernertwernerpackage org.jboss.cache.marshall; import org.jboss.cache.Cache; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.Region; import org.jboss.cache.config.Configuration; import org.jboss.cache.lock.IsolationLevel; import static org.jboss.cache.marshall.CustomCollectionTest.MarshallingMode.*; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.io.Serializable; import java.util.List; import java.util.Map; import java.util.Set; /** * Unit test to cover replication and marshalling of custom collections * * @author Galder Zamarreno * @author Manik Surtani (manik@jboss.org) */ @Test(groups = {"functional"}) public class CustomCollectionTest extends RegionBasedMarshallingTestBase implements Serializable { private transient Cache cache1 = null; private transient Cache cache2 = null; private String myListClass = MyList.class.getName(); private String mySetClass = MySet.class.getName(); private String myMapClass = MyMap.class.getName(); @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { cache1 = createCache(); cache2 = createCache(); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { cache1.stop(); cache2.stop(); } public void testMap() throws Exception { doMapTest(DEFAULT_WITHOUT_MARSHALLED_VALUES); } public void testMapWithRegions() throws Exception { doMapTest(CUSTOM_CLASSLOADER_WITH_REGIONS); } public void testMapWithMarshalledValues() throws Exception { doMapTest(CUSTOM_CLASSLOADER_WITH_MARSHALLEDVALUES); } public void testMapWithMarshalledValuesDefaultClassloader() throws Exception { doMapTest(DEFAULT_WITH_MARSHALLED_VALUES); } public void testSet() throws Exception { doSetTest(DEFAULT_WITHOUT_MARSHALLED_VALUES); } public void testSetWithRegions() throws Exception { doSetTest(CUSTOM_CLASSLOADER_WITH_REGIONS); } public void testSetWithMarshalledValues() throws Exception { doSetTest(CUSTOM_CLASSLOADER_WITH_MARSHALLEDVALUES); } public void testSetWithMarshalledValuesDefaultClassloader() throws Exception { doSetTest(DEFAULT_WITH_MARSHALLED_VALUES); } public void testList() throws Exception { doListTest(DEFAULT_WITHOUT_MARSHALLED_VALUES); } public void testListWithRegions() throws Exception { doListTest(CUSTOM_CLASSLOADER_WITH_REGIONS); } public void testListWithMarshalledValues() throws Exception { doListTest(CUSTOM_CLASSLOADER_WITH_MARSHALLEDVALUES); } public void testListWithMarshalledValuesDefaultClassloader() throws Exception { doListTest(DEFAULT_WITH_MARSHALLED_VALUES); } enum MarshallingMode { DEFAULT_WITH_MARSHALLED_VALUES, DEFAULT_WITHOUT_MARSHALLED_VALUES, CUSTOM_CLASSLOADER_WITH_REGIONS, CUSTOM_CLASSLOADER_WITH_MARSHALLEDVALUES; boolean isUsingCustomClassLoader() { return this == CUSTOM_CLASSLOADER_WITH_MARSHALLEDVALUES || this == CUSTOM_CLASSLOADER_WITH_REGIONS; } } @SuppressWarnings("unchecked") private void doMapTest(MarshallingMode marshallingMode) throws Exception { ClassLoader customClassLoader = getCollectionsClassLoader(); Class mapClass = customClassLoader.loadClass(myMapClass); Map map = (Map) (marshallingMode.isUsingCustomClassLoader() ? mapClass.newInstance() : new MyMap()); map.put("k", "v"); configureCaches(customClassLoader, marshallingMode); cache1.start(); cache2.start(); if (marshallingMode == CUSTOM_CLASSLOADER_WITH_MARSHALLEDVALUES) Thread.currentThread().setContextClassLoader(customClassLoader); cache1.put(fqn("/a"), "key", map); Object o = cache2.get(fqn("/a"), "key"); if (marshallingMode == CUSTOM_CLASSLOADER_WITH_MARSHALLEDVALUES) resetContextClassLoader(); assertTrue(o instanceof Map); if (marshallingMode.isUsingCustomClassLoader()) { assertNotSame(MyMap.class, o.getClass()); assertSame(mapClass, o.getClass()); } else { assertSame(MyMap.class, o.getClass()); assertNotSame(mapClass, o.getClass()); } assertEquals("v", ((Map) o).get("k")); } @SuppressWarnings("unchecked") private void doSetTest(MarshallingMode marshallingMode) throws Exception { ClassLoader customClassLoader = getCollectionsClassLoader(); Class setClass = customClassLoader.loadClass(mySetClass); Set set = (Set) (marshallingMode.isUsingCustomClassLoader() ? setClass.newInstance() : new MySet()); set.add("k"); configureCaches(customClassLoader, marshallingMode); cache1.start(); cache2.start(); if (marshallingMode == CUSTOM_CLASSLOADER_WITH_MARSHALLEDVALUES) Thread.currentThread().setContextClassLoader(customClassLoader); cache1.put(fqn("/a"), "key", set); Object o = cache2.get(fqn("/a"), "key"); if (marshallingMode == CUSTOM_CLASSLOADER_WITH_MARSHALLEDVALUES) resetContextClassLoader(); assertTrue(o instanceof Set); if (marshallingMode.isUsingCustomClassLoader()) { assertNotSame(MySet.class, o.getClass()); assertSame(setClass, o.getClass()); } else { assertSame(MySet.class, o.getClass()); assertNotSame(setClass, o.getClass()); } assertTrue(((Set) o).contains("k")); } @SuppressWarnings("unchecked") private void doListTest(MarshallingMode marshallingMode) throws Exception { ClassLoader customClassLoader = getCollectionsClassLoader(); Class listClass = customClassLoader.loadClass(myListClass); List list = (List) (marshallingMode.isUsingCustomClassLoader() ? listClass.newInstance() : new MyList()); list.add("k"); configureCaches(customClassLoader, marshallingMode); cache1.start(); cache2.start(); if (marshallingMode == CUSTOM_CLASSLOADER_WITH_MARSHALLEDVALUES) Thread.currentThread().setContextClassLoader(customClassLoader); cache1.put(fqn("/a"), "key", list); Object o = cache2.get(fqn("/a"), "key"); if (marshallingMode == CUSTOM_CLASSLOADER_WITH_MARSHALLEDVALUES) resetContextClassLoader(); assertTrue(o instanceof List); if (marshallingMode.isUsingCustomClassLoader()) { assertSame(listClass, o.getClass()); assertNotSame(MyList.class, o.getClass()); } else { assertSame(MyList.class, o.getClass()); assertNotSame(listClass, o.getClass()); } assertTrue(((List) o).contains("k")); } public void testHarnessClassLoader() throws Exception { Class myListFromCustomLoader = getCollectionsClassLoader().loadClass(myListClass); assertNotNull(myListFromCustomLoader); assertFalse(MyList.class.equals(myListFromCustomLoader)); Object customLoaderMyList = myListFromCustomLoader.newInstance(); try { @SuppressWarnings("unused") MyList m = (MyList) customLoaderMyList; fail("Should have barfed"); } catch (ClassCastException cce) { // expected } } private void configureCaches(ClassLoader customClassLoader, MarshallingMode marshallingMode) { switch (marshallingMode) { case CUSTOM_CLASSLOADER_WITH_REGIONS: cache1.getConfiguration().setUseRegionBasedMarshalling(true); Region region1 = cache1.getRegion(fqn("/a"), true); region1.registerContextClassLoader(customClassLoader); cache2.getConfiguration().setUseRegionBasedMarshalling(true); Region region2 = cache2.getRegion(fqn("/a"), true); region2.registerContextClassLoader(customClassLoader); // and carry on to the same setting as DEFAULT_WITHOUT_MARSHALLED_VALUES case DEFAULT_WITHOUT_MARSHALLED_VALUES: cache1.getConfiguration().setUseLazyDeserialization(false); cache2.getConfiguration().setUseLazyDeserialization(false); break; case DEFAULT_WITH_MARSHALLED_VALUES: case CUSTOM_CLASSLOADER_WITH_MARSHALLEDVALUES: cache1.getConfiguration().setUseLazyDeserialization(true); cache2.getConfiguration().setUseLazyDeserialization(true); break; } } private ClassLoader getCollectionsClassLoader() { String[] includesClasses = {myListClass, mySetClass, myMapClass}; String[] excludesClasses = {}; ClassLoader cl = Thread.currentThread().getContextClassLoader(); return new SelectedClassnameClassLoader(includesClasses, excludesClasses, cl); } private Cache createCache() { Cache cache = new DefaultCacheFactory().createCache(false); cache.getConfiguration().setCacheMode(Configuration.CacheMode.REPL_SYNC); cache.getConfiguration().setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); cache.getConfiguration().setIsolationLevel(IsolationLevel.REPEATABLE_READ); return cache; } private static Fqn fqn(String fqn) { return Fqn.fromString(fqn); } }jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/marshall/SyncReplMarshalledValuesTest.java0000644000175000017500000000073310745240616032666 0ustar twernertwernerpackage org.jboss.cache.marshall; import org.testng.annotations.Test; /** * @author Manik Surtani (manik@jboss.org) * @since 2.1.0 */ @Test(groups = {"functional", "jgroups"}) public class SyncReplMarshalledValuesTest extends SyncReplTest { public SyncReplMarshalledValuesTest() { useMarshalledValues = true; } @Override public void testCustomFqn() { // meaningless when using marshalled values } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/marshall/MySet.java0000644000175000017500000000025310665031725026150 0ustar twernertwernerpackage org.jboss.cache.marshall; import java.util.HashSet; public class MySet extends HashSet { private static final long serialVersionUID = 2164839240744662205L; } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/marshall/AsyncReplTest.java0000644000175000017500000002572311020007335027642 0ustar twernertwerner/* * * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.marshall; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.Region; import org.jboss.cache.commands.write.PutKeyValueCommand; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.factories.UnitTestCacheConfigurationFactory; import org.jboss.cache.marshall.data.Address; import org.jboss.cache.marshall.data.Person; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.util.internals.ReplicationListener; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.*; import java.lang.reflect.Method; /** * Test marshalling for async mode. * * @author Ben Wang * @version $Revision: 5919 $ */ @Test(groups = {"functional", "jgroups"}) public class AsyncReplTest extends RegionBasedMarshallingTestBase { CacheSPI cache1, cache2; String props = null; Person ben; Address addr; Throwable ex; private Fqn aop = Fqn.fromString("/aop"); protected boolean useMarshalledValues = false; ReplicationListener replListener1; ReplicationListener replListener2; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { resetContextClassLoader(); log("creating cache1"); cache1 = createCache("TestCache"); log("creating cache2"); cache2 = createCache("TestCache"); replListener1 = new ReplicationListener(cache1); replListener2 = new ReplicationListener(cache2); addr = new Address(); addr.setCity("San Jose"); ben = new Person(); ben.setName("Ben"); ben.setAddress(addr); // Pause to give caches time to see each other TestingUtil.blockUntilViewsReceived(new CacheSPI[]{cache1, cache2}, 60000); } private CacheSPI createCache(String name) { CacheSPI cache = (CacheSPI) new DefaultCacheFactory().createCache(UnitTestCacheConfigurationFactory.createConfiguration(CacheMode.REPL_ASYNC), false); cache.getConfiguration().setClusterName(name); // Use marshaller cache.getConfiguration().setUseLazyDeserialization(useMarshalledValues); cache.getConfiguration().setUseRegionBasedMarshalling(!useMarshalledValues); cache.create(); cache.start(); return cache; } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { TestingUtil.killCaches(cache1, cache2); resetContextClassLoader(); } /** * Test replication with classloaders. * * @throws Exception */ public void testCLSet2() throws Exception { ClassLoader cla = getClassLoader(); ClassLoader clb = getClassLoader(); if (!useMarshalledValues) { Region existing = cache1.getRegion(aop, true); existing.registerContextClassLoader(cla); existing = cache2.getRegion(aop, true); existing.registerContextClassLoader(clb); } if (useMarshalledValues) Thread.currentThread().setContextClassLoader(cla); replListener2.expect(PutKeyValueCommand.class); cache1.put(aop, "person", ben); replListener2.waitForReplicationToOccur(500); replListener2.expect(PutKeyValueCommand.class); cache1.put(Fqn.fromString("/alias"), "person", ben); replListener2.waitForReplicationToOccur(500); if (useMarshalledValues) resetContextClassLoader(); Object ben2 = null; // Can't cast it to Person. CCE will resutl. if (useMarshalledValues) Thread.currentThread().setContextClassLoader(clb); ben2 = cache2.get(aop, "person"); if (useMarshalledValues) resetContextClassLoader(); assertNotNull(ben2); assertEquals(ben.toString(), ben2.toString()); Class claz = clb.loadClass(ADDRESS_CLASSNAME); Object add = claz.newInstance(); Method setValue = claz.getMethod("setCity", String.class); setValue.invoke(add, "Sunnyvale"); Class clasz1 = clb.loadClass(PERSON_CLASSNAME); setValue = clasz1.getMethod("setAddress", claz); setValue.invoke(ben2, add); // Set it back to the cache // Can't cast it to Person. CCE will resutl. if (useMarshalledValues) Thread.currentThread().setContextClassLoader(clb); replListener1.expect(PutKeyValueCommand.class); cache2.put(aop, "person", ben2); replListener1.waitForReplicationToOccur(1000); if (useMarshalledValues) resetContextClassLoader(); if (useMarshalledValues) Thread.currentThread().setContextClassLoader(cla); Object ben3 = cache1.get(aop, "person"); if (useMarshalledValues) resetContextClassLoader(); assertEquals(ben2.toString(), ben3.toString()); } public void testPuts() throws Exception { ClassLoader cla = getClassLoader(); ClassLoader clb = getClassLoader(); if (!useMarshalledValues) { Region r1 = cache1.getRegion(aop, false) == null ? cache1.getRegion(aop, true) : cache1.getRegion(aop, false); r1.registerContextClassLoader(cla); Region r2 = cache2.getRegion(aop, false) == null ? cache2.getRegion(aop, true) : cache2.getRegion(aop, false); r2.registerContextClassLoader(clb); } // Create an empty Person loaded by this classloader Object scopedBen1 = getPersonFromClassloader(cla); // Create another empty Person loaded by this classloader Object scopedBen2 = getPersonFromClassloader(clb); if (useMarshalledValues) Thread.currentThread().setContextClassLoader(cla); replListener2.expect(PutKeyValueCommand.class); cache1.put(Fqn.fromString("/aop/1"), "person", ben); replListener2.waitForReplicationToOccur(1000); replListener2.expect(PutKeyValueCommand.class); cache1.put(Fqn.fromString("/aop/2"), "person", scopedBen1); replListener2.waitForReplicationToOccur(1000); if (useMarshalledValues) resetContextClassLoader(); Object ben2 = null; // Can't cast it to Person. CCE will resutl. if (useMarshalledValues) Thread.currentThread().setContextClassLoader(clb); ben2 = cache2.get(Fqn.fromString("/aop/1"), "person"); assertEquals(ben.toString(), ben2.toString()); ben2 = cache2.get(Fqn.fromString("/aop/2"), "person"); assertFalse("cache2 deserialized with scoped classloader", ben2 instanceof Person); assertFalse("cache2 deserialized with cache2 classloader", scopedBen1.equals(ben2)); assertEquals("scopedBen deserialized properly", scopedBen2, ben2); } public void testTxPut() throws Exception { replListener2.expectAny(); beginTransaction(); cache1.put(aop, "person", ben); commit(); replListener2.waitForReplicationToOccur(1000); Person ben2 = (Person) cache2.get(aop, "person"); assertNotNull("Person from 2nd cache should not be null ", ben2); assertEquals(ben.toString(), ben2.toString()); } public void testTxCLSet2() throws Exception { ClassLoader cla = getClassLoader(); ClassLoader clb = getClassLoader(); if (!useMarshalledValues) { Region r1 = cache1.getRegion(aop, false) == null ? cache1.getRegion(aop, true) : cache1.getRegion(aop, false); r1.registerContextClassLoader(cla); Region r2 = cache2.getRegion(aop, false) == null ? cache2.getRegion(aop, true) : cache2.getRegion(aop, false); r2.registerContextClassLoader(clb); } if (useMarshalledValues) Thread.currentThread().setContextClassLoader(cla); replListener2.expectAny(); beginTransaction(); cache1.put(aop, "person", ben); commit(); replListener2.waitForReplicationToOccur(1000); if (useMarshalledValues) resetContextClassLoader(); Object ben2 = null; // Can't cast it to Person. CCE will resutl. if (useMarshalledValues) Thread.currentThread().setContextClassLoader(clb); ben2 = cache2.get(aop, "person"); if (useMarshalledValues) resetContextClassLoader(); assertEquals(ben.toString(), ben2.toString()); Class claz = clb.loadClass(ADDRESS_CLASSNAME); Object add = claz.newInstance(); Method setValue = claz.getMethod("setCity", String.class); setValue.invoke(add, "Sunnyvale"); Class clasz1 = clb.loadClass(PERSON_CLASSNAME); setValue = clasz1.getMethod("setAddress", claz); setValue.invoke(ben2, add); // Set it back to the cache // Can't cast it to Person. CCE will resutl. if (useMarshalledValues) Thread.currentThread().setContextClassLoader(clb); replListener1.expectAny(); cache2.put(aop, "person", ben2); if (useMarshalledValues) resetContextClassLoader(); replListener1.waitForReplicationToOccur(100); if (useMarshalledValues) Thread.currentThread().setContextClassLoader(cla); Object ben3 = cache1.get(aop, "person"); if (useMarshalledValues) resetContextClassLoader(); assertEquals(ben2.toString(), ben3.toString()); } public void testCustomFqn() throws Exception { FooClassLoader cl1 = new FooClassLoader(Thread.currentThread().getContextClassLoader()); Region r1 = cache1.getRegion(aop, false) == null ? cache1.getRegion(aop, true) : cache1.getRegion(aop, false); r1.registerContextClassLoader(cl1); FooClassLoader cl2 = new FooClassLoader(Thread.currentThread().getContextClassLoader()); Region r2 = cache2.getRegion(aop, false) == null ? cache2.getRegion(aop, true) : cache2.getRegion(aop, false); r2.registerContextClassLoader(cl2); Class clazz = cl1.loadFoo(); Object custom1 = clazz.newInstance(); clazz = cl2.loadFoo(); Object custom2 = clazz.newInstance(); Fqn base = Fqn.fromString("/aop"); Fqn fqn = Fqn.fromRelativeElements(base, custom1); replListener2.expectAny(); cache1.put(fqn, "key", "value"); replListener2.waitForReplicationToOccur(1000); Fqn fqn2 = Fqn.fromRelativeElements(base, custom2); Object val = cache2.get(fqn2, "key"); assertEquals("value", val); } private Transaction beginTransaction() throws SystemException, NotSupportedException { TransactionManager mgr = cache1.getConfiguration().getRuntimeConfig().getTransactionManager(); mgr.begin(); return mgr.getTransaction(); } private void commit() throws SecurityException, IllegalStateException, RollbackException, HeuristicMixedException, HeuristicRollbackException, SystemException { cache1.getConfiguration().getRuntimeConfig().getTransactionManager().commit(); } protected Object getPersonFromClassloader(ClassLoader cl) throws Exception { Class clazz = cl.loadClass(PERSON_CLASSNAME); return clazz.newInstance(); } private void log(String msg) { System.out.println("-- [" + Thread.currentThread() + "]: " + msg); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/marshall/RegionBasedMarshallingTestBase.java0000644000175000017500000000242210745240616033106 0ustar twernertwernerpackage org.jboss.cache.marshall; import org.testng.annotations.Test; @Test(groups = {"functional"}) public abstract class RegionBasedMarshallingTestBase { protected static final String ADDRESS_CLASSNAME = "org.jboss.cache.marshall.data.Address"; protected static final String PERSON_CLASSNAME = "org.jboss.cache.marshall.data.Person"; protected ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader(); protected ClassLoader getClassLoader() throws Exception { String[] includesClasses = {PERSON_CLASSNAME, ADDRESS_CLASSNAME}; String[] excludesClasses = {}; ClassLoader cl = Thread.currentThread().getContextClassLoader(); return new SelectedClassnameClassLoader(includesClasses, excludesClasses, cl); } protected ClassLoader getFailingClassLoader() throws Exception { String[] includesClasses = {}; String[] excludesClasses = {}; String[] failingClasses = {PERSON_CLASSNAME, ADDRESS_CLASSNAME}; ClassLoader cl = Thread.currentThread().getContextClassLoader(); return new SelectedClassnameClassLoader(includesClasses, excludesClasses, failingClasses, cl); } protected void resetContextClassLoader() { Thread.currentThread().setContextClassLoader(originalClassLoader); } } ././@LongLink0000000000000000000000000000014700000000000011567 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/marshall/InvalidRegionForStateTransferTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/marshall/InvalidRegionForStateTransferTest.ja0000644000175000017500000000703711020007335033320 0ustar twernertwernerpackage org.jboss.cache.marshall; import org.jboss.cache.Cache; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.Region; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.util.internals.ReplicationListener; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.UnitTestCacheConfigurationFactory; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * This test ensures the CacheMarshaller doesn't use stale regions when attempting to unmarshall state transfers. Seen intermittently * when async replication is used, since JGroups doesn't attempt to marshall a return value, thereby leaving a stale region in thread local * in the cache marshaller, which then gets reused when the thread is reused to provide state. *

* Need to ensure that the same thread is used to process incoming requests as well as state transfers, hence limiting the JGroups * thread pool size to 1. *

* * @author Manik Surtani (manik@jboss.org) * @since 2.1.0 */ @Test(groups = "functional") public class InvalidRegionForStateTransferTest { Cache c1, c2; ReplicationListener replListener2; @BeforeMethod public void setUp() throws CloneNotSupportedException { c1 = new DefaultCacheFactory().createCache(UnitTestCacheConfigurationFactory.createConfiguration(Configuration.CacheMode.REPL_ASYNC), false); String jgroupsCfg = c1.getConfiguration().getClusterConfig(); // make sure we use STATE_TRANSFER and not STREAMING_STATE_TRANSFER, so the same thread pool is used for incoming calls and ST jgroupsCfg = jgroupsCfg.replace("STREAMING_STATE_TRANSFER", "STATE_TRANSFER"); // also make sure we use a thread pool size of 1 jgroupsCfg = jgroupsCfg.replaceFirst("thread_pool.max_threads=[0-9]*;", "thread_pool.max_threads=1;"); System.out.println(">>>> " + jgroupsCfg); c1.getConfiguration().setClusterConfig(jgroupsCfg); c1.getConfiguration().setUseRegionBasedMarshalling(true); c1.start(); c2 = new DefaultCacheFactory().createCache(c1.getConfiguration().clone()); replListener2 = new ReplicationListener(c2); TestingUtil.blockUntilViewsReceived(60000, c1, c2); } @AfterMethod public void tearDown() { TestingUtil.killCaches(c1, c2); } public void testUseOfInvalidRegion() { Fqn fqn = Fqn.fromString("/a/b/c/d"); c1.getRegion(fqn.getParent(), true).registerContextClassLoader(getClass().getClassLoader()); c2.getRegion(fqn.getParent(), true).registerContextClassLoader(getClass().getClassLoader()); replListener2.expectAny(); // write something; will cause a stale region to be stored in C2's cache marshaller c1.put(fqn, "k", "v"); assert c1.get(fqn, "k").equals("v"); replListener2.waitForReplicationToOccur(250); // assert that this made it to c2 assert c2.get(fqn, "k").equals("v"); // c2's cache marshaller's thread local would be polluted now. // restart c1 so that it forces a state transfer from c2 c1.destroy(); c1.create(); Region r = c1.getRegion(fqn.getParent(), true); r.registerContextClassLoader(getClass().getClassLoader()); r.deactivate(); c1.start(); TestingUtil.blockUntilViewsReceived(60000, c1, c2); // assert that the state has been transferred to C1 assert c1.get(fqn, "k").equals("v"); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/marshall/FooClassLoader.java0000644000175000017500000001723210701213260027737 0ustar twernertwernerpackage org.jboss.cache.marshall; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; public class FooClassLoader extends ClassLoader { private Class foo; private boolean useCachedByteStream = true; public FooClassLoader(ClassLoader parent) { super(parent); } public Class loadFoo() throws ClassNotFoundException { if (foo == null) { byte[] bytes; if (useCachedByteStream) { bytes = getFooClazzAsBytes(); } else { try { InputStream is = getResourceAsStream("org/jboss/cache/marshall/Foo.clazz"); bytes = new byte[1024]; ByteArrayOutputStream baos = new ByteArrayOutputStream(1024); int read; while ((read = is.read(bytes)) > -1) { baos.write(bytes, 0, read); } bytes = getFooClazzAsBytes(); } catch (IOException e) { throw new ClassNotFoundException("cannot read org/jboss/cache/marshall/Foo.clazz", e); } } foo = this.defineClass("org.jboss.cache.marshall.Foo", bytes, 0, bytes.length); } return foo; } private byte[] getFooClazzAsBytes() { // GENERATED using main() method to read org/jboss/cache/marshall/Foo.clazz into a byte[] // so that this byte stream is available even if Foo.clazz is not included in the test classpath by Maven // Copy out this generated snippet into FooClassLoader.java and use this byte[] instead of // trying to read Foo.clazz off the classpath. return new byte[]{ -54, -2, -70, -66, 0, 0, 0, 46, 0, 61, 7, 0, 2, 1, 0, 28, 111, 114, 103, 47, 106, 98, 111, 115, 115, 47, 99, 97, 99, 104, 101, 47, 109, 97, 114, 115, 104, 97, 108, 108, 47, 70, 111, 111, 7, 0, 4, 1, 0, 16, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 79, 98, 106, 101, 99, 116, 7, 0, 6, 1, 0, 20, 106, 97, 118, 97, 47, 105, 111, 47, 83, 101, 114, 105, 97, 108, 105, 122, 97, 98, 108, 101, 1, 0, 16, 115, 101, 114, 105, 97, 108, 86, 101, 114, 115, 105, 111, 110, 85, 73, 68, 1, 0, 1, 74, 1, 0, 13, 67, 111, 110, 115, 116, 97, 110, 116, 86, 97, 108, 117, 101, 5, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 6, 60, 105, 110, 105, 116, 62, 1, 0, 3, 40, 41, 86, 1, 0, 4, 67, 111, 100, 101, 10, 0, 3, 0, 16, 12, 0, 12, 0, 13, 1, 0, 15, 76, 105, 110, 101, 78, 117, 109, 98, 101, 114, 84, 97, 98, 108, 101, 1, 0, 18, 76, 111, 99, 97, 108, 86, 97, 114, 105, 97, 98, 108, 101, 84, 97, 98, 108, 101, 1, 0, 4, 116, 104, 105, 115, 1, 0, 30, 76, 111, 114, 103, 47, 106, 98, 111, 115, 115, 47, 99, 97, 99, 104, 101, 47, 109, 97, 114, 115, 104, 97, 108, 108, 47, 70, 111, 111, 59, 1, 0, 6, 101, 113, 117, 97, 108, 115, 1, 0, 21, 40, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 79, 98, 106, 101, 99, 116, 59, 41, 90, 1, 0, 3, 111, 98, 106, 1, 0, 18, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 79, 98, 106, 101, 99, 116, 59, 1, 0, 8, 104, 97, 115, 104, 67, 111, 100, 101, 1, 0, 3, 40, 41, 73, 1, 0, 8, 116, 111, 83, 116, 114, 105, 110, 103, 1, 0, 20, 40, 41, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 7, 0, 30, 1, 0, 26, 106, 97, 118, 97, 47, 115, 101, 99, 117, 114, 105, 116, 121, 47, 83, 101, 99, 117, 114, 101, 82, 97, 110, 100, 111, 109, 10, 0, 29, 0, 16, 7, 0, 33, 1, 0, 22, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 66, 117, 102, 102, 101, 114, 8, 0, 35, 1, 0, 36, 111, 114, 103, 46, 106, 98, 111, 115, 115, 46, 99, 97, 99, 104, 101, 46, 109, 97, 114, 115, 104, 97, 108, 108, 46, 70, 111, 111, 91, 114, 97, 110, 100, 111, 109, 61, 10, 0, 32, 0, 37, 12, 0, 12, 0, 38, 1, 0, 21, 40, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 41, 86, 10, 0, 40, 0, 42, 7, 0, 41, 1, 0, 16, 106, 97, 118, 97, 47, 117, 116, 105, 108, 47, 82, 97, 110, 100, 111, 109, 12, 0, 43, 0, 26, 1, 0, 7, 110, 101, 120, 116, 73, 110, 116, 10, 0, 32, 0, 45, 12, 0, 46, 0, 47, 1, 0, 6, 97, 112, 112, 101, 110, 100, 1, 0, 27, 40, 73, 41, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 66, 117, 102, 102, 101, 114, 59, 8, 0, 49, 1, 0, 1, 93, 10, 0, 32, 0, 51, 12, 0, 46, 0, 52, 1, 0, 44, 40, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 41, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 66, 117, 102, 102, 101, 114, 59, 10, 0, 32, 0, 54, 12, 0, 27, 0, 28, 1, 0, 6, 114, 97, 110, 100, 111, 109, 1, 0, 18, 76, 106, 97, 118, 97, 47, 117, 116, 105, 108, 47, 82, 97, 110, 100, 111, 109, 59, 1, 0, 2, 115, 98, 1, 0, 24, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 66, 117, 102, 102, 101, 114, 59, 1, 0, 10, 83, 111, 117, 114, 99, 101, 70, 105, 108, 101, 1, 0, 8, 70, 111, 111, 46, 106, 97, 118, 97, 0, 33, 0, 1, 0, 3, 0, 1, 0, 5, 0, 1, 0, 26, 0, 7, 0, 8, 0, 1, 0, 9, 0, 0, 0, 2, 0, 10, 0, 4, 0, 1, 0, 12, 0, 13, 0, 1, 0, 14, 0, 0, 0, 47, 0, 1, 0, 1, 0, 0, 0, 5, 42, -73, 0, 15, -79, 0, 0, 0, 2, 0, 17, 0, 0, 0, 6, 0, 1, 0, 0, 0, 15, 0, 18, 0, 0, 0, 12, 0, 1, 0, 0, 0, 5, 0, 19, 0, 20, 0, 0, 0, 1, 0, 21, 0, 22, 0, 1, 0, 14, 0, 0, 0, 57, 0, 1, 0, 2, 0, 0, 0, 5, 43, -63, 0, 1, -84, 0, 0, 0, 2, 0, 17, 0, 0, 0, 6, 0, 1, 0, 0, 0, 22, 0, 18, 0, 0, 0, 22, 0, 2, 0, 0, 0, 5, 0, 19, 0, 20, 0, 0, 0, 0, 0, 5, 0, 23, 0, 24, 0, 1, 0, 1, 0, 25, 0, 26, 0, 1, 0, 14, 0, 0, 0, 44, 0, 1, 0, 1, 0, 0, 0, 2, 4, -84, 0, 0, 0, 2, 0, 17, 0, 0, 0, 6, 0, 1, 0, 0, 0, 27, 0, 18, 0, 0, 0, 12, 0, 1, 0, 0, 0, 2, 0, 19, 0, 20, 0, 0, 0, 1, 0, 27, 0, 28, 0, 1, 0, 14, 0, 0, 0, 111, 0, 3, 0, 3, 0, 0, 0, 37, -69, 0, 29, 89, -73, 0, 31, 76, -69, 0, 32, 89, 18, 34, -73, 0, 36, 77, 44, 43, -74, 0, 39, -74, 0, 44, 18, 48, -74, 0, 50, 87, 44, -74, 0, 53, -80, 0, 0, 0, 2, 0, 17, 0, 0, 0, 18, 0, 4, 0, 0, 0, 32, 0, 8, 0, 33, 0, 18, 0, 34, 0, 32, 0, 35, 0, 18, 0, 0, 0, 32, 0, 3, 0, 0, 0, 37, 0, 19, 0, 20, 0, 0, 0, 8, 0, 29, 0, 55, 0, 56, 0, 1, 0, 18, 0, 19, 0, 57, 0, 58, 0, 2, 0, 1, 0, 59, 0, 0, 0, 2, 0, 60,}; } /* public static void main(String[] args) throws Exception { InputStream is = FooClassLoader.class.getClassLoader().getResourceAsStream("org/jboss/cache/marshall/Foo.clazz"); byte[] bytes = new byte[1024]; ByteArrayOutputStream baos = new ByteArrayOutputStream(1024); int read; while ((read = is.read(bytes)) > -1) { baos.write(bytes, 0, read); } bytes = baos.toByteArray(); System.out.println("// GENERATED using main() method to read org/jboss/cache/marshall/Foo.clazz into a byte[]"); System.out.println("// so that this byte stream is available even if Foo.clazz is not included in the test classpath by Maven"); System.out.println("// Copy out this generated snippet into FooClassLoader.java and use this byte[] instead of "); System.out.println("// trying to read Foo.clazz off the classpath. "); System.out.println(); System.out.println("return new byte[] {"); int i=0; for (byte b : bytes) { i++; System.out.print(b); System.out.print(", "); if (i % 30 == 0) System.out.println(); } System.out.println("};"); }*/ } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/marshall/MyMap.java0000644000175000017500000000025310665031725026132 0ustar twernertwernerpackage org.jboss.cache.marshall; import java.util.HashMap; public class MyMap extends HashMap { private static final long serialVersionUID = -269748324316844637L; } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/marshall/ReturnValueMarshallingTest.java0000644000175000017500000001330711017455042032403 0ustar twernertwernerpackage org.jboss.cache.marshall; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.Region; import org.jboss.cache.buddyreplication.GravitateResult; import org.jboss.cache.commands.DataCommand; import org.jboss.cache.commands.read.GetKeyValueCommand; import org.jboss.cache.commands.read.GravitateDataCommand; import org.jboss.cache.commands.remote.ClusteredGetCommand; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.CommandsFactory; import org.jboss.cache.util.TestingUtil; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.List; /** * Tests the marshalling of retvals * * @author Manik Surtani * @since 2.0.0 */ @Test(groups = {"functional", "jgroups"}) public class ReturnValueMarshallingTest extends RegionBasedMarshallingTestBase { protected boolean useMarshalledValues = false; private CacheSPI cache1, cache2; private Fqn fqn = Fqn.fromString("/a"); private ClassLoader classLoader; private Object key = "key", value; private String className = "org.jboss.cache.marshall.MyList"; private Class listClass; private CommandsFactory commandsFactory = new CommandsFactory(); @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { cache1 = (CacheSPI) new DefaultCacheFactory().createCache(false); cache1.getConfiguration().setUseLazyDeserialization(useMarshalledValues); cache1.getConfiguration().setUseRegionBasedMarshalling(!useMarshalledValues); cache1.getConfiguration().setCacheMode(Configuration.CacheMode.REPL_SYNC); cache1.getConfiguration().setSyncReplTimeout(60000);// to aid with debugging cache1.start(); cache2 = (CacheSPI) new DefaultCacheFactory().createCache(false); cache2.getConfiguration().setUseLazyDeserialization(useMarshalledValues); cache2.getConfiguration().setUseRegionBasedMarshalling(!useMarshalledValues); cache2.getConfiguration().setCacheMode(Configuration.CacheMode.REPL_SYNC); cache2.getConfiguration().setSyncReplTimeout(60000);// to aid with debugging cache2.start(); classLoader = getClassLoader(); if (!useMarshalledValues) { Region r1 = cache1.getRegion(fqn, true); r1.setActive(true); r1.registerContextClassLoader(classLoader); Region r2 = cache2.getRegion(fqn, true); r2.setActive(true); r2.registerContextClassLoader(classLoader); } listClass = classLoader.loadClass(className); value = listClass.newInstance(); cache1.put(fqn, key, value); } @AfterMethod(alwaysRun = true) public void tearDown() { TestingUtil.killCaches(cache1, cache2); resetContextClassLoader(); } @Override protected ClassLoader getClassLoader() { String[] includesClasses = {className}; String[] excludesClasses = {}; ClassLoader cl = Thread.currentThread().getContextClassLoader(); return new SelectedClassnameClassLoader(includesClasses, excludesClasses, cl); } public void testClusteredGet() throws Exception { if (useMarshalledValues) Thread.currentThread().setContextClassLoader(classLoader); assertNotNull(cache1.get(fqn, key)); assertNotSame(MyList.class, cache1.get(fqn, key).getClass()); assertSame(listClass, cache1.get(fqn, key).getClass()); assertNotNull(cache2.get(fqn, key)); assertNotSame(MyList.class, cache2.get(fqn, key).getClass()); assertSame(listClass, cache2.get(fqn, key).getClass()); DataCommand command = new GetKeyValueCommand(fqn, key, false); ClusteredGetCommand clusteredGet = new ClusteredGetCommand(false, command); List responses = cache1.getRPCManager().callRemoteMethods(null, clusteredGet, true, 15000, false); List response1 = (List) responses.get(0);// response from the first (and only) node Boolean found = (Boolean) response1.get(0); assertTrue("Should have found remote data", found); Object data = response1.get(1); // now test that the data returned has been marshalled using the appropriate class loader. assertNotNull(data); if (useMarshalledValues) data = ((MarshalledValue) data).get(); assertNotSame(MyList.class, data.getClass()); assertSame(listClass, data.getClass()); } public void testDataGravitation() throws Exception { if (useMarshalledValues) Thread.currentThread().setContextClassLoader(classLoader); assertNotNull(cache1.get(fqn, key)); assertNotSame(MyList.class, cache1.get(fqn, key).getClass()); assertSame(listClass, cache1.get(fqn, key).getClass()); assertNotNull(cache2.get(fqn, key)); assertNotSame(MyList.class, cache2.get(fqn, key).getClass()); assertSame(listClass, cache2.get(fqn, key).getClass()); GravitateDataCommand gravitateDataCommand = new GravitateDataCommand(fqn, false, cache1.getRPCManager().getLocalAddress()); List responses = cache1.getRPCManager().callRemoteMethods(null, gravitateDataCommand, true, 15000, false); GravitateResult data = (GravitateResult) responses.get(0);// response from the first (and only) node assertTrue("Should have found remote data", data.isDataFound()); assertNotNull(data.getNodeData()); Object value = data.getNodeData().get(0).getAttributes().get(key); assertNotNull(value); if (useMarshalledValues) value = ((MarshalledValue) value).get(); assertNotSame(MyList.class, value.getClass()); assertSame(listClass, value.getClass()); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/marshall/VersionAwareMarshallerTest.java0000644000175000017500000000736511011035077032370 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.marshall; import org.jboss.cache.Version; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.ComponentRegistry; import org.jboss.cache.invocation.CacheInvocationDelegate; import org.jboss.util.stream.MarshalledValueInputStream; import static org.testng.AssertJUnit.assertEquals; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.io.ByteArrayInputStream; import java.io.ObjectInputStream; /** * Tests the enhanced treecache marshaller * * @author Manik Surtani (manik@jboss.org) */ @Test(groups = {"functional"}) public class VersionAwareMarshallerTest extends AbstractVersionAwareMarshallerTest { @BeforeMethod public void setUp() { cr = new ComponentRegistry(new Configuration(), new CacheInvocationDelegate()); } @AfterMethod public void tearDown() { cr = null; } public void testMarshallerSelection() { VersionAwareMarshaller marshaller = createVAMandRestartCache("2.2.0.GA"); assertEquals(CacheMarshaller210.class, marshaller.defaultMarshaller.getClass()); marshaller = createVAMandRestartCache("2.1.0.GA"); assertEquals(CacheMarshaller210.class, marshaller.defaultMarshaller.getClass()); marshaller = createVAMandRestartCache("2.0.0.GA"); assertEquals(CacheMarshaller200.class, marshaller.defaultMarshaller.getClass()); marshaller = createVAMandRestartCache("1.4.0.GA"); assertEquals(CacheMarshaller210.class, marshaller.defaultMarshaller.getClass()); marshaller = createVAMandRestartCache("1.5.0.GA"); assertEquals(CacheMarshaller210.class, marshaller.defaultMarshaller.getClass()); marshaller = createVAMandRestartCache("1.3.0.GA"); assertEquals(CacheMarshaller210.class, marshaller.defaultMarshaller.getClass()); marshaller = createVAMandRestartCache("1.3.0.SP2"); assertEquals(CacheMarshaller210.class, marshaller.defaultMarshaller.getClass()); marshaller = createVAMandRestartCache("1.3.1.GA"); assertEquals(CacheMarshaller210.class, marshaller.defaultMarshaller.getClass()); marshaller = createVAMandRestartCache("1.2.4.SP2"); assertEquals(CacheMarshaller210.class, marshaller.defaultMarshaller.getClass()); marshaller = createVAMandRestartCache("1.2.3"); assertEquals(CacheMarshaller210.class, marshaller.defaultMarshaller.getClass()); } public void testVersionHeaderDefaultCurrent() throws Exception { VersionAwareMarshaller marshaller = createVAMandRestartCache(Version.getVersionString(Version.getVersionShort())); byte[] bytes = marshaller.objectToByteBuffer("Hello"); ObjectInputStream in = new MarshalledValueInputStream(new ByteArrayInputStream(bytes)); assertEquals("Version header short should be '22'", 22, in.readShort()); } public void testVersionHeader210() throws Exception { VersionAwareMarshaller marshaller = createVAMandRestartCache("2.1.0.GA"); byte[] bytes = marshaller.objectToByteBuffer("Hello"); ObjectInputStream in = new MarshalledValueInputStream(new ByteArrayInputStream(bytes)); assertEquals("Version header short should be '21'", 21, in.readShort()); } public void testVersionHeader200() throws Exception { VersionAwareMarshaller marshaller = createVAMandRestartCache("2.0.0.GA"); byte[] bytes = marshaller.objectToByteBuffer("Hello"); ObjectInputStream in = new MarshalledValueInputStream(new ByteArrayInputStream(bytes)); assertEquals("Version header short should be '20'", 20, in.readShort()); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/marshall/Foo.clazz0000644000175000017500000000212510535375655026045 0ustar twernertwernerÊþº¾.=org/jboss/cache/marshall/Foojava/lang/Objectjava/io/SerializableserialVersionUIDJ ConstantValue()VCode  LineNumberTableLocalVariableTablethisLorg/jboss/cache/marshall/Foo;equals(Ljava/lang/Object;)ZobjLjava/lang/Object;hashCode()ItoString()Ljava/lang/String;java/security/SecureRandom !java/lang/StringBuffer#$org.jboss.cache.marshall.Foo[random= % &(Ljava/lang/String;)V (*)java/util/Random +nextInt - ./append(I)Ljava/lang/StringBuffer;1] 3 .4,(Ljava/lang/String;)Ljava/lang/StringBuffer; 6 randomLjava/util/Random;sbLjava/lang/StringBuffer; SourceFileFoo.java!   /*·± 9+Á¬,¬ o%»Y·L» Y"·$M,+¶'¶,0¶2W,¶5° !" # %789:;<jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/marshall/CacheLoaderMarshallingJDBCTest.java0000644000175000017500000001146611017455042032710 0ustar twernertwernerpackage org.jboss.cache.marshall; import org.jboss.cache.Cache; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.Region; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.EvictionConfig; import org.jboss.cache.config.EvictionRegionConfig; import org.jboss.cache.eviction.LRUConfiguration; import org.jboss.cache.eviction.LRUPolicy; import org.jboss.cache.loader.JDBCCacheLoaderConfig; import static org.testng.AssertJUnit.assertEquals; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Properties; /** * Tests marshalling/unmarshalling during cache loader operations involving types * not visible to the cache's default classloader. * * @author Brian Stansberry * @since 2.1.0 */ @Test(groups = "functional") public class CacheLoaderMarshallingJDBCTest extends RegionBasedMarshallingTestBase { private static final String className = "org.jboss.cache.marshall.MyUUID"; private Cache cache; private Fqn fqn = Fqn.fromString("/a"); @BeforeMethod(alwaysRun = true) protected void setUp() throws Exception { originalClassLoader = Thread.currentThread().getContextClassLoader(); } @AfterMethod(alwaysRun = true) protected void tearDown() { resetContextClassLoader(); TestingUtil.killCaches(cache); } @Override protected ClassLoader getClassLoader() { String[] includesClasses = {className}; String[] excludesClasses = {}; ClassLoader cl = Thread.currentThread().getContextClassLoader(); return new SelectedClassnameClassLoader(includesClasses, excludesClasses, cl); } public void testCacheLoaderMarshalling() throws Exception { cacheLoaderMarshallingTest(false); } public void testCacheLoaderRegionBasedMarshalling() throws Exception { cacheLoaderMarshallingTest(true); } private void cacheLoaderMarshallingTest(boolean useRegionBased) throws Exception { cache = createCache(useRegionBased); cache.start(); FooClassLoader loader = new FooClassLoader(originalClassLoader); if (useRegionBased) { Region r = cache.getRegion(Fqn.ROOT, true); r.registerContextClassLoader(loader); r.activate(); } Class clazz = loader.loadFoo(); Object obj = clazz.newInstance(); Thread.currentThread().setContextClassLoader(loader); cache.put(fqn, "key", obj); this.resetContextClassLoader(); cache.evict(fqn); Thread.currentThread().setContextClassLoader(loader); assertEquals(obj, cache.get(fqn, "key")); } private Cache createCache(boolean useRegionBased) throws Exception { Properties prop = getProperties(); // ensure cleanup after each test prop.setProperty("cache.jdbc.table.drop", "true"); Cache cache = new DefaultCacheFactory().createCache(false); Configuration config = cache.getConfiguration(); config.setUseRegionBasedMarshalling(useRegionBased); config.setInactiveOnStartup(useRegionBased); EvictionConfig ec = new EvictionConfig(); ec.setDefaultEvictionPolicyClass(LRUPolicy.class.getName()); ec.setWakeupIntervalSeconds(1000); // a long time; really disabled EvictionRegionConfig erc = new EvictionRegionConfig(); erc.setRegionFqn(Fqn.ROOT); erc.setRegionName("_default_"); LRUConfiguration epc = new LRUConfiguration(); epc.setMaxNodes(1000); epc.setTimeToLiveSeconds(1000); erc.setEvictionPolicyConfig(epc); List ercs = new ArrayList(); ercs.add(erc); ec.setEvictionRegionConfigs(ercs); config.setEvictionConfig(ec); CacheLoaderConfig clc = new CacheLoaderConfig(); clc.setPassivation(true); clc.setShared(false); JDBCCacheLoaderConfig jdbc_clc = new JDBCCacheLoaderConfig(); jdbc_clc.setProperties(prop); clc.setIndividualCacheLoaderConfigs(Collections.singletonList(jdbc_clc)); config.setCacheLoaderConfig(clc); return cache; } private Properties getProperties() throws Exception { Properties properties = new Properties(); try { properties.load(this.getClass().getClassLoader().getResourceAsStream("cache-jdbc.properties")); return properties; } catch (Exception e) { throw new Exception("Error loading jdbc properties ", e); } } }jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/marshall/UnmarshalledReferencesTest.java0000644000175000017500000000304210763637237032400 0ustar twernertwernerpackage org.jboss.cache.marshall; import org.testng.annotations.Test; import static org.testng.Assert.*; import static org.testng.Assert.assertEquals; /** * Tester for UnmarshalledReferences. * * @author Mircea.Markus@jboss.com */ @Test(groups = {"functional"}) public class UnmarshalledReferencesTest { public void testSimpleGetPut() { UnmarshalledReferences refs = new UnmarshalledReferences(); for (int i = 0; i < 100; i++) { refs.putReferencedObject(i, String.valueOf(i)); } for (int i = 0; i < 100; i++) { assertEquals(refs.getReferencedObject(i), String.valueOf(i)); } } public void testPutWithGap() { UnmarshalledReferences refs = new UnmarshalledReferences(); refs.putReferencedObject(0, "0"); refs.putReferencedObject(2, "2"); assertEquals(refs.getReferencedObject(0), "0"); assertNull(refs.getReferencedObject(1)); assertEquals(refs.getReferencedObject(2), "2"); } public void testPutBefore() { UnmarshalledReferences refs = new UnmarshalledReferences(); refs.putReferencedObject(2, "2"); refs.putReferencedObject(3, "3"); //when adding this make sure other positions are not shifted refs.putReferencedObject(1, "1"); assertNull(refs.getReferencedObject(0)); assertEquals("1", refs.getReferencedObject(1)); assertEquals("2", refs.getReferencedObject(2)); assertEquals("3", refs.getReferencedObject(3)); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/marshall/SelectedClassnameClassLoader.java0000644000175000017500000001717410745240616032615 0ustar twernertwernerpackage org.jboss.cache.marshall; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import java.io.ByteArrayOutputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.util.Map; /** * A ClassLoader that loads classes whose classname begins with one of a * given set of strings, without attempting first to delegate to its * parent loader. *

* This class is intended to allow emulation of 2 different types of common J2EE * classloading situations. *

    *
  • Servlet-style child-first classloading, where this class is the * child loader.
  • *
  • Parent-first classloading where the parent does not have access to * certain classes
  • *
*

*

* This class can also be configured to raise a ClassNotFoundException if * asked to load certain classes, thus allowing classes on the classpath * to be hidden from a test environment. *

* * @author Brian Stansberry */ public class SelectedClassnameClassLoader extends ClassLoader { private String[] includedClasses = null; private String[] excludedClasses = null; private String[] notFoundClasses = null; private Log log = LogFactory.getLog(SelectedClassnameClassLoader.class); private Map classes = new java.util.HashMap(); /** * Creates a new classloader that loads the given classes. * * @param includedClasses array of class or package names that should be * directly loaded by this loader. Classes * whose name starts with any of the strings * in this array will be loaded by this class, * unless their name appears in * excludedClasses. * Can be null * @param excludedClasses array of class or package names that should NOT * be directly loaded by this loader. Loading of * classes whose name starts with any of the * strings in this array will be delegated to * parent, even if the classes * package or classname appears in * includedClasses. Typically this * parameter is used to exclude loading one or * more classes in a package whose other classes * are loaded by this object. * @param parent ClassLoader to which loading of classes should * be delegated if necessary */ public SelectedClassnameClassLoader(String[] includedClasses, String[] excludedClasses, ClassLoader parent) { super(parent); this.includedClasses = includedClasses; this.excludedClasses = excludedClasses; } /** * Creates a new classloader that loads the given classes. * * @param includedClasses array of class or package names that should be * directly loaded by this loader. Classes * whose name starts with any of the strings * in this array will be loaded by this class, * unless their name appears in * excludedClasses. * Can be null * @param excludedClasses array of class or package names that should NOT * be directly loaded by this loader. Loading of * classes whose name starts with any of the * strings in this array will be delegated to * parent, even if the classes * package or classname appears in * includedClasses. Typically this * parameter is used to exclude loading one or * more classes in a package whose other classes * are loaded by this object. * @param notFoundClasses array of class or package names for which this * should raise a ClassNotFoundException * @param parent ClassLoader to which loading of classes should * be delegated if necessary */ public SelectedClassnameClassLoader(String[] includedClasses, String[] excludedClasses, String[] notFoundClasses, ClassLoader parent) { super(parent); this.includedClasses = includedClasses; this.excludedClasses = excludedClasses; this.notFoundClasses = notFoundClasses; } protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException { log.info("In SelectedClassnameClassLoader.loadClass(" + name + "," + resolve + ")"); if (isIncluded(name) && (isExcluded(name) == false)) { Class c = findClass(name); if (resolve) { resolveClass(c); } return c; } else { return super.loadClass(name, resolve); } } protected Class findClass(String name) throws ClassNotFoundException { log.info("In SelectedClassnameClassLoader.findClass()"); Class result = classes.get(name); if (result != null) { return result; } if (isIncluded(name) && (isExcluded(name) == false)) { try { InputStream is = getResourceAsStream(name.replace('.', '/').concat(".class")); byte[] bytes = new byte[1024]; ByteArrayOutputStream baos = new ByteArrayOutputStream(1024); int read; while ((read = is.read(bytes)) > -1) { baos.write(bytes, 0, read); } bytes = baos.toByteArray(); result = this.defineClass(name, bytes, 0, bytes.length); } catch (FileNotFoundException e) { throw new ClassNotFoundException("cannot find " + name, e); } catch (IOException e) { throw new ClassNotFoundException("cannot read " + name, e); } } else if (isNotFound(name)) { throw new ClassNotFoundException(name + " is discarded"); } else { result = super.findClass(name); } classes.put(name, result); return result; } private boolean isIncluded(String className) { if (includedClasses != null) { for (int i = 0; i < includedClasses.length; i++) { if (className.startsWith(includedClasses[i])) { return true; } } } return false; } private boolean isExcluded(String className) { if (excludedClasses != null) { for (int i = 0; i < excludedClasses.length; i++) { if (className.startsWith(excludedClasses[i])) { return true; } } } return false; } private boolean isNotFound(String className) { if (notFoundClasses != null) { for (int i = 0; i < notFoundClasses.length; i++) { if (className.startsWith(notFoundClasses[i])) { return true; } } } return false; } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/marshall/MethodIdPreservationTest.java0000644000175000017500000000615011015045001032026 0ustar twernertwernerpackage org.jboss.cache.marshall; import org.jboss.cache.Fqn; import org.jboss.cache.commands.ReversibleCommand; import org.jboss.cache.commands.tx.PrepareCommand; import org.jboss.cache.commands.write.PutDataMapCommand; import org.jboss.cache.factories.CommandsFactory; import static org.testng.AssertJUnit.assertEquals; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.ArrayList; import java.util.List; /** * @author Manik Surtani * @since 2.0.0 */ @Test(groups = {"functional"}) public class MethodIdPreservationTest { private Marshaller m; private ObjectOutputStream stream; private ByteArrayOutputStream byteStream; private ReversibleCommand command1; private List list = new ArrayList(2); private PrepareCommand prepareComand; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { byteStream = new ByteArrayOutputStream(); stream = new ObjectOutputStream(byteStream); command1 = new PutDataMapCommand(null, Fqn.ROOT, null); list.clear(); list.add(command1); list.add(new PutDataMapCommand(null, Fqn.ROOT, null)); prepareComand = new PrepareCommand(null, list, null, true); CacheMarshaller210 cm210 = new CacheMarshaller210(); CommandsFactory factory = new CommandsFactory(); cm210.injectCommandsFactory(factory); m = cm210; } public void testSingleMethodCall() throws Exception { m.objectToObjectStream(command1, stream); stream.close(); ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(byteStream.toByteArray())); Object result = m.objectFromObjectStream(in); assertEquals(command1.getClass(), result.getClass()); } public void testListOfMethodCalls() throws Exception { m.objectToObjectStream(list, stream); stream.close(); ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(byteStream.toByteArray())); Object result = m.objectFromObjectStream(in); assertEquals(list.getClass(), result.getClass()); assertEquals(list.size(), ((List) result).size()); assert ((List) result).get(0) instanceof PutDataMapCommand; assert ((List) result).get(1) instanceof PutDataMapCommand; } public void testMethodCallsInPrepare() throws Exception { m.objectToObjectStream(prepareComand, stream); stream.close(); ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(byteStream.toByteArray())); Object result = m.objectFromObjectStream(in); assertEquals(prepareComand.getClass(), result.getClass()); PrepareCommand prepareCallRes = (PrepareCommand) result; List listResult = prepareCallRes.getModifications(); assertEquals(list.size(), listResult.size()); assert listResult.get(0) instanceof PutDataMapCommand; assert listResult.get(1) instanceof PutDataMapCommand; } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/marshall/MyList.java0000644000175000017500000000025710665031725026334 0ustar twernertwernerpackage org.jboss.cache.marshall; import java.util.ArrayList; public class MyList extends ArrayList { private static final long serialVersionUID = 773098444170102370L; } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/marshall/CacheMarshaller200Test.java0000644000175000017500000002304311066201252031177 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.marshall; import org.jboss.cache.Fqn; import org.jboss.cache.Region; import org.jboss.cache.RegionManager; import org.jboss.cache.commands.remote.ClusteredGetCommand; import org.testng.annotations.Test; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; @Test(groups = {"functional"}) public class CacheMarshaller200Test extends CacheMarshallerTestBase { public CacheMarshaller200Test() { currentVersion = "2.0.0.GA"; currentVersionShort = 20; expectedMarshallerClass = CacheMarshaller200.class; } public void testBadEquals() throws Exception { // object1 and Object2 should NOT be equal, even though their equals() methods are broken. Broken o1 = new Broken(); Broken o2 = new Broken(); o1.name = "o1"; o2.name = "o2"; assert o1 != o2; assert o1.equals(o2); List l = new ArrayList(2); // lists will allow "duplicate" entries. l.add(o1); l.add(o2); CacheMarshaller200 cm200 = new CacheMarshaller200(); ByteArrayOutputStream bout = new ByteArrayOutputStream(); ObjectOutputStream out = new ObjectOutputStream(bout); cm200.objectToObjectStream(l, out); out.close(); ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bout.toByteArray())); List l2 = (List) cm200.objectFromObjectStream(in); assert l2.size() == 2; assert l2.get(0).name.equals("o1"); assert l2.get(1).name.equals("o2"); assert l2.get(0) != l2.get(1); assert l2.get(0).equals(l2.get(1)); } public void testRegionalisedStream() throws Exception { // need to test what's going on with CacheMarshaller200 cm200 = new CacheMarshaller200(); c.setUseRegionBasedMarshalling(true); cm200.injectDependencies(new RegionManager(), c, getClass().getClassLoader()); cm200.init(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); cm200.objectToObjectStream("Hello World", oos, Fqn.fromString("/hello")); oos.close(); ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray())); // test that the first thing on the stream is the fqn! byte magic = ois.readByte(); short ref = ois.readShort(); System.out.println("Magic number is " + magic); assert magic == CacheMarshaller200.MAGICNUMBER_FQN; // now the chunks of an Fqn Fqn f = cm200.unmarshallFqn(ois, new UnmarshalledReferences()); assert f.equals(Fqn.fromString("/hello")); } /** * This test emulates the behaviour observed when an incoming method call is unmarshalled (using objectFromOutputStream), * region based marshalling is used, and it is expected that we would need to marshall the response using the same * region as well. To deal with this, the region (as an Fqn) is read off the stream when unmarshalling and then * stored in a ThreadLocal so it can be accessed for when the response needs to be marshalled again. *

* The problem here - registered as JBCACHE-1170 - is that this has a tendency to leak scope and affect more than the * required method call. */ public void testLeakageOfFqn() throws Throwable { // Use a thread pool so that we know threads will be reused. // You don't need any concurrency here to demonstrate the failure - a single thread is enough. ExecutorService e = Executors.newFixedThreadPool(1); // to capture throwables final List throwables = new CopyOnWriteArrayList(); // each thread will perform 1 of 3 tasks: // 1. unmarshall a stream, and marshall a response - typical case such as a clustered get // 2. unmarshall a stream, and marshall a 'void' response. // 3. marshall a (primitive response) - case such as a state transfer sending out boolean status // first create a stream to unmarshall. // RegionManager rm = new RegionManager(); final CacheMarshaller200 cm200 = new CacheMarshaller200(); c.setInactiveOnStartup(false); c.setUseRegionBasedMarshalling(true); c.setCacheMarshaller(cm200); cr.registerComponent(cm200, CacheMarshaller200.class); cr.rewire(); cr.start(); RegionManager rm = cr.getComponent(RegionManager.class); ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); final Fqn region = Fqn.fromString("/hello"); Region r = rm.getRegion(region, true); r.registerContextClassLoader(this.getClass().getClassLoader()); cm200.objectToObjectStream(new ClusteredGetCommand(false, null), oos, region); oos.close(); final byte[] stream = baos.toByteArray(); // so now the stream starts with the Fqn "/hello". // repeat 100 times for (int i = 0; i < 100; i++) { if (i % 3 == 0) { // task 1 above e.execute(new Runnable() { public void run() { try { RegionalizedMethodCall rmc = cm200.regionalizedMethodCallFromObjectStream(new ObjectInputStream(new ByteArrayInputStream(stream))); ByteArrayOutputStream out = new ByteArrayOutputStream(); ObjectOutputStream outStream = new ObjectOutputStream(out); RegionalizedReturnValue rrv = new RegionalizedReturnValue("A result", rmc); cm200.objectToObjectStream(rrv, outStream); outStream.close(); out.close(); // test that the output stream has got "/hello" as it's region Fqn. ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(out.toByteArray())); assert in.readByte() == CacheMarshaller200.MAGICNUMBER_FQN : "The stream should start with an Fqn"; // discard the nest refId short in.readShort(); Fqn f = cm200.unmarshallFqn(in, new UnmarshalledReferences()); assert region.equals(f) : "Should use the same region for the response as was used for the request!"; } catch (Throwable t) { throwables.add(t); } } }); } else if (i % 3 == 1) { // task 2 above e.execute(new Runnable() { public void run() { try { cm200.objectFromObjectStream(new ObjectInputStream(new ByteArrayInputStream(stream))); // and now just send back a 'void' return type (In JGroups this is treated as a null) cm200.objectToObjectStream(null, new ObjectOutputStream(new ByteArrayOutputStream())); } catch (Throwable t) { throwables.add(t); } } }); } else if (i % 3 == 2) { // task 3 above e.execute(new Runnable() { public void run() { try { // and now don't bother with any umarshalling // directly marshall a boolean. ByteArrayOutputStream out = new ByteArrayOutputStream(); ObjectOutputStream outStream = new ObjectOutputStream(out); cm200.objectToObjectStream(true, outStream); outStream.close(); out.close(); // test that the output stream has got "/hello" as it's region Fqn. ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(out.toByteArray())); byte magic = in.readByte(); assert magic != CacheMarshaller200.MAGICNUMBER_FQN : "The stream should NOT start with an Fqn!"; assert magic == CacheMarshaller200.MAGICNUMBER_NULL : "Should start with a NULL. Instead, was " + magic; assert in.readByte() == CacheMarshaller200.MAGICNUMBER_BOOLEAN : "Should have a boolean magic number before the boolean value"; assert in.readBoolean() : "The boolean written to the stream should be true"; } catch (Throwable t) { throwables.add(t); } } }); } } e.shutdown(); e.awaitTermination(60, TimeUnit.SECONDS); for (Throwable t : throwables) { t.printStackTrace(); } assert throwables.size() == 0 : "Should not have caught any exceptions!"; } } class Broken implements Serializable { String name; @Override public boolean equals(Object o) { return true; } @Override public int hashCode() { return 10; } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/FqnTest.java0000644000175000017500000003104411021223332024652 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache; import org.jboss.cache.config.Configuration; import org.jboss.cache.marshall.CacheMarshaller210; import org.jboss.cache.marshall.Marshaller; import org.testng.annotations.AfterTest; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.Arrays; import java.util.HashMap; /** * Tests {@link Fqn}. * * @author Bela Ban May 9, 2003 * @version $Revision: 5938 $ */ @Test(groups = "unit") public class FqnTest { private Cache cache; private Marshaller marshaller; @BeforeTest protected void setUp() { cache = null; marshaller = new CacheMarshaller210(); } @AfterTest protected void tearDown() { if (cache != null) { cache.stop(); cache = null; } } public void testNull() { Fqn fqn = Fqn.ROOT; log("null fqn is " + fqn); assert 0 == fqn.size(); int hcode = fqn.hashCode(); assert hcode != -1; } public void testOne() { Fqn fqn = Fqn.fromElements(22); log("one fqn is " + fqn); assert 1 == fqn.size(); int hcode = fqn.hashCode(); assert hcode != -1; } public void testEmptyFqn() { Fqn f1 = Fqn.ROOT; Fqn f2 = Fqn.ROOT; assert f1.equals(f2); } public void testFqn() { Fqn fqn = Fqn.fromString("/a/b/c"); log("fqn is " + fqn); assert 3 == fqn.size(); Fqn fqn2 = Fqn.fromElements("a", "b", "c"); log("fqn2 is " + fqn2); assert 3 == fqn.size(); assert fqn.equals(fqn2); assert fqn.hashCode() == fqn2.hashCode(); } public void testHereogeneousNames() { Fqn fqn = Fqn.fromElements("string", 38, true); log("fqn is " + fqn); assert 3 == fqn.size(); Fqn fqn2 = Fqn.fromElements("string", 38, true); assert fqn.equals(fqn2); assert fqn.hashCode() == fqn2.hashCode(); } public void testHashcode() { Fqn fqn1, fqn2; fqn1 = Fqn.fromElements("a", "b", "c"); fqn2 = Fqn.fromString("/a/b/c"); log("fqn is " + fqn1); assert fqn1.equals(fqn2); HashMap map = new HashMap(); map.put(fqn1, 33); map.put(fqn2, 34); assert map.size() == 1; assert map.get(fqn1).equals(34); } public void testHashcode2() { Fqn fqn = Fqn.fromElements(-1); log("one fqn is " + fqn); assert fqn.size() == 1; int hcode = fqn.hashCode(); assert hcode == -1; } public void testEquals() { Fqn fqn1 = Fqn.fromElements("person/test"); Fqn f1, f2, f3; f1 = Fqn.fromRelativeElements(fqn1, "0"); f2 = Fqn.fromRelativeElements(fqn1, "1"); f3 = Fqn.fromRelativeElements(fqn1, "2"); HashMap map = new HashMap(); map.put(f1, "0"); map.put(f2, "1"); map.put(f3, "2"); assert map.get(Fqn.fromRelativeElements(fqn1, "0")) != null; assert map.get(Fqn.fromRelativeElements(fqn1, "1")) != null; assert map.get(Fqn.fromRelativeElements(fqn1, "2")) != null; } public void testEquals2() { Fqn f1; Fqn f2; f1 = Fqn.fromString("/a/b/c"); f2 = Fqn.fromString("/a/b/c"); assert f1.equals(f2); f2 = Fqn.fromString("/a/b"); assert !f1.equals(f2); f2 = Fqn.fromString("/a/b/c/d"); assert !f1.equals(f2); } public void testEquals2WithMarshalling() throws Exception { Fqn f1, f2; f1 = Fqn.fromString("/a/b/c"); f2 = marshalAndUnmarshal(f1); assert f1.equals(f2); } public void testEquals3() { Fqn f1; Fqn f2; f1 = Fqn.fromElements("a", 322649, Boolean.TRUE); f2 = Fqn.ROOT; assert !f1.equals(f2); assert !f2.equals(f1); f2 = Fqn.fromString("a/322649/TRUE"); assert !f1.equals(f2); f2 = Fqn.fromElements("a", 322649, Boolean.FALSE); assert !f1.equals(f2); f2 = Fqn.fromElements("a", 322649, Boolean.TRUE); assert f1.equals(f2); } public void testEquals3WithMarshalling() throws Exception { Fqn f1, f2; f1 = Fqn.fromElements("a", 322649, Boolean.TRUE); f2 = marshalAndUnmarshal(f1); assert f1.equals(f2); assert f2.equals(f1); Fqn f3 = Fqn.fromString("a/322649/TRUE"); f3 = marshalAndUnmarshal(f3); assert !f1.equals(f3); f2 = Fqn.fromElements("a", 322649, Boolean.FALSE); f2 = marshalAndUnmarshal(f2); assert !f1.equals(f2); f2 = Fqn.fromElements("a", 322649, Boolean.TRUE); f2 = marshalAndUnmarshal(f2); assert f1.equals(f2); } public void testEquals4() { Fqn fqn = Fqn.fromString("X"); // Check casting assert !fqn.equals("X"); // Check null assert !fqn.equals(null); } public void testClone() throws CloneNotSupportedException { Fqn fqn1 = Fqn.fromString("/a/b/c"); Fqn fqn2 = fqn1.clone(); assert fqn1.equals(fqn2); assert fqn1.hashCode() == fqn2.hashCode(); } public void testNullElements() throws CloneNotSupportedException { Fqn fqn0 = Fqn.fromElements((Object) null); assert 1 == fqn0.size(); Fqn fqn1 = Fqn.fromElements("NULL", null, 0); assert 3 == fqn1.size(); Fqn fqn2 = Fqn.fromElements("NULL", null, 0); assert fqn1.hashCode() == fqn2.hashCode(); assert fqn1.equals(fqn2); assert fqn1.equals(fqn1.clone()); } public void testIteration() { Fqn fqn = Fqn.fromString("/a/b/c"); assert 3 == fqn.size(); Fqn tmp_fqn = Fqn.ROOT; assert 0 == tmp_fqn.size(); for (int i = 0; i < fqn.size(); i++) { String s = fqn.get(i); tmp_fqn = Fqn.fromRelativeElements(tmp_fqn, s); assert tmp_fqn.size() == i + 1; } assert 3 == tmp_fqn.size(); assert fqn.equals(tmp_fqn); } public void testIsChildOf() { Fqn child = Fqn.fromString("/a/b"); Fqn parent = Fqn.fromString("/a"); assert child.isChildOf(parent); assert !parent.isChildOf(child); assert child.isChildOrEquals(child); parent = Fqn.fromString("/a/b/c"); child = Fqn.fromString("/a/b/c/d/e/f/g/h/e/r/e/r/t/tt/"); assert child.isChildOf(parent); } public void testIsChildOf2() { Fqn child = Fqn.fromString("/a/b/c/d"); assert "/b/c/d".equals(child.getSubFqn(1, child.size()).toString()); } public void testParentage() { Fqn fqnRoot = Fqn.ROOT; Fqn parent = fqnRoot.getParent(); assert parent.equals(fqnRoot); Fqn fqnOne = Fqn.fromString("/one"); parent = fqnOne.getParent(); assert parent.equals(fqnRoot); assert fqnOne.isChildOf(parent); Fqn fqnTwo = Fqn.fromString("/one/two"); parent = fqnTwo.getParent(); assert parent.equals(fqnOne); assert fqnTwo.isChildOf(parent); Fqn fqnThree = Fqn.fromString("/one/two/three"); parent = fqnThree.getParent(); assert parent.equals(fqnTwo); assert fqnThree.isChildOf(parent); } public void testRoot() { Fqn fqn = Fqn.ROOT; assert fqn.isRoot(); fqn = Fqn.fromString("/one/two"); assert !fqn.isRoot(); Fqn f = Fqn.fromString("/"); assert f.isRoot(); assert f.equals(Fqn.ROOT); } public void testGetName() { Fqn integerFqn = Fqn.fromElements(1); assert "1".equals(integerFqn.getLastElementAsString()); Object object = new Object(); Fqn objectFqn = Fqn.fromElements(object); assert object.toString().equals(objectFqn.getLastElementAsString()); } public void testCloningString() throws CloneNotSupportedException { Fqn f = Fqn.fromString("/a/b/c"); assert f.equals(f.clone()); } public void testCloningOtherTypes() throws CloneNotSupportedException { Fqn f = Fqn.fromElements("blah", 10, Boolean.TRUE); assert f.equals(f.clone()); } public void testRemovalNonString() throws Exception { Fqn f = Fqn.fromElements("test", 1); Configuration c = new Configuration(); c.setCacheMode("LOCAL"); cache = new DefaultCacheFactory().createCache(c); cache.put(f, "key", "value"); assert "value".equals(cache.get(f, "key")); assert cache.getRoot().hasChild(f); cache.removeNode(f); assert cache.get(f, "key") == null; assert !cache.getRoot().hasChild(f); } @SuppressWarnings("unchecked") Fqn marshalAndUnmarshal(Fqn fqn) throws Exception { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream out = new ObjectOutputStream(baos); marshaller.objectToObjectStream(fqn, out); out.close(); baos.close(); ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray())); return (Fqn) marshaller.objectFromObjectStream(in); } // testing generics public void testGenerics() { Fqn f = Fqn.fromString("/blah/blah"); Fqn f2 = Fqn.fromString("/blah/blah"); assert f.equals(f2); Fqn f3 = Fqn.fromElements(1, 2, 3, 5); Fqn f4 = Fqn.fromElements(1, 2, 3); assert f3.getParent().equals(f4); } public void testSize() { Fqn f = Fqn.ROOT; assert f.size() == 0; assert f.isRoot(); f = Fqn.fromString("/"); assert f.size() == 0; assert f.isRoot(); f = Fqn.fromString("/hello"); assert f.size() == 1; assert !f.isRoot(); } // public void testIntermediateFqns() // { // Fqn lastCreated = Fqn.fromString("/a/b"); // Fqn target = Fqn.fromString("/a/b/c/d/e/f"); // // List intermediates = new ArrayList(); // intermediates.add(Fqn.fromString("/a/b/c")); // intermediates.add(Fqn.fromString("/a/b/c/d")); // intermediates.add(Fqn.fromString("/a/b/c/d/e")); // // System.out.println("Expecting " + intermediates); // System.out.println("Was: " + target.getIntermediateFqns(lastCreated)); // // assert target.getIntermediateFqns(lastCreated).equals(intermediates); // } // public void testGenerations() { Fqn f = Fqn.fromElements(1, 2, 3, 4, 5, 6, 7); assert f.equals(f.getAncestor(f.size())); assert f.getParent().equals(f.getAncestor(f.size() - 1)); assert Fqn.ROOT.equals(f.getAncestor(0)); assert Fqn.fromElements(1).equals(f.getAncestor(1)); assert Fqn.fromElements(1, 2).equals(f.getAncestor(2)); assert Fqn.fromElements(1, 2, 3).equals(f.getAncestor(3)); assert Fqn.fromElements(1, 2, 3, 4).equals(f.getAncestor(4)); assert Fqn.fromElements(1, 2, 3, 4, 5).equals(f.getAncestor(5)); try { f.getAncestor(-1); // should fail assert false; } catch (IllegalArgumentException good) { // expected } try { f.getAncestor(f.size() + 1); // should fail assert false; } catch (IndexOutOfBoundsException good) { // expected } } void log(String msg) { System.out.println("-- " + msg); } public void testDifferentFactories() { Fqn[] fqns = new Fqn[7]; int i = 0; fqns[i++] = Fqn.fromString("/a/b/c"); fqns[i++] = Fqn.fromRelativeElements(Fqn.ROOT, "a", "b", "c"); fqns[i++] = Fqn.fromElements("a", "b", "c"); fqns[i++] = Fqn.fromList(Arrays.asList(new String[]{"a", "b", "c"})); fqns[i++] = Fqn.fromRelativeList(Fqn.ROOT, Arrays.asList(new String[]{"a", "b", "c"})); fqns[i++] = Fqn.fromRelativeFqn(Fqn.ROOT, Fqn.fromString("/a/b/c")); fqns[i] = new Fqn("a", "b", "c"); // "old-style" Fqn // all of the above should be equal to each other. for (i = 0; i < fqns.length; i++) { for (int j = 0; j < fqns.length; j++) { assert fqns[i].equals(fqns[j]) : "Error on equals comparing " + i + " and " + j; assert fqns[j].equals(fqns[i]) : "Error on equals comparing " + i + " and " + j; assert fqns[i].hashCode() == fqns[j].hashCode() : "Error on hashcode comparing " + i + " and " + j; } } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/api/0000755000175000017500000000000011376173765023225 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/api/CacheAPITest.java0000644000175000017500000002755511022175401026254 0ustar twernertwernerpackage org.jboss.cache.api; import org.jboss.cache.Cache; import org.jboss.cache.CacheFactory; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.Node; import org.jboss.cache.Region; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.ConfigurationException; import org.jboss.cache.notifications.annotation.CacheListener; import org.jboss.cache.notifications.annotation.NodeCreated; import org.jboss.cache.notifications.event.Event; import org.jboss.cache.transaction.GenericTransactionManagerLookup; import org.jboss.cache.util.CachePrinter; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.TransactionManager; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Tests the {@link org.jboss.cache.Cache} public API at a high level * * @author Manik Surtani */ @Test(groups = "functional") public class CacheAPITest { private Cache cache; protected boolean optimistic; final List events = new ArrayList(); @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { // start a single cache instance CacheFactory cf = new DefaultCacheFactory(); cache = cf.createCache("META-INF/conf-test/local-tx-service.xml", false); cache.getConfiguration().setNodeLockingScheme(optimistic ? Configuration.NodeLockingScheme.OPTIMISTIC : Configuration.NodeLockingScheme.PESSIMISTIC); cache.start(); events.clear(); } @AfterMethod(alwaysRun = true) public void tearDown() { if (cache != null) cache.stop(); } /** * Tests that the configuration contains the values expected, as well as immutability of certain elements */ public void testConfiguration() { Configuration c = cache.getConfiguration(); assertEquals(Configuration.CacheMode.LOCAL, c.getCacheMode()); assertEquals(GenericTransactionManagerLookup.class.getName(), c.getTransactionManagerLookupClass()); // note that certain values should be immutable. E.g., CacheMode cannot be changed on the fly. try { c.setCacheMode(Configuration.CacheMode.REPL_SYNC); assert false : "Should have thrown an Exception"; } catch (ConfigurationException e) { // expected } // others should be changeable though. c.setLockAcquisitionTimeout(100); } public void testGetMembersInLocalMode() { assert cache.getMembers() == null : "Cache members should be null if running in LOCAL mode"; } /** * Basic usage of cache listeners *

* A more complete test that tests notifications is in org.jboss.cache.notifications */ public void testCacheListeners() { assertEquals(0, cache.getCacheListeners().size()); Object dummy = new Listener(); cache.addCacheListener(dummy); assertEquals(1, cache.getCacheListeners().size()); cache.getRoot().addChild(Fqn.fromString("/blah")); // test that the event was captured by the listener. // FOR A FULL TEST ON NOTIFICATIONS SEE TESTS IN org.jboss.cache.notifications assertEquals(1, events.size()); cache.removeCacheListener(dummy); assertEquals(0, cache.getCacheListeners().size()); } /** * All cache operations should happen on a {@link Node} - I.e., you look up a {@link Node} and perform data operations * on this {@link Node}. For convenience and familiarity with JBoss Cache 1.x, we provide some helpers in {@link Cache} * which dives you direct data access to nodes. *

* This test exercises these. */ public void testConvenienceMethods() { Fqn fqn = Fqn.fromString("/test/fqn"); String key = "key", value = "value"; Map data = new HashMap(); data.put(key, value); assertNull(cache.get(fqn, key)); cache.put(fqn, key, value); assertEquals(value, cache.get(fqn, key)); cache.remove(fqn, key); assertNull(cache.get(fqn, key)); cache.put(fqn, data); assertEquals(value, cache.get(fqn, key)); } /** * Another convenience method that tests node removal */ public void testNodeConvenienceNodeRemoval() { // this fqn is relative, but since it is from the root it may as well be absolute Fqn fqn = Fqn.fromString("/test/fqn"); cache.getRoot().addChild(fqn); assertTrue(cache.getRoot().hasChild(fqn)); assertEquals(true, cache.removeNode(fqn)); assertFalse(cache.getRoot().hasChild(fqn)); assertEquals(false, cache.removeNode(fqn)); System.out.println("Cache: " + CachePrinter.printCacheDetails(cache)); // Check that it's removed if it has a child Fqn child = Fqn.fromString("/test/fqn/child"); cache.getRoot().addChild(child); assertTrue(cache.getRoot().hasChild(child)); assertEquals(true, cache.removeNode(fqn)); assertFalse(cache.getRoot().hasChild(fqn)); assertEquals(false, cache.removeNode(fqn)); } /** * Tests basic eviction */ public void testEvict() { Fqn one = Fqn.fromString("/one"); Fqn two = Fqn.fromString("/one/two"); String key = "key", value = "value"; cache.getRoot().addChild(one).put(key, value); cache.getRoot().addChild(two).put(key, value); assertTrue(cache.getRoot().hasChild(one)); assertFalse(cache.getRoot().getChild(one).getData().isEmpty()); assertTrue(cache.getRoot().hasChild(two)); assertFalse(cache.getRoot().getChild(two).getData().isEmpty()); // evict two cache.evict(two, false); assertTrue(cache.getRoot().hasChild(one)); assertTrue(cache.getRoot().getChild(one).getKeys().contains(key)); assertFalse(cache.getRoot().hasChild(two)); // now add 2 again... cache.getRoot().addChild(two).put(key, value); // now evict one, NOT recursive cache.evict(one, false); // one will NOT be removed, just emptied. assertTrue(cache.getRoot().hasChild(one)); assertFalse(cache.getRoot().getChild(one).getKeys().contains(key)); // two will be unaffected assertTrue(cache.getRoot().hasChild(two)); assertTrue(cache.getRoot().getChild(two).getKeys().contains(key)); } /** * Tests recursive eviction */ public void testEvictRecursive() { Fqn one = Fqn.fromString("/one"); Fqn two = Fqn.fromString("/one/two"); String key = "key", value = "value"; cache.getRoot().addChild(one).put(key, value); cache.getRoot().addChild(two).put(key, value); assertTrue(cache.getRoot().hasChild(one)); assertFalse(cache.getRoot().getChild(one).getData().isEmpty()); assertTrue(cache.getRoot().hasChild(two)); assertFalse(cache.getRoot().getChild(two).getData().isEmpty()); // evict two cache.evict(two, true); assertTrue(cache.getRoot().hasChild(one)); assertFalse(cache.getRoot().getChild(one).getData().isEmpty()); assertFalse(cache.getRoot().hasChild(two)); // now add 2 again... cache.getRoot().addChild(two).put(key, value); // now evict one, recursive cache.evict(one, true); assertFalse(cache.getRoot().hasChild(one)); assertFalse(cache.getRoot().hasChild(two)); } /** * Again, see org.jboss.cache for more extensive tests on Regions. This just tests the getRegion API on cache. */ public void testRegion() { Region rootRegion = cache.getRegion(Fqn.ROOT, true); assertNotNull(rootRegion);// guaranteed never to return null if createIfAbsent is true. assertSame(rootRegion, cache.getRegion(Fqn.ROOT, true)); Region otherRegion = cache.getRegion(Fqn.fromString("/other/region"), true); assertNotNull(otherRegion); assertSame(otherRegion, cache.getRegion(Fqn.fromString("/other/region"), true)); } /** * Again, see org.jboss.cache for more extensive tests on Regions. This just tests the getRegion API on cache. */ public void testParentRegion1() { Region rootRegion = cache.getRegion(Fqn.ROOT, true); assertNotNull(rootRegion);// guaranteed never to return null if createIfAbsent is true. assertSame(rootRegion, cache.getRegion(Fqn.ROOT, false)); Region otherRegion = cache.getRegion(Fqn.fromString("/other/region"), false); // should return the same parent region as root. assertSame(otherRegion, rootRegion); } /** * Again, see org.jboss.cache for more extensive tests on Regions. This just tests the getRegion API on cache. */ public void testParentRegion2() { Region rootRegion = cache.getRegion(Fqn.ROOT, true); Region parentRegion = cache.getRegion(Fqn.fromString("/parent"), true); assertNotSame("parentRegion should be a new region in its own right", rootRegion, parentRegion); Region childRegion = cache.getRegion(Fqn.fromString("/parent/region"), false); assertSame("Expecting the same region as parentRegion", childRegion, parentRegion); } /** * Again, see org.jboss.cache for more extensive tests on Regions. This just tests the getRegion API on cache. */ public void testNullRegion() { Region myRegion = cache.getRegion(Fqn.fromString("/myregion"), true); assertNotNull(myRegion);// guaranteed never to return null if createIfAbsent is true. assertSame(myRegion, cache.getRegion(Fqn.fromString("/myregion"), false)); Region otherRegion = cache.getRegion(Fqn.fromString("/other/region"), false); // should return the default region assertNotNull(otherRegion); assertEquals(Fqn.ROOT, otherRegion.getFqn()); } public void testStopClearsData() throws Exception { Fqn a = Fqn.fromString("/a"); Fqn b = Fqn.fromString("/a/b"); String key = "key", value = "value"; cache.getRoot().addChild(a).put(key, value); cache.getRoot().addChild(b).put(key, value); cache.getRoot().put(key, value); assertEquals(value, cache.getRoot().get(key)); assertEquals(value, cache.getRoot().getChild(a).get(key)); assertEquals(value, cache.getRoot().getChild(b).get(key)); cache.stop(); cache.start(); assertNull(cache.getRoot().get(key)); assertTrue(cache.getRoot().getData().isEmpty()); assertTrue(cache.getRoot().getChildren().isEmpty()); } public void testPhantomStructuralNodesOnRemove() { CacheSPI spi = (CacheSPI) cache; assert spi.peek(Fqn.fromString("/a/b/c"), true, true) == null; assert !spi.removeNode("/a/b/c"); assert spi.peek(Fqn.fromString("/a/b/c"), true, true) == null; assert spi.peek(Fqn.fromString("/a/b"), true, true) == null; assert spi.peek(Fqn.fromString("/a"), true, true) == null; } public void testPhantomStructuralNodesOnRemoveTransactional() throws Exception { CacheSPI spi = (CacheSPI) cache; TransactionManager tm = spi.getTransactionManager(); assert spi.peek(Fqn.fromString("/a/b/c"), true, true) == null; tm.begin(); assert !spi.removeNode("/a/b/c"); tm.commit(); assert spi.peek(Fqn.fromString("/a/b/c"), true, true) == null; assert spi.peek(Fqn.fromString("/a/b"), true, true) == null; assert spi.peek(Fqn.fromString("/a"), true, true) == null; } public void testRpcManagerElements() { assertEquals("CacheMode.LOCAL cache has no address", null, cache.getLocalAddress()); assertEquals("CacheMode.LOCAL cache has no members list", null, cache.getMembers()); } @CacheListener public class Listener { @NodeCreated public void nodeCreated(Event e) { if (e.isPre()) events.add("Created"); } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/api/DeletedChildResurrectionTest.java0000644000175000017500000001176311017455042031636 0ustar twernertwernerpackage org.jboss.cache.api; import org.jboss.cache.Cache; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.Node; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.UnitTestCacheConfigurationFactory; import org.jboss.cache.util.TestingUtil; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.TransactionManager; /** * Tests whether, in a single tx, deleting a parent node with an pre-existing * child and then re-adding a node with the parent Fqn results * in the pre-existing child remaining in the cache after tx commit. * * @author Brian Stansberry * @since 2.1.0 */ @Test(groups = {"functional"}) public class DeletedChildResurrectionTest { private Cache cache; private static final Fqn A_B = Fqn.fromString("/a/b"); private static final Fqn A = Fqn.fromString("/a"); private static final Fqn A_C = Fqn.fromString("/a/c"); private static final String KEY = "key"; private static final String VALUE = "value"; private static final String K2 = "k2"; private static final String V2 = "v2"; @BeforeMethod(alwaysRun = true) public void setUp() { cache = new DefaultCacheFactory().createCache(UnitTestCacheConfigurationFactory.createConfiguration(Configuration.CacheMode.LOCAL, true), false); cache.getConfiguration().setCacheMode(Configuration.CacheMode.LOCAL); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { TestingUtil.killCaches(cache); } /** * Tests whether the deleted child re-appears if the parent node is re-added * via a simple node.addChild(parentFqn) call. Pessimistic locking case. * * @throws Exception */ public void testDeletedChildResurrectionPessimistic1() throws Exception { deletedChildResurrectionTest1(false); } /** * Tests whether the deleted child re-appears if the parent node is re-added * via a simple node.addChild(parentFqn) call. Optimistic locking case. * * @throws Exception */ public void testDeletedChildResurrectionOptimistic1() throws Exception { deletedChildResurrectionTest1(true); } /** * Tests whether the deleted child re-appears if the parent node is re-added * via a node.addChild(differentChildofParentFqn) call. Pessimistic locking case. * * @throws Exception */ public void testDeletedChildResurrectionPessimistic2() throws Exception { deletedChildResurrectionTest2(false); } /** * Tests whether the deleted child re-appears if the parent node is re-added * via a node.addChild(differentChildofParentFqn) call. Optimistic locking case. * * @throws Exception */ public void testDeletedChildResurrectionOptimistic2() throws Exception { deletedChildResurrectionTest2(true); } /** * Tests whether, in a single tx, deleting a parent node with an pre-existing * child and then inserting a different child under the parent Fqn results * in the pre-existing child remaining in the cache after tx commit. */ private void deletedChildResurrectionTest1(boolean optimistic) throws Exception { cache.getConfiguration().setNodeLockingOptimistic(optimistic); cache.start(); CacheSPI spi = (CacheSPI) cache; Node root = cache.getRoot(); TransactionManager txManager = cache.getConfiguration().getRuntimeConfig().getTransactionManager(); root.addChild(A_B).put(KEY, VALUE); cache.put(A, "key","value"); txManager.begin(); root.removeChild(A); root.addChild(A); txManager.commit(); assert !root.hasChild(A_B); assert null == cache.get(A, "key"); // do a peek to ensure the node really has been removed and not just marked for removal assert spi.peek(A_B, true, true) == null; assert root.hasChild(A); } /** * Tests whether, in a single tx, deleting a parent node with an pre-existing * child and then inserting a different child under the parent Fqn results * in the pre-existing child remaining in the cache after tx commit. */ private void deletedChildResurrectionTest2(boolean optimistic) throws Exception { cache.getConfiguration().setNodeLockingOptimistic(optimistic); cache.start(); Node root = cache.getRoot(); TransactionManager txManager = cache.getConfiguration().getRuntimeConfig().getTransactionManager(); root.addChild(A_B).put(KEY, VALUE); txManager.begin(); root.removeChild(A); root.addChild(A_C).put(K2, V2); txManager.commit(); assert !root.hasChild(A_B); assert root.hasChild(A_C); assert V2.equals(root.getChild(A_C).get(K2)); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/api/nodevalidity/0000755000175000017500000000000011376173764025717 5ustar twernertwerner././@LongLink0000000000000000000000000000015400000000000011565 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/api/nodevalidity/ReplicatedPessNodeValidityTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/api/nodevalidity/ReplicatedPessNodeValidityTe0000644000175000017500000000170410732232543033342 0ustar twernertwernerpackage org.jboss.cache.api.nodevalidity; import org.jboss.cache.Cache; import org.jboss.cache.CacheFactory; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.config.Configuration; import org.testng.annotations.Test; /** * @author Manik Surtani * @since 2.1.0 */ @Test(groups = {"functional"}) public class ReplicatedPessNodeValidityTest extends NodeValidityTestBase { protected Cache createObserver() { return newCache(); } protected Cache createModifier() { return newCache(); } protected Cache newCache() { CacheFactory f = new DefaultCacheFactory(); Cache cache = f.createCache(false); cache.getConfiguration().setCacheMode(Configuration.CacheMode.REPL_SYNC); optimisticConfiguration(cache.getConfiguration()); cache.start(); return cache; } } ././@LongLink0000000000000000000000000000014700000000000011567 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/api/nodevalidity/LocalPessNodeValidityTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/api/nodevalidity/LocalPessNodeValidityTest.ja0000644000175000017500000000217111017455042033255 0ustar twernertwernerpackage org.jboss.cache.api.nodevalidity; import org.jboss.cache.Cache; import org.jboss.cache.CacheFactory; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.util.TestingUtil; import org.testng.annotations.AfterMethod; import org.testng.annotations.Test; /** * @author Manik Surtani * @since 2.1.0 */ @Test(groups = {"functional"}) public class LocalPessNodeValidityTest extends NodeValidityTestBase { private Cache cache; public LocalPessNodeValidityTest() { clustered = false; } @AfterMethod public void tearDown() { super.tearDown(); TestingUtil.killCaches(cache); cache = null; } protected Cache createObserver() { return createModifier(); } protected Cache createModifier() { if (cache == null) { CacheFactory f = new DefaultCacheFactory(); cache = f.createCache(false); optimisticConfiguration(cache.getConfiguration()); cache.start(); return cache; } return cache; } } ././@LongLink0000000000000000000000000000015500000000000011566 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/api/nodevalidity/InvalidatedPessNodeValidityTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/api/nodevalidity/InvalidatedPessNodeValidityT0000644000175000017500000000372210732232543033347 0ustar twernertwernerpackage org.jboss.cache.api.nodevalidity; import org.jboss.cache.Cache; import org.jboss.cache.CacheFactory; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.config.Configuration; import org.jboss.cache.loader.DummyInMemoryCacheLoader; import org.jboss.cache.loader.DummySharedInMemoryCacheLoader; import org.testng.annotations.AfterMethod; import org.testng.annotations.Test; /** * @author Manik Surtani * @since 2.1.0 */ @Test(groups = {"functional"}) public class InvalidatedPessNodeValidityTest extends NodeValidityTestBase { protected DummyInMemoryCacheLoader loader; public InvalidatedPessNodeValidityTest() { invalidation = true; } protected Cache createObserver() { return newCache(); } protected Cache createModifier() { return newCache(); } @AfterMethod public void emptyCacheLoader() { if (loader != null) loader.wipe(); } protected Cache newCache() { CacheFactory f = new DefaultCacheFactory(); Cache cache = f.createCache(false); cache.getConfiguration().setCacheMode(Configuration.CacheMode.INVALIDATION_SYNC); optimisticConfiguration(cache.getConfiguration()); // need a cache loader as a shared data source between the 2 instances CacheLoaderConfig.IndividualCacheLoaderConfig iclc = new CacheLoaderConfig.IndividualCacheLoaderConfig(); iclc.setClassName(DummySharedInMemoryCacheLoader.class.getName()); CacheLoaderConfig clc = new CacheLoaderConfig(); clc.addIndividualCacheLoaderConfig(iclc); cache.getConfiguration().setCacheLoaderConfig(clc); cache.start(); CacheSPI spi = (CacheSPI) cache; loader = (DummyInMemoryCacheLoader) spi.getCacheLoaderManager().getCacheLoader(); return cache; } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/api/nodevalidity/NodeValidityTestBase.java0000644000175000017500000003000411017455042032565 0ustar twernertwernerpackage org.jboss.cache.api.nodevalidity; import org.jboss.cache.Cache; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.Node; import org.jboss.cache.NodeNotValidException; import org.jboss.cache.NodeSPI; import org.jboss.cache.config.Configuration; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.optimistic.DefaultDataVersion; import org.jboss.cache.transaction.DummyTransactionManagerLookup; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.Collections; /** * exercises the isValid() api call on node. * * @author Manik Surtani * @since 2.1.0 */ @Test(groups = {"functional"}) public abstract class NodeValidityTestBase { protected boolean optimistic; // needed to attach a blockUntilViewsReceived in setup protected boolean clustered = true; // needed to test tombstones protected boolean invalidation = false; protected Cache observer; protected Cache modifier; protected Fqn parent = Fqn.fromString("/parent"); protected Fqn child = Fqn.fromString("/parent/child"); protected String K = "k", V = "v"; protected abstract Cache createObserver(); protected abstract Cache createModifier(); protected void optimisticConfiguration(Configuration c) { if (optimistic) { c.setNodeLockingScheme(Configuration.NodeLockingScheme.OPTIMISTIC); c.setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName()); c.setSyncCommitPhase(true); c.setSyncRollbackPhase(true); } } @BeforeMethod public void setUp() { observer = createObserver(); modifier = createModifier(); if (clustered) TestingUtil.blockUntilViewsReceived(60000, observer, modifier); } @AfterMethod public void tearDown() { TestingUtil.killCaches(observer, modifier); observer = null; modifier = null; } public void testRemoval() { // observer.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); observer.put(parent, K, V); Node obsNode = observer.getRoot().getChild(parent); assert obsNode.get(K).equals(V) : "Data should be in the node."; assert obsNode.isValid() : "Node should be valid"; modifier.removeNode(parent); assert !obsNode.isValid() : "Should no longer be valid"; } public void testRemovalWithChildren() { // observer.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); observer.put(child, K, V); Node obsParentNode = observer.getRoot().getChild(parent); Node obsChildNode = observer.getRoot().getChild(child); assert obsChildNode.get(K).equals(V) : "Data should be in the node."; assert obsChildNode.isValid() : "Node should be valid"; assert obsParentNode.isValid() : "Node should be valid"; modifier.removeNode(parent); assert !obsParentNode.isValid() : "Should no longer be valid"; assert !obsChildNode.isValid() : "Should no longer be valid"; } public void testMove() { Fqn newParent = Fqn.fromString("/newParent/parent"); //observer.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); observer.put(parent, K, V); Node obsNode = observer.getRoot().getChild(parent); assert obsNode.get(K).equals(V) : "Data should be in the node."; assert obsNode.isValid() : "Node should be valid"; // new parent needs to exist first. modifier.getRoot().addChild(newParent); modifier.move(parent, newParent.getParent()); // the old node is only marked as invalid if we use opt locking // with pess locking we directly move the node reference so the old ref is still valid, EVEN if the move happens // remotely. if (optimistic) assert !obsNode.isValid() : "Should no longer be valid"; assert observer.getRoot().getChild(newParent).isValid() : "Should be valid"; } public void testMoveWithChildren() { Fqn newParent = Fqn.fromString("/newParent/parent"); Fqn newChild = Fqn.fromString("/newParent/parent/child"); // observer.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); observer.put(child, K, V); Node obsParentNode = observer.getRoot().getChild(parent); Node obsChildNode = observer.getRoot().getChild(child); assert obsChildNode.get(K).equals(V) : "Data should be in the node."; assert obsChildNode.isValid() : "Node should be valid"; assert obsParentNode.isValid() : "Node should be valid"; // new parent needs to exist first. modifier.getRoot().addChild(newParent); modifier.move(parent, newParent.getParent()); // the old node is only marked as invalid if we use opt locking // with pess locking we directly move the node reference so the old ref is still valid. if (optimistic) { assert !obsParentNode.isValid() : "Should no longer be valid"; assert !obsChildNode.isValid() : "Should no longer be valid"; } assert observer.getRoot().getChild(newParent).isValid() : "Should be valid"; assert observer.getRoot().getChild(newChild).isValid() : "Should be valid"; } public void testEvict() { // eviction should affect validity observer.put(parent, K, V); Node obsNode = observer.getRoot().getChild(parent); assert obsNode.get(K).equals(V) : "Data should be in the node."; assert obsNode.isValid() : "Node should be valid"; // eviction needs to happen on the same cache being watched observer.evict(parent, false); assert !obsNode.isValid() : "Node should not be valid"; } public void testOperationsOnInvalidNode() { observer.put(parent, K, V); Node obsNode = observer.getRoot().getChild(parent); assert obsNode.get(K).equals(V) : "Data should be in the node."; assert obsNode.isValid() : "Node should be valid"; modifier.removeNode(parent); assert !obsNode.isValid() : "Node should not be valid"; // all operations on the cached node should throw a NodeNotValidException try { obsNode.get(K); assert false : "Should fail"; } catch (NodeNotValidException good) { // do nothing } try { obsNode.put(K, "v2"); assert false : "Should fail"; } catch (NodeNotValidException good) { // do nothing } try { obsNode.remove(K); assert false : "Should fail"; } catch (NodeNotValidException good) { // do nothing } try { obsNode.clearData(); assert false : "Should fail"; } catch (NodeNotValidException good) { // do nothing } try { obsNode.putAll(Collections.singletonMap(K, "v2")); assert false : "Should fail"; } catch (NodeNotValidException good) { // do nothing } try { obsNode.getKeys(); assert false : "Should fail"; } catch (NodeNotValidException good) { // do nothing } try { obsNode.hasChild("Something"); assert false : "Should fail"; } catch (NodeNotValidException good) { // do nothing } try { obsNode.removeChild("Something"); assert false : "Should fail"; } catch (NodeNotValidException good) { // do nothing } try { obsNode.addChild(child); assert false : "Should fail"; } catch (NodeNotValidException good) { // do nothing } try { obsNode.getChildrenNames(); assert false : "Should fail"; } catch (NodeNotValidException good) { // do nothing } } public void testExistenceOfTombstones() { CacheSPI modifierImpl = (CacheSPI) modifier; CacheSPI observerImpl = (CacheSPI) observer; modifier.put(parent, K, V); modifier.removeNode(parent); if (optimistic && invalidation) { // if we are using optimistic invalidation then we should see tombstones. NOT otherwise. NodeSPI modifierTombstone = modifierImpl.peek(parent, true, true); NodeSPI observerTombstone = observerImpl.peek(parent, true, true); assert modifierTombstone != null : "Modifier tombstone should not be null"; assert observerTombstone != null : "Observer tombstone should not be null"; assert !modifierTombstone.isValid() : "Should not be valid"; assert !observerTombstone.isValid() : "Should not be valid"; assert ((DefaultDataVersion) modifierTombstone.getVersion()).getRawVersion() == 2 : "Tombstone should be versioned"; assert ((DefaultDataVersion) observerTombstone.getVersion()).getRawVersion() == 2 : "Tombstone should be versioned"; } else { // if we are using pess locking there should be NO tombstones, regardless of replication/invalidation! assert modifierImpl.peek(parent, true, true) == null : "Tombstone should not exist"; assert observerImpl.peek(parent, true, true) == null : "Tombstone should not exist"; } } public void testExistenceOfTombstonesWithChildren() { CacheSPI modifierImpl = (CacheSPI) modifier; CacheSPI observerImpl = (CacheSPI) observer; modifier.put(child, K, V); modifier.removeNode(parent); if (optimistic && invalidation) { // if we are using optimistic invalidation then we should see tombstones. NOT otherwise. NodeSPI modifierParentTombstone = modifierImpl.peek(parent, true, true); NodeSPI observerParentTombstone = observerImpl.peek(parent, true, true); NodeSPI modifierChildTombstone = modifierImpl.peek(child, true, true); NodeSPI observerChildTombstone = observerImpl.peek(child, true, true); assert modifierParentTombstone != null : "Modifier parent tombstone should not be null"; assert observerParentTombstone != null : "Observer parent tombstone should not be null"; assert modifierChildTombstone != null : "Modifier child tombstone should not be null"; assert observerChildTombstone != null : "Observer child tombstone should not be null"; assert !modifierParentTombstone.isValid() : "Should not be valid"; assert !observerParentTombstone.isValid() : "Should not be valid"; assert !modifierChildTombstone.isValid() : "Should not be valid"; assert !observerChildTombstone.isValid() : "Should not be valid"; assert ((DefaultDataVersion) modifierParentTombstone.getVersion()).getRawVersion() == 1 : "Tombstone should be versioned"; assert ((DefaultDataVersion) observerParentTombstone.getVersion()).getRawVersion() == 1 : "Tombstone should be versioned"; // note that versions on children cannot be incremented/updated since the remove operation was // performed on the parent. assert ((DefaultDataVersion) modifierChildTombstone.getVersion()).getRawVersion() == 1 : "Tombstone should be versioned"; assert ((DefaultDataVersion) observerChildTombstone.getVersion()).getRawVersion() == 1 : "Tombstone should be versioned"; } else { // if we are using pess locking there should be NO tombstones, regardless of replication/invalidation! assert modifierImpl.peek(parent, true, true) == null : "Tombstone should not exist"; assert observerImpl.peek(parent, true, true) == null : "Tombstone should not exist"; assert modifierImpl.peek(child, true, true) == null : "Tombstone should not exist"; assert observerImpl.peek(child, true, true) == null : "Tombstone should not exist"; } } } ././@LongLink0000000000000000000000000000014600000000000011566 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/api/nodevalidity/LocalOptNodeValidityTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/api/nodevalidity/LocalOptNodeValidityTest.jav0000644000175000017500000000054110707226542033300 0ustar twernertwernerpackage org.jboss.cache.api.nodevalidity; import org.testng.annotations.Test; /** * * @author Manik Surtani * @since 2.1.0 */ @Test(groups = {"functional"}) public class LocalOptNodeValidityTest extends LocalPessNodeValidityTest { public LocalOptNodeValidityTest() { optimistic = true; } }././@LongLink0000000000000000000000000000015300000000000011564 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/api/nodevalidity/ReplicatedOptNodeValidityTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/api/nodevalidity/ReplicatedOptNodeValidityTes0000644000175000017500000000055210707226542033361 0ustar twernertwernerpackage org.jboss.cache.api.nodevalidity; import org.testng.annotations.Test; /** * * @author Manik Surtani * @since 2.1.0 */ @Test(groups = {"functional"}) public class ReplicatedOptNodeValidityTest extends ReplicatedPessNodeValidityTest { public ReplicatedOptNodeValidityTest() { optimistic = true; } }././@LongLink0000000000000000000000000000015400000000000011565 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/api/nodevalidity/InvalidatedOptNodeValidityTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/api/nodevalidity/InvalidatedOptNodeValidityTe0000644000175000017500000000725710727771403033362 0ustar twernertwernerpackage org.jboss.cache.api.nodevalidity; import org.jboss.cache.CacheSPI; import org.jboss.cache.NodeSPI; import org.jboss.cache.optimistic.DefaultDataVersion; import org.testng.annotations.Test; /** * @author Manik Surtani * @since 2.1.0 */ @Test(groups = {"functional"}) public class InvalidatedOptNodeValidityTest extends InvalidatedPessNodeValidityTest { public InvalidatedOptNodeValidityTest() { optimistic = true; } public void testTombstoneRevival() { modifier.put(parent, K, V); modifier.removeNode(parent); NodeSPI observerNode = (NodeSPI) observer.getRoot().getChild(parent); assert observerNode == null : "Should be removed"; // now try a put on a with a newer data version; should work modifier.getInvocationContext().getOptionOverrides().setDataVersion(new DefaultDataVersion(10)); modifier.put(parent, K, V); NodeSPI modifierNode = (NodeSPI) modifier.getRoot().getChild(parent); assert modifierNode != null : "Should not be null"; assert modifierNode.isValid() : "No longer a tombstone"; assert ((DefaultDataVersion) modifierNode.getVersion()).getRawVersion() == 10 : "Version should be updated"; observerNode = (NodeSPI) observer.getRoot().getChild(parent); assert observerNode != null : "Should not be null"; assert observerNode.isValid() : "No longer a tombstone"; assert ((DefaultDataVersion) observerNode.getVersion()).getRawVersion() == 10 : "Version should be updated"; } public void testTombstoneVersioningFailure() throws Exception { CacheSPI modifierImpl = (CacheSPI) modifier; CacheSPI observerImpl = (CacheSPI) observer; modifier.put(parent, K, V); // test that this exists in the (shared) loader assert loader.get(parent) != null; assert loader.get(parent).size() > 0; modifier.removeNode(parent); // assert that tombstones exist on both instances assert modifierImpl.peek(parent, true, true) != null; assert observerImpl.peek(parent, true, true) != null; assert modifierImpl.peek(parent, false, false) == null; assert observerImpl.peek(parent, false, false) == null; // make sure this does not exist in the loader; since it HAS been removed assert loader.get(parent) == null; NodeSPI observerNode = (NodeSPI) observer.getRoot().getChild(parent); assert observerNode == null : "Should be removed"; // now try a put on a with a newer data version; should work modifier.getInvocationContext().getOptionOverrides().setDataVersion(new DefaultDataVersion(1)); try { modifier.put(parent, K, V); assert false : "Should have barfed!"; } catch (RuntimeException expected) { } NodeSPI modifierNode = (NodeSPI) modifier.getRoot().getChild(parent); assert modifierNode == null : "Should be null"; observerNode = (NodeSPI) observer.getRoot().getChild(parent); assert observerNode == null : "Should be null"; NodeSPI modifierTombstone = modifierImpl.peek(parent, true, true); NodeSPI observerTombstone = observerImpl.peek(parent, true, true); assert modifierTombstone != null : "Tombstone should still exist"; assert observerTombstone != null : "Tombstone should still exist"; assert !modifierTombstone.isValid() : "Should not be valid"; assert !observerTombstone.isValid() : "Should not be valid"; assert ((DefaultDataVersion) modifierTombstone.getVersion()).getRawVersion() == 2 : "Should retain versioning"; assert ((DefaultDataVersion) observerTombstone.getVersion()).getRawVersion() == 2 : "Should retain versioning"; } }jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/api/DestroyedCacheAPITest.java0000644000175000017500000002200311022175401030116 0ustar twernertwernerpackage org.jboss.cache.api; import org.jboss.cache.Cache; import org.jboss.cache.CacheFactory; import org.jboss.cache.CacheStatus; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.Node; import org.jboss.cache.config.Configuration; import org.jboss.cache.notifications.annotation.CacheListener; import org.jboss.cache.notifications.annotation.NodeCreated; import org.jboss.cache.notifications.event.Event; import org.jboss.cache.transaction.GenericTransactionManagerLookup; import static org.testng.AssertJUnit.*; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.HashMap; import java.util.Map; /** * Tests aspects of the {@link org.jboss.cache.Cache} public API when * destroy() has been called on the cache. * * @author Brian Stansberry */ @Test(groups = "functional") public class DestroyedCacheAPITest { private Cache cache; protected boolean optimistic; private Fqn parent = Fqn.fromString("/test/fqn"); private Fqn child = Fqn.fromString("/test/fqn/child"); private String version; private Node root; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { // start a single cache instance CacheFactory cf = new DefaultCacheFactory(); cache = cf.createCache("META-INF/conf-test/local-tx-service.xml", false); cache.getConfiguration().setNodeLockingScheme(optimistic ? Configuration.NodeLockingScheme.OPTIMISTIC : Configuration.NodeLockingScheme.PESSIMISTIC); cache.start(); version = cache.getVersion(); cache.getRoot().addChild(parent); cache.getRoot().addChild(child); root = cache.getRoot(); cache.stop(); cache.destroy(); } /** * Tests that the configuration contains the values expected, as well as immutability of certain elements */ public void testConfiguration() { Configuration c = cache.getConfiguration(); assertEquals(Configuration.CacheMode.LOCAL, c.getCacheMode()); assertEquals(GenericTransactionManagerLookup.class.getName(), c.getTransactionManagerLookupClass()); // Certain values, e.g. CacheMode should be immutable once started, // but we aren't started, so should be able to change them c.setCacheMode(Configuration.CacheMode.REPL_SYNC); assertEquals(Configuration.CacheMode.REPL_SYNC, c.getCacheMode()); // others are always changeable. c.setLockAcquisitionTimeout(100); assertEquals(100, c.getLockAcquisitionTimeout()); } /** * Basic usage of cache listeners *

* A more complete test that tests notifications is in org.jboss.cache.notifications */ public void testCacheListeners() { try { cache.getCacheListeners(); } catch (IllegalStateException good) { // expected } Object dummy = new Listener(); try { cache.addCacheListener(dummy); } catch (IllegalStateException good) { // expected } try { cache.removeCacheListener(dummy); } catch (IllegalStateException good) { // expected } } /** * Tests the basic gets, puts. Expectation is all will throw an * ISE. *

* BES 2008/03/22 -- This behavior is not actually documented. Maintainers * shouldn't feel constrained from updating this test to match * agreed upon behavior changes; I'm just adding it so any changes to the * current behavior will trigger failures and ensure that people are aware of * the change and agree that it's correct. */ public void testConvenienceMethods() { String key = "key", value = "value"; Map data = new HashMap(); data.put(key, value); try { cache.get(parent, key); fail("Get key on destroyed cache did not throw ISE"); } catch (IllegalStateException good) { } try { cache.getNode(parent); fail("Get node on destroyed cache did not throw ISE"); } catch (IllegalStateException good) { } try { cache.put(parent, key, value); fail("Put key/value on destroyed cache did not throw ISE"); } catch (IllegalStateException good) { } try { cache.putForExternalRead(parent, key, value); fail("Put for external read on destroyed cache did not throw ISE"); } catch (IllegalStateException good) { } try { cache.put(parent, data); fail("Put Map on destroyed cache did not throw ISE"); } catch (IllegalStateException good) { } try { cache.move(child, Fqn.ROOT); fail("Remove move on destroyed cache did not throw ISE"); } catch (IllegalStateException good) { } try { cache.getData(parent); fail("getData on destroyed cache did not throw ISE"); } catch (IllegalStateException good) { } try { cache.getKeys(parent); fail("getKeys on destroyed cache did not throw ISE"); } catch (IllegalStateException good) { } try { cache.clearData(parent); fail("clearData on destroyed cache did not throw ISE"); } catch (IllegalStateException good) { } try { cache.remove(parent, key); fail("Remove key on destroyed cache did not throw ISE"); } catch (IllegalStateException good) { } try { cache.removeNode(parent); fail("Remove node on destroyed cache did not throw ISE"); } catch (IllegalStateException good) { } } /** * Tests the basic node addition, existence check, get, remove operations. * Expectation is all will throw an ISE. *

* BES 2008/03/22 -- This behavior is not actually documented. Maintainers * shouldn't feel constrained from updating this test to match * agreed upon behavior changes; I'm just adding it so any changes to the * current behavior will trigger failures and ensure that people are aware of * the change and agree that it's correct. */ public void testNodeAPI() { try { root.addChild(parent); fail("addChild on destroyed cache did not throw ISE"); } catch (IllegalStateException good) { } try { root.hasChild(parent); fail("hasChild on destroyed cache did not throw ISE"); } catch (IllegalStateException good) { } try { root.getChild(parent); fail("getChild on destroyed cache did not throw ISE"); } catch (IllegalStateException good) { } try { root.removeChild(parent); fail("removeChild on destroyed cache did not throw ISE"); } catch (IllegalStateException good) { } } /** * Tests the evict operation. Expectation is it will throw an ISE. */ public void testEvict() { try { cache.evict(parent, false); assert false : "Should throw ISE"; } catch (IllegalStateException ok) { } try { cache.evict(child, false); assert false : "Should throw ISE"; } catch (IllegalStateException ok) { } try { cache.evict(parent, true); assert false : "Should throw ISE"; } catch (IllegalStateException ok) { } } public void testGetRegion() { assertNull(cache.getRegion(parent, false)); // The javadoc mentions "UnsupportedOperationException" although it's // not clear about why it would be thrown. assertNotNull(cache.getRegion(Fqn.ROOT, true)); } public void testRemoveRegion() { assertFalse(cache.removeRegion(parent)); } public void testGetLocalAddress() { assertEquals("CacheMode.LOCAL cache has no address", null, cache.getLocalAddress()); } public void testGetMembers() { assertNull(cache.getMembers()); } public void testGetCacheStatus() { assertEquals(CacheStatus.DESTROYED, cache.getCacheStatus()); } /** * BES 2008/03/22. I don't know what the correct behavior should be. * The test checks the call succeeds, which is one possibility and is * what the current impl does. Can be something else, just not NPE. */ public void testInvocationContext() { InvocationContext ctx = new InvocationContext(); cache.setInvocationContext(ctx); assertSame(ctx, cache.getInvocationContext()); } public void testGetVersion() { assertEquals(version, cache.getVersion()); } @CacheListener public class Listener { @NodeCreated public void nodeCreated(Event e) { } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/api/SyncReplTest.java0000644000175000017500000001144311017455042026451 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.api; import org.jboss.cache.Cache; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.Node; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.config.Option; import org.jboss.cache.factories.UnitTestCacheConfigurationFactory; import org.jboss.cache.util.TestingUtil; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.HashMap; import java.util.Map; /** * @author Manik Surtani (manik@jboss.org) */ @Test(groups = {"functional", "jgroups"}) public class SyncReplTest { private CacheSPI cache1, cache2; @BeforeMethod(alwaysRun = true) public void setUp() { System.out.println("*** In setUp()"); cache1 = (CacheSPI) new DefaultCacheFactory().createCache(UnitTestCacheConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC)); cache2 = (CacheSPI) new DefaultCacheFactory().createCache(UnitTestCacheConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC)); TestingUtil.blockUntilViewsReceived(new Cache[]{cache1, cache2}, 5000); System.out.println("*** Finished setUp()"); } @AfterMethod(alwaysRun = true) public void tearDown() { if (cache1 != null) cache1.stop(); if (cache2 != null) cache2.stop(); } public void testBasicOperation() { assertClusterSize("Should only be 2 caches in the cluster!!!", 2); assertInvocationContextInitState(); Fqn f = Fqn.fromString("/test/data"); String k = "key", v = "value"; assertNull("Should be null", cache1.getRoot().getChild(f)); assertNull("Should be null", cache2.getRoot().getChild(f)); Node node = cache1.getRoot().addChild(f); assertNotNull("Should not be null", node); node.put(k, v); assertEquals(v, node.get(k)); assertEquals(v, cache1.get(f, k)); assertEquals("Should have replicated", v, cache2.get(f, k)); } public void testSyncRepl() { assertClusterSize("Should only be 2 caches in the cluster!!!", 2); assertInvocationContextInitState(); Fqn fqn = Fqn.fromString("/JSESSIONID/1010.10.5:3000/1234567890/1"); cache1.getConfiguration().setSyncCommitPhase(true); cache2.getConfiguration().setSyncCommitPhase(true); cache1.put(fqn, "age", 38); assertEquals("Value should be set", 38, cache1.get(fqn, "age")); assertEquals("Value should have replicated", 38, cache2.get(fqn, "age")); } public void testPutMap() { assertClusterSize("Should only be 2 caches in the cluster!!!", 2); assertInvocationContextInitState(); Fqn fqn = Fqn.fromString("/JSESSIONID/10.10.10.5:3000/1234567890/1"); Fqn fqn1 = Fqn.fromString("/JSESSIONID/10.10.10.5:3000/1234567890/2"); Map map = new HashMap(); map.put("1", "1"); map.put("2", "2"); cache1.getInvocationContext().getOptionOverrides().setSuppressLocking(true); cache1.getRoot().addChild(fqn).putAll(map); cache1.getInvocationContext().getOptionOverrides().setSuppressLocking(true); assertEquals("Value should be set", "1", cache1.get(fqn, "1")); map = new HashMap(); map.put("3", "3"); map.put("4", "4"); cache1.getInvocationContext().getOptionOverrides().setSuppressLocking(true); cache1.getRoot().addChild(fqn1).putAll(map); cache1.getInvocationContext().getOptionOverrides().setSuppressLocking(true); assertEquals("Value should be set", "2", cache1.get(fqn, "2")); } private void assertClusterSize(String message, int size) { assertClusterSize(message, size, cache1); assertClusterSize(message, size, cache2); } private void assertClusterSize(String message, int size, Cache c) { assertEquals(message, size, c.getMembers().size()); } private void assertInvocationContextInitState() { assertInvocationContextInitState(cache1); assertInvocationContextInitState(cache2); } private void assertInvocationContextInitState(Cache c) { InvocationContext ctx = c.getInvocationContext(); InvocationContext control = null; try { control = ctx.clone(); } catch (CloneNotSupportedException e) { throw new RuntimeException(e); } control.reset(); control.setOptionOverrides(new Option()); assertEquals("Should be equal", control, ctx); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/api/SyncReplTxTest.java0000644000175000017500000001223211017455042026762 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.api; import org.jboss.cache.Cache; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.Node; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.config.Option; import org.jboss.cache.factories.UnitTestCacheConfigurationFactory; import org.jboss.cache.util.TestingUtil; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.HeuristicMixedException; import javax.transaction.HeuristicRollbackException; import javax.transaction.NotSupportedException; import javax.transaction.RollbackException; import javax.transaction.Status; import javax.transaction.SystemException; import javax.transaction.Transaction; import javax.transaction.TransactionManager; import java.util.ArrayList; import java.util.List; /** * @author Manik Surtani (manik@jboss.org) */ @Test(groups = {"functional", "jgroups", "transaction"}) public class SyncReplTxTest { private List> caches; @BeforeMethod(alwaysRun = true) public void setUp() { System.out.println("*** In setUp()"); caches = new ArrayList>(); caches.add((CacheSPI) new DefaultCacheFactory().createCache(UnitTestCacheConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC))); caches.add((CacheSPI) new DefaultCacheFactory().createCache(UnitTestCacheConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC))); TestingUtil.blockUntilViewsReceived(caches.toArray(new Cache[0]), 5000); System.out.println("*** Finished setUp()"); } @AfterMethod(alwaysRun = true) public void tearDown() { System.out.println("*** In tearDown()"); if (caches != null) { for (CacheSPI c : caches) { if (c != null) { Transaction t; try { if ((t = c.getTransactionManager().getTransaction()) != null) { try { if (t.getStatus() == Status.STATUS_ACTIVE) c.getTransactionManager().rollback(); } catch (SystemException e) { // do nothing } } } catch (SystemException e) { // do nothing } c.stop(); } } caches = null; } System.out.println("*** Finished tearDown()"); } private TransactionManager beginTransaction(Cache cache) throws NotSupportedException, SystemException { TransactionManager mgr = cache.getConfiguration().getRuntimeConfig().getTransactionManager(); mgr.begin(); return mgr; } public void testBasicOperation() throws SystemException, NotSupportedException, HeuristicMixedException, HeuristicRollbackException, RollbackException { assertClusterSize("Should only be 2 caches in the cluster!!!", 2); assertInvocationContextInitState(); Fqn f = Fqn.fromString("/test/data"); String k = "key", v = "value"; assertNull("Should be null", caches.get(0).getRoot().getChild(f)); assertNull("Should be null", caches.get(1).getRoot().getChild(f)); Node node = caches.get(0).getRoot().addChild(f); assertNotNull("Should not be null", node); TransactionManager tm = beginTransaction(caches.get(0)); node.put(k, v); Transaction tx1 = caches.get(0).getInvocationContext().getTransaction(); assertNotNull("Transaction can't be null ", tx1); tm.commit(); assertEquals(v, node.get(k)); assertEquals(v, caches.get(0).get(f, k)); assertEquals("Should have replicated", v, caches.get(1).get(f, k)); } private void assertClusterSize(String message, int size) { for (Cache c : caches) { assertClusterSize(message, size, c); } } private void assertClusterSize(String message, int size, Cache c) { assertEquals(message, size, c.getMembers().size()); } private void assertInvocationContextInitState() { for (Cache c : caches) { assertInvocationContextInitState(c); } } private void assertInvocationContextInitState(Cache c) { InvocationContext ctx = c.getInvocationContext(); InvocationContext control = null; try { control = ctx.clone(); } catch (CloneNotSupportedException e) { throw new RuntimeException(e); } control.reset(); control.setOptionOverrides(new Option()); assertEquals("Should be equal", control, ctx); } }jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/api/NodeSPITest.java0000644000175000017500000000727211003335605026155 0ustar twernertwernerpackage org.jboss.cache.api; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.NodeSPI; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.Map; import java.util.Set; /** * Tests NodeSPI specific APIs. */ @Test(groups = {"functional"}) public class NodeSPITest { private CacheSPI cache; private NodeSPI root; @BeforeMethod(alwaysRun = true) public void setUp() { cache = (CacheSPI) new DefaultCacheFactory().createCache(); root = cache.getRoot(); } @AfterMethod(alwaysRun = true) public void tearDown() { if (cache != null) cache.stop(); root = null; cache = null; } public void testDeepOperations() throws Exception { Fqn A = Fqn.fromString("/a"); Fqn B = Fqn.fromString("/b"); Fqn A_B = Fqn.fromString("/a/b"); NodeSPI nodeA, nodeB; cache.put(A, "k", "v"); cache.put(A_B, "k", "v"); nodeA = cache.getRoot().getChildDirect(A);// should work nodeB = cache.getRoot().getChildDirect(A_B);// should work assertEquals(A_B, nodeB.getFqn()); nodeB = nodeA.getChildDirect(B);// should work assertEquals(A_B, nodeB.getFqn()); assertEquals(true, cache.getRoot().removeChildDirect(A_B));// should work assertEquals(false, cache.getRoot().removeChildDirect(A_B));// should work cache.put(A_B, "k", "v"); assertEquals(true, nodeA.removeChildDirect(B));// should work assertEquals(false, nodeA.removeChildDirect(B));// should work assertEquals(true, cache.getRoot().removeChildDirect(A.getLastElement())); assertEquals(false, cache.getRoot().removeChildDirect(A.getLastElement())); try { cache.getRoot().addChildDirect(A_B);// should fail fail("Should have failed"); } catch (UnsupportedOperationException e) { // expected } nodeA = cache.getRoot().addChildDirect(A);// should work nodeA.addChildDirect(B);// should work } public void testChildrenImmutabilityAndDefensiveCopy() { // put some stuff in the root node String childName = "childName"; String newChild = "newChild"; root.addChild(Fqn.fromElements(childName)); Set childrenDirect = root.getChildrenDirect(); try { childrenDirect.clear(); fail("getChildrenDirect() should return an unmodifiable collection object"); } catch (UnsupportedOperationException uoe) { // good; should be immutable } // now test defensive copy root.addChild(Fqn.fromElements(newChild)); assertTrue("root.addChild() should have succeeded", root.getChildrenNamesDirect().contains(newChild)); assertTrue("getChildrenDirect() should have made a defensive copy of the data collection object", !childrenDirect.contains(newChild)); } public void testNullCollections() { // nothing in root, make sure we see no nulls. assertNotNull("Should not be null", root.getDataDirect()); assertTrue("Should be empty", root.getDataDirect().isEmpty()); assertNotNull("Should not be null", root.getKeysDirect()); assertTrue("Should be empty", root.getKeysDirect().isEmpty()); assertNotNull("Should not be null", root.getChildrenDirect()); assertTrue("Should be empty", root.getChildrenDirect().isEmpty()); assertNotNull("Should not be null", root.getChildrenNamesDirect()); assertTrue("Should be empty", root.getChildrenNamesDirect().isEmpty()); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/api/CacheAPIOptimisticTest.java0000644000175000017500000000047110737501363030321 0ustar twernertwernerpackage org.jboss.cache.api; import org.testng.annotations.Test; /** * Optimistically locked version of {@link org.jboss.cache.api.CacheAPITest} */ @Test(groups = "functional") public class CacheAPIOptimisticTest extends CacheAPITest { public CacheAPIOptimisticTest() { optimistic = true; } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/api/NodeMoveOptimisticTest.java0000644000175000017500000000160211002145716030465 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.api; import org.testng.annotations.Test; public class NodeMoveOptimisticTest extends NodeMoveAPITest { public NodeMoveOptimisticTest() { optimistic = true; } public void testLocks() { // no op } public void testLocksDeepMove() { // no op } @Override @Test(groups = {"functional"}) public void testWithCacheloaders() throws Exception { super.testWithCacheloaders(); //To change body of overridden methods use File | Settings | File Templates. } @Override @Test(groups = {"functional"}) public void testWithPassivation() throws Exception { super.testWithPassivation(); //To change body of overridden methods use File | Settings | File Templates. } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/api/NodeReplicatedMoveTest.java0000644000175000017500000001666111006114744030431 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.api; import org.jboss.cache.Cache; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.Node; import org.jboss.cache.NodeNotExistsException; import org.jboss.cache.NodeSPI; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.factories.UnitTestCacheConfigurationFactory; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertNull; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.TransactionManager; @Test(groups = {"functional", "jgroups"}) public class NodeReplicatedMoveTest { protected Node rootNode, nodeA, nodeB, nodeC, nodeD, nodeE; protected CacheSPI cache1; protected CacheSPI cache2; protected TransactionManager tm; protected static final Fqn A = Fqn.fromString("/a"), B = Fqn.fromString("/b"), C = Fqn.fromString("/c"), D = Fqn.fromString("/d"), E = Fqn.fromString("/e"); protected Object k = "key", vA = "valueA", vB = "valueB", vC = "valueC", vD = "valueD", vE = "valueE"; protected boolean optimistic = false; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { // start a single cache instance cache1 = (CacheSPI) new DefaultCacheFactory().createCache(UnitTestCacheConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC), false); cache1.getConfiguration().setSyncCommitPhase(true); cache1.getConfiguration().setSyncRollbackPhase(true); cache1.getConfiguration().setNodeLockingScheme(optimistic ? Configuration.NodeLockingScheme.OPTIMISTIC : Configuration.NodeLockingScheme.PESSIMISTIC); cache1.start(); rootNode = cache1.getRoot(); tm = cache1.getTransactionManager(); // start second instance cache2 = (CacheSPI) new DefaultCacheFactory().createCache(UnitTestCacheConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC), false); cache2.getConfiguration().setSyncCommitPhase(true); cache2.getConfiguration().setSyncRollbackPhase(true); cache2.getConfiguration().setNodeLockingScheme(optimistic ? Configuration.NodeLockingScheme.OPTIMISTIC : Configuration.NodeLockingScheme.PESSIMISTIC); cache2.start(); } @AfterMethod(alwaysRun = true) public void tearDown() { if (cache1 != null) cache1.stop(); if (cache2 != null) cache2.stop(); if (rootNode != null) rootNode = null; } public void testReplicatability() { nodeA = rootNode.addChild(A); nodeB = nodeA.addChild(B); nodeA.put(k, vA); nodeB.put(k, vB); assertEquals(vA, cache1.getRoot().getChild(A).get(k)); assertEquals(vB, cache1.getRoot().getChild(A).getChild(B).get(k)); assertEquals(vA, cache2.getRoot().getChild(A).get(k)); assertEquals(vB, cache2.getRoot().getChild(A).getChild(B).get(k)); // now move... cache1.move(nodeB.getFqn(), Fqn.ROOT); assertEquals(vA, cache1.getRoot().getChild(A).get(k)); assertEquals(vB, cache1.getRoot().getChild(B).get(k)); assertEquals(vA, cache2.getRoot().getChild(A).get(k)); assertEquals(vB, cache2.getRoot().getChild(B).get(k)); } public void testInvalidations() throws Exception { cache1.stop(); cache2.stop(); cache1.destroy(); cache2.destroy(); if (optimistic) { cache1.getConfiguration().setNodeLockingScheme(Configuration.NodeLockingScheme.OPTIMISTIC); cache2.getConfiguration().setNodeLockingScheme(Configuration.NodeLockingScheme.OPTIMISTIC); } cache1.getConfiguration().setCacheMode(Configuration.CacheMode.INVALIDATION_SYNC); cache2.getConfiguration().setCacheMode(Configuration.CacheMode.INVALIDATION_SYNC); cache1.start(); cache2.start(); nodeA = rootNode.addChild(A); nodeB = nodeA.addChild(B); nodeA.put(k, vA); nodeB.put(k, vB); assertEquals(vA, cache1.getRoot().getChild(A).get(k)); assertEquals(vB, cache1.getRoot().getChild(A).getChild(B).get(k)); assertInvalidated(cache2, A, "Should be invalidated", optimistic); assertInvalidated(cache2, Fqn.fromRelativeElements(A, B.getLastElement()), "Should be invalidated", optimistic); // now move... cache1.move(nodeB.getFqn(), Fqn.ROOT); assertEquals(vA, cache1.getRoot().getChild(A).get(k)); assertEquals(vB, cache1.getRoot().getChild(B).get(k)); assertInvalidated(cache2, A, "Should be invalidated", optimistic); assertInvalidated(cache2, B, "Should be invalidated", optimistic); // now make sure a node exists on cache 2 cache2.getRoot().addChild(A).put("k2", "v2"); // te invalidation will happen in afterCompletion, hence no exception! try { cache1.move(B, A);// should throw an NPE if (!optimistic) assert false : "Should throw an exception!"; } catch (NodeNotExistsException expected) { if (optimistic) assert false : "Should not have thrown an exception!"; } } private void assertInvalidated(Cache cache, Fqn fqn, String msg, boolean optimistic) { assert cache.getRoot().getChild(fqn) == null : msg; NodeSPI n = ((CacheSPI) cache).peek(fqn, true, true); assert n == null || optimistic : msg; assert !optimistic || !n.isValid() : msg; } public void testReplTxCommit() throws Exception { Fqn A_B = Fqn.fromRelativeFqn(A, B); nodeA = rootNode.addChild(A); nodeB = nodeA.addChild(B); nodeA.put(k, vA); nodeB.put(k, vB); assertEquals(vA, cache1.getRoot().getChild(A).get(k)); assertEquals(vB, cache1.getRoot().getChild(A).getChild(B).get(k)); assertEquals(vA, cache2.getRoot().getChild(A).get(k)); assertEquals(vB, cache2.getRoot().getChild(A).getChild(B).get(k)); // now move... tm.begin(); cache1.move(nodeB.getFqn(), Fqn.ROOT); assertEquals(vA, cache1.get(A, k)); assertNull(cache1.get(A_B, k)); assertEquals(vB, cache1.get(B, k)); tm.commit(); assertEquals(vA, cache1.getRoot().getChild(A).get(k)); assertEquals(vB, cache1.getRoot().getChild(B).get(k)); assertEquals(vA, cache2.getRoot().getChild(A).get(k)); assertEquals(vB, cache2.getRoot().getChild(B).get(k)); } public void testReplTxRollback() throws Exception { nodeA = rootNode.addChild(A); nodeB = nodeA.addChild(B); nodeA.put(k, vA); nodeB.put(k, vB); assertEquals(vA, cache1.getRoot().getChild(A).get(k)); assertEquals(vB, cache1.getRoot().getChild(A).getChild(B).get(k)); assertEquals(vA, cache2.getRoot().getChild(A).get(k)); assertEquals(vB, cache2.getRoot().getChild(A).getChild(B).get(k)); // now move... tm.begin(); cache1.move(nodeB.getFqn(), Fqn.ROOT); assertEquals(vA, cache1.get(A, k)); assertEquals(vB, cache1.get(B, k)); tm.rollback(); assertEquals(vA, cache1.getRoot().getChild(A).get(k)); assertEquals(vB, cache1.getRoot().getChild(A).getChild(B).get(k)); assertEquals(vA, cache2.getRoot().getChild(A).get(k)); assertEquals(vB, cache2.getRoot().getChild(A).getChild(B).get(k)); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/api/NodeMoveAPITest.java0000644000175000017500000004211211017455042026755 0ustar twernertwernerpackage org.jboss.cache.api; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.Node; import org.jboss.cache.NodeNotExistsException; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.XmlConfigurationParser; import org.jboss.cache.loader.DummyInMemoryCacheLoader; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.xml.XmlHelper; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import org.w3c.dom.Element; import javax.transaction.TransactionManager; import java.util.Map; import java.util.Random; import java.util.concurrent.CountDownLatch; /** * Excercises and tests the new move() api * * @author Manik Surtani * @since 2.0.0 */ public class NodeMoveAPITest { protected final Log log = LogFactory.getLog(getClass()); protected Node rootNode, nodeA, nodeB, nodeC, nodeD, nodeE; protected CacheSPI cache; protected TransactionManager tm; protected static final Fqn A = Fqn.fromString("/a"), B = Fqn.fromString("/b"), C = Fqn.fromString("/c"), D = Fqn.fromString("/d"), E = Fqn.fromString("/e"); protected Object k = "key", vA = "valueA", vB = "valueB", vC = "valueC", vD = "valueD", vE = "valueE"; protected boolean optimistic = false; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { // start a single cache instance cache = (CacheSPI) new DefaultCacheFactory().createCache("META-INF/conf-test/local-tx-service.xml", false); cache.getConfiguration().setNodeLockingScheme(optimistic ? Configuration.NodeLockingScheme.OPTIMISTIC : Configuration.NodeLockingScheme.PESSIMISTIC); cache.start(); rootNode = cache.getRoot(); tm = cache.getTransactionManager(); } @AfterMethod(alwaysRun = true) public void tearDown() { TestingUtil.killCaches(cache); } @Test(groups = {"functional"}) public void testBasicMove() { nodeA = rootNode.addChild(A); nodeA.put(k, vA); nodeB = rootNode.addChild(B); nodeB.put(k, vB); nodeC = nodeA.addChild(C); nodeC.put(k, vC); /* /a/c /b */ assertTrue(rootNode.hasChild(A)); assertTrue(rootNode.hasChild(B)); assertFalse(rootNode.hasChild(C)); assertTrue(nodeA.hasChild(C)); // test data assertEquals("" + nodeA, vA, nodeA.get(k)); assertEquals(vB, nodeB.get(k)); assertEquals(vC, nodeC.get(k)); // parentage assertEquals(nodeA, nodeC.getParent()); log.info("BEFORE MOVE " + cache); // move cache.move(nodeC.getFqn(), nodeB.getFqn()); // re-fetch nodeC nodeC = cache.getNode(Fqn.fromRelativeFqn(nodeB.getFqn(), C)); log.info("POST MOVE " + cache); log.info("HC " + nodeC + " " + System.identityHashCode(nodeC)); Node x = cache.getRoot().getChild(Fqn.fromString("b/c")); log.info("HC " + x + " " + System.identityHashCode(x)); /* /a /b/c */ assertEquals("NODE C " + nodeC, "/b/c", nodeC.getFqn().toString()); assertTrue(rootNode.hasChild(A)); assertTrue(rootNode.hasChild(B)); assertFalse(rootNode.hasChild(C)); assertFalse(nodeA.hasChild(C)); assertTrue(nodeB.hasChild(C)); // test data assertEquals(vA, nodeA.get(k)); assertEquals(vB, nodeB.get(k)); assertEquals(vC, nodeC.get(k)); // parentage assertEquals("B is parent of C: " + nodeB, nodeB, nodeC.getParent()); } @SuppressWarnings("unchecked") private Node genericize(Node node) { return (Node) node; } @Test(groups = {"functional"}) public void testMoveWithChildren() { nodeA = rootNode.addChild(A); nodeA.put(k, vA); nodeB = rootNode.addChild(B); nodeB.put(k, vB); nodeC = nodeA.addChild(C); nodeC.put(k, vC); nodeD = nodeC.addChild(D); nodeD.put(k, vD); nodeE = nodeD.addChild(E); nodeE.put(k, vE); assertTrue(rootNode.hasChild(A)); assertTrue(rootNode.hasChild(B)); assertFalse(rootNode.hasChild(C)); assertTrue(nodeA.hasChild(C)); assertTrue(nodeC.hasChild(D)); assertTrue(nodeD.hasChild(E)); // test data assertEquals(vA, nodeA.get(k)); assertEquals(vB, nodeB.get(k)); assertEquals(vC, nodeC.get(k)); assertEquals(vD, nodeD.get(k)); assertEquals(vE, nodeE.get(k)); // parentage assertEquals(rootNode, nodeA.getParent()); assertEquals(rootNode, nodeB.getParent()); assertEquals(nodeA, nodeC.getParent()); assertEquals(nodeC, nodeD.getParent()); assertEquals(nodeD, nodeE.getParent()); // move log.info("move " + nodeC + " to " + nodeB); cache.move(nodeC.getFqn(), nodeB.getFqn()); //System.out.println("nodeB " + nodeB); //System.out.println("nodeC " + nodeC); // child nodes will need refreshing, since existing pointers will be stale. nodeC = nodeB.getChild(C); nodeD = nodeC.getChild(D); nodeE = nodeD.getChild(E); assertTrue(rootNode.hasChild(A)); assertTrue(rootNode.hasChild(B)); assertFalse(rootNode.hasChild(C)); assertFalse(nodeA.hasChild(C)); assertTrue(nodeB.hasChild(C)); assertTrue(nodeC.hasChild(D)); assertTrue(nodeD.hasChild(E)); // test data assertEquals(vA, nodeA.get(k)); assertEquals(vB, nodeB.get(k)); assertEquals(vC, nodeC.get(k)); assertEquals(vD, nodeD.get(k)); assertEquals(vE, nodeE.get(k)); // parentage assertEquals(rootNode, nodeA.getParent()); assertEquals(rootNode, nodeB.getParent()); assertEquals(nodeB, nodeC.getParent()); assertEquals(nodeC, nodeD.getParent()); assertEquals(nodeD, nodeE.getParent()); } @Test(groups = {"functional"}) public void testTxCommit() throws Exception { nodeA = rootNode.addChild(A); nodeB = nodeA.addChild(B); assertEquals(rootNode, nodeA.getParent()); assertEquals(nodeA, nodeB.getParent()); assertEquals(nodeA, rootNode.getChildren().iterator().next()); assertEquals(nodeB, nodeA.getChildren().iterator().next()); tm.begin(); // move node B up to hang off the root cache.move(nodeB.getFqn(), Fqn.ROOT); tm.commit(); nodeB = rootNode.getChild(B); assertEquals(rootNode, nodeA.getParent()); assertEquals(rootNode, nodeB.getParent()); assertTrue(rootNode.getChildren().contains(nodeA)); assertTrue(rootNode.getChildren().contains(nodeB)); assertTrue(nodeA.getChildren().isEmpty()); } @Test(groups = {"functional"}) public void testTxRollback() throws Exception { nodeA = rootNode.addChild(A); nodeB = nodeA.addChild(B); assertEquals(rootNode, nodeA.getParent()); assertEquals(nodeA, nodeB.getParent()); assertEquals(nodeA, rootNode.getChildren().iterator().next()); assertEquals(nodeB, nodeA.getChildren().iterator().next()); tm.begin(); // move node B up to hang off the root cache.move(nodeB.getFqn(), Fqn.ROOT); // need to think of a way to test the same with optimistically locked nodes if (!optimistic) { assertEquals(rootNode, nodeA.getParent()); assertEquals(rootNode, nodeB.getParent()); assertTrue(rootNode.getChildren().contains(nodeA)); assertTrue(rootNode.getChildren().contains(nodeB)); assertTrue(nodeA.getChildren().isEmpty()); } tm.rollback(); nodeA = rootNode.getChild(A); nodeB = nodeA.getChild(B); // should revert assertEquals(rootNode, nodeA.getParent()); assertEquals(nodeA, nodeB.getParent()); assertEquals(nodeA, rootNode.getChildren().iterator().next()); assertEquals(nodeB, nodeA.getChildren().iterator().next()); } @Test(groups = {"functional"}) public void testWithCacheloaders() throws Exception { doCacheLoaderTest(false, false); } @Test(groups = {"functional"}) public void testWithPassivation() throws Exception { doCacheLoaderTest(true, false); } @Test(groups = {"functional"}) public void testWithCacheloadersTx() throws Exception { doCacheLoaderTest(false, true); } @Test(groups = {"functional"}) public void testWithPassivationTx() throws Exception { doCacheLoaderTest(true, true); } protected void doCacheLoaderTest(boolean pasv, boolean useTx) throws Exception { // cache.stop(); cache.destroy(); cache.getConfiguration().setCacheLoaderConfig(getSingleCacheLoaderConfig(pasv, "", DummyInMemoryCacheLoader.class.getName(), null, false, false, false, false)); cache.start(); DummyInMemoryCacheLoader loader = (DummyInMemoryCacheLoader) cache.getCacheLoaderManager().getCacheLoader(); rootNode.put("key", "value"); if (!pasv) { Map m = loader.get(Fqn.ROOT); assertNotNull("Should not be null", m); assertEquals("value", m.get("key")); } nodeA = rootNode.addChild(A); nodeA.put(k, vA); nodeB = rootNode.addChild(B); nodeB.put(k, vB); nodeC = nodeA.addChild(C); nodeC.put(k, vC); nodeD = nodeC.addChild(D); nodeD.put(k, vD); nodeE = nodeD.addChild(E); nodeE.put(k, vE); cache.evict(Fqn.ROOT, true); // move if (useTx) tm.begin(); cache.move(nodeC.getFqn(), nodeB.getFqn()); if (useTx) tm.commit(); // after eviction, the node objects we hold are probably stale. nodeA = rootNode.getChild(A); nodeB = rootNode.getChild(B); nodeC = nodeB.getChild(C); log.info("nodeC get child B "); nodeD = nodeC.getChild(D); log.info("nodeD get child E "); nodeE = nodeD.getChild(E); Fqn old_C = C.clone(); Fqn old_D = Fqn.fromRelativeFqn(old_C, D); Fqn old_E = Fqn.fromRelativeFqn(old_D, E); // test data assertEquals(vA, nodeA.get(k)); assertEquals(vB, nodeB.get(k)); assertEquals(vC, nodeC.get(k)); assertEquals(vD, nodeD.get(k)); assertEquals(vE, nodeE.get(k)); // parentage assertEquals(rootNode, nodeA.getParent()); assertEquals(rootNode, nodeB.getParent()); assertEquals(nodeB, nodeC.getParent()); assertEquals(nodeC, nodeD.getParent()); assertEquals(nodeD, nodeE.getParent()); if (pasv) cache.evict(Fqn.ROOT, true); //now inspect the loader. assertEquals(vA, loader.get(nodeA.getFqn()).get(k)); assertEquals(vB, loader.get(nodeB.getFqn()).get(k)); assertEquals(vC, loader.get(nodeC.getFqn()).get(k)); assertEquals(vD, loader.get(nodeD.getFqn()).get(k)); assertEquals(vE, loader.get(nodeE.getFqn()).get(k)); assertNull(loader.get(old_C)); assertNull(loader.get(old_D)); assertNull(loader.get(old_E)); } @Test(groups = {"functional"}) public void testLocksDeepMove() throws Exception { nodeA = rootNode.addChild(A); nodeB = nodeA.addChild(B); nodeD = nodeB.addChild(D); nodeC = rootNode.addChild(C); assertEquals(0, cache.getNumberOfLocksHeld()); tm.begin(); cache.move(nodeC.getFqn(), nodeB.getFqn()); // nodeC should have a RL, nodeA should have a RL, nodeB should have a WL, nodeD should have a WL assertEquals("ROOT should have a RL, nodeC should have a RL, nodeA should have a RL, nodeB should have a WL, nodeD should have a WL", 5, cache.getNumberOfLocksHeld()); tm.commit(); assertEquals(0, cache.getNumberOfLocksHeld()); } @Test(groups = {"functional"}) public void testLocks() throws Exception { nodeA = rootNode.addChild(A); nodeB = nodeA.addChild(B); nodeC = rootNode.addChild(C); assertEquals(0, cache.getNumberOfLocksHeld()); tm.begin(); cache.move(nodeC.getFqn(), nodeB.getFqn()); assertEquals("ROOT should have a RL, nodeC should have a RL, nodeA should have a RL, nodeB should have a WL", 4, cache.getNumberOfLocksHeld()); tm.commit(); assertEquals(0, cache.getNumberOfLocksHeld()); } @Test(groups = {"functional"}) public void testConcurrency() throws InterruptedException { // FIXME: investigate intermittent failure when in optimistic mode. if (optimistic) return; final int N = 3;// number of threads final int loops = 1 << 6;// number of loops // tests a tree structure as such: // /a // /b // /c // /d // /e // /x // /y // N threads constantly move /x and /y around to hang off either /a ~ /e randomly. final Fqn FQN_A = A, FQN_B = B, FQN_C = C, FQN_D = D, FQN_E = E, FQN_X = Fqn.fromString("/x"), FQN_Y = Fqn.fromString("/y"); // set up the initial structure. final Node[] NODES = { rootNode.addChild(FQN_A), rootNode.addChild(FQN_B), rootNode.addChild(FQN_C), rootNode.addChild(FQN_D), rootNode.addChild(FQN_E) }; final Node NODE_X = genericize(NODES[0]).addChild(FQN_X); final Node NODE_Y = genericize(NODES[1]).addChild(FQN_Y); Thread[] movers = new Thread[N]; final CountDownLatch latch = new CountDownLatch(1); final Random r = new Random(); for (int i = 0; i < N; i++) { movers[i] = new Thread("Mover-" + i) { public void run() { try { latch.await(); } catch (InterruptedException e) { } for (int counter = 0; counter < loops; counter++) { System.out.println(getName() + ": Attempt " + counter); try { cache.move(NODE_X.getFqn(), NODES[r.nextInt(NODES.length)].getFqn()); } catch (NodeNotExistsException e) { // this may happen ... } TestingUtil.sleepRandom(250); try { cache.move(NODE_Y.getFqn(), NODES[r.nextInt(NODES.length)].getFqn()); } catch (NodeNotExistsException e) { // this may happen ... } TestingUtil.sleepRandom(250); } } }; movers[i].start(); } latch.countDown(); for (Thread t : movers) { t.join(); } assertEquals(0, cache.getNumberOfLocksHeld()); boolean found_x = false, found_x_again = false; for (Node erased : NODES) { Node n = genericize(erased); if (!found_x) { found_x = n.hasChild(FQN_X); } else { found_x_again = found_x_again || n.hasChild(FQN_X); } } boolean found_y = false, found_y_again = false; for (Node erased : NODES) { Node n = genericize(erased); if (!found_y) { found_y = n.hasChild(FQN_Y); } else { found_y_again = found_y_again || n.hasChild(FQN_Y); } } assertTrue("Should have found x", found_x); assertTrue("Should have found y", found_y); assertFalse("Should have only found x once", found_x_again); assertFalse("Should have only found y once", found_y_again); } @Test(groups = {"functional"}) public void testMoveInSamePlace() { final Fqn FQN_X = Fqn.fromString("/x"); // set up the initial structure. Node aNode = rootNode.addChild(A); Node xNode = aNode.addChild(FQN_X); assertEquals(aNode.getChildren().size(), 1); cache.move(xNode.getFqn(), aNode.getFqn()); assertEquals(aNode.getChildren().size(), 1); } protected CacheLoaderConfig getSingleCacheLoaderConfig(boolean passivation, String preload, String cacheloaderClass, String properties, boolean async, boolean fetchPersistentState, boolean shared, boolean purgeOnStartup) throws Exception { String xml = "\n" + "" + passivation + "\n" + "" + preload + "\n" + "\n" + "" + cacheloaderClass + "\n" + "" + properties + "\n" + "" + async + "\n" + "" + shared + "\n" + "" + fetchPersistentState + "\n" + "" + purgeOnStartup + "\n" + "\n" + ""; Element element = XmlHelper.stringToElement(xml); return XmlConfigurationParser.parseCacheLoaderConfig(element); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/api/NodeAPIOptimisticTest.java0000644000175000017500000000045411003220233030161 0ustar twernertwernerpackage org.jboss.cache.api; import org.testng.annotations.Test; /** * An optimistic version of {@link org.jboss.cache.api.NodeAPITest} */ @Test(groups = "functional") public class NodeAPIOptimisticTest extends NodeAPITest { public NodeAPIOptimisticTest() { optimistic = true; } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/api/NodeReplicatedMoveOptimisticTest.java0000644000175000017500000000061411010537324032463 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.api; import org.testng.annotations.Test; @Test(groups = {"functional", "jgroups"}) public class NodeReplicatedMoveOptimisticTest extends NodeReplicatedMoveTest { public NodeReplicatedMoveOptimisticTest() { optimistic = true; } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/api/ResidentNodesTest.java0000644000175000017500000002237111017455042027462 0ustar twernertwernerpackage org.jboss.cache.api; import org.jboss.cache.Cache; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.util.internals.EvictionController; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.EvictionConfig; import org.jboss.cache.config.EvictionRegionConfig; import org.jboss.cache.eviction.LRUConfiguration; import org.jboss.cache.factories.UnitTestCacheConfigurationFactory; import static org.testng.Assert.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.TransactionManager; /** * Tester class for Node.isResident functionality. * * @author Mircea Markus * @since 2.1.0 */ @Test(groups = {"functional"}) public class ResidentNodesTest { private CacheSPI cache; private EvictionController evController; private final String TEST_NODES_ROOT = "residentNodesTest"; private Cache[] caches = {}; @BeforeMethod(alwaysRun = true) public void setUp() { Configuration cacheConfig = UnitTestCacheConfigurationFactory.createConfiguration(Configuration.CacheMode.LOCAL, true); cacheConfig.setCacheMode(Configuration.CacheMode.LOCAL); cache = (CacheSPI) new DefaultCacheFactory().createCache(cacheConfig, false); cache.getConfiguration().getEvictionConfig().setWakeupIntervalSeconds(1); createNewRegion(); cache.start(); evController = new EvictionController(cache); } /** * Setting up a new region for our purposes. */ private void createNewRegion() { EvictionConfig evConfig = cache.getConfiguration().getEvictionConfig(); EvictionRegionConfig evRegConfig = new EvictionRegionConfig(); evRegConfig.setRegionFqn(Fqn.fromString("/" + TEST_NODES_ROOT)); evRegConfig.setEventQueueSize(100); LRUConfiguration lruConfig = new LRUConfiguration(); lruConfig.setMaxAgeSeconds(100000); lruConfig.setTimeToLiveSeconds(100000); lruConfig.setMaxNodes(3); evRegConfig.setEvictionPolicyConfig(lruConfig); evConfig.getEvictionRegionConfigs().add(evRegConfig); //end setting up region stuff } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { cache.stop(); for (Cache c : caches) { if (c != null) { c.stop(); } } } /** * Mark some nodes as resident and show that they won't get evicted, * even if normally scenario they would */ public void testHappyFlow() throws InterruptedException { cache.put(getSubFqn("/a"), "k_a", "v_a"); cache.getNode(getSubFqn("/a")).setResident(true); cache.put(getSubFqn("/b"), "k_b", "v_b"); cache.getNode(getSubFqn("/b")).setResident(true); cache.put(getSubFqn("/c"), "k_c", "v_c"); cache.put(getSubFqn("/d"), "k_d", "v_d"); cache.put(getSubFqn("/e"), "k_e", "v_e"); cache.put(getSubFqn("/f"), "k_f", "v_f"); cache.put(getSubFqn("/g"), "k_g", "v_g"); cache.put(getSubFqn("/h"), "k_h", "v_h"); cache.put(getSubFqn("/i"), "k_i", "v_i"); evController.startEviction(); assertTrue(cache.exists(getSubFqn("/a"))); assertTrue(cache.exists(getSubFqn("/b"))); assertFalse(cache.exists(getSubFqn("/c"))); assertFalse(cache.exists(getSubFqn("/d"))); assertFalse(cache.exists(getSubFqn("/e"))); assertFalse(cache.exists(getSubFqn("/f"))); //only last three used are not evicted assertTrue(cache.exists(getSubFqn("/g"))); assertTrue(cache.exists(getSubFqn("/h"))); assertTrue(cache.exists(getSubFqn("/i"))); } public void simpleTest() throws Exception { cache.put(getSubFqn("/a"), "k_a", "v_a"); cache.put(getSubFqn("/b"), "k_b", "v_b"); cache.put(getSubFqn("/c"), "k_c", "v_c"); cache.put(getSubFqn("/d"), "k_d", "v_d"); evController.startEviction(); assertFalse(cache.exists(getSubFqn("/a"))); assertTrue(cache.exists(getSubFqn("/b"))); assertTrue(cache.exists(getSubFqn("/c"))); assertTrue(cache.exists(getSubFqn("/d"))); } /** * If a node is marked as resident, and a get is made on that given node then an VISITED event would normally be * added to the eviction queue. In a LRU scenario, this will cause another node to be evicted given that the size of * the eviction queue is bounded. This test makes sure that this scenario will not hapen. */ public void testNoEvictionEventsForResidentNodes() throws InterruptedException { cache.put(getSubFqn("/a"), "k_a", "v_a"); cache.put(getSubFqn("/b"), "k_b", "v_b"); cache.getNode(getSubFqn("/a")).setResident(true); cache.getNode(getSubFqn("/b")).setResident(true); cache.put(getSubFqn("/c"), "k_c", "v_c"); cache.put(getSubFqn("/d"), "k_d", "v_d"); cache.put(getSubFqn("/e"), "k_e", "v_e"); cache.put(getSubFqn("/f"), "k_f", "v_f"); cache.put(getSubFqn("/g"), "k_g", "v_g"); cache.put(getSubFqn("/h"), "k_h", "v_h"); //at this point the oldest nodes are /a and /b so. There are eviction events in the queue corresponding // to those nodes cache.getNode(getSubFqn("/a")); cache.getNode(getSubFqn("/b")); evController.startEviction(); //a and b should exist as those were marked resident. Also they shouldn't be caunted as nodes in the eviction // queue assertTrue(cache.exists(getSubFqn("/a"))); assertTrue(cache.exists(getSubFqn("/b"))); // c, d and e were the first accessed, they should be evicted assertFalse(cache.exists(getSubFqn("/c"))); assertFalse(cache.exists(getSubFqn("/d"))); assertFalse(cache.exists(getSubFqn("/e"))); //all of them should be there - even if we re-retrieved a and b at a prev step (cache.get(getSubFqn("/a"))) this //call shouldn't create an eviction event. assertTrue(cache.exists(getSubFqn("/f"))); assertTrue(cache.exists(getSubFqn("/g"))); assertTrue(cache.exists(getSubFqn("/h"))); } /** * Check the behavior whilst using optimistic locking. */ public void testResidencyAndOptimisticLocking() throws Exception { Configuration config = UnitTestCacheConfigurationFactory.createConfiguration(Configuration.CacheMode.LOCAL, true); config.setCacheMode(Configuration.CacheMode.LOCAL); config.setNodeLockingOptimistic(true); CacheSPI cache = (CacheSPI) new DefaultCacheFactory().createCache(config, true); cache.put(Fqn.fromString("/a/b"), "key", "value"); TransactionManager txManager = cache.getTransactionManager(); txManager.begin(); cache.getRoot().getChild(Fqn.fromString("/a/b")).setResident(true); cache.getRoot().getChild(Fqn.fromString("/a/b")).put("k2", "v2"); assertEquals(cache.getRoot().getChild(Fqn.fromString("/a/b")).getKeys().size(), 2); txManager.rollback(); assertTrue(cache.getRoot().getChild(Fqn.fromString("/a/b")).isResident()); txManager.begin(); cache.getRoot().getChild(Fqn.fromString("/a/b")).setResident(false); cache.getRoot().getChild(Fqn.fromString("/a/b")).put("k2", "v2"); assertEquals(cache.getRoot().getChild(Fqn.fromString("/a/b")).getKeys().size(), 2); txManager.commit(); assertFalse(cache.getRoot().getChild(Fqn.fromString("/a/b")).isResident()); assertEquals(cache.getRoot().getChild(Fqn.fromString("/a/b")).getKeys().size(), 2); try { if (cache.getTransactionManager().getTransaction() != null) { cache.getTransactionManager().rollback(); } } finally { cache.stop(); } } public void testResidencyAndPessimisticLocking() throws Exception { cache.put(Fqn.fromString("/a/b"), "key", "value"); TransactionManager txManager = cache.getTransactionManager(); assert cache.getNumberOfLocksHeld() == 0 : "Should have no stale locks!"; txManager.begin(); cache.getRoot().getChild(Fqn.fromString("/a/b")).setResident(true); cache.getRoot().getChild(Fqn.fromString("/a/b")).put("k2", "v2"); assertEquals(cache.getRoot().getChild(Fqn.fromString("/a/b")).getKeys().size(), 2); txManager.rollback(); assertTrue(cache.getRoot().getChild(Fqn.fromString("/a/b")).isResident()); assert cache.getNumberOfLocksHeld() == 0 : "Should have no stale locks!"; txManager.begin(); cache.getRoot().getChild(Fqn.fromString("/a/b")).setResident(false); cache.getRoot().getChild(Fqn.fromString("/a/b")).put("k2", "v2"); assertEquals(cache.getRoot().getChild(Fqn.fromString("/a/b")).getKeys().size(), 2); txManager.commit(); assertFalse(cache.getRoot().getChild(Fqn.fromString("/a/b")).isResident()); assertEquals(cache.getRoot().getChild(Fqn.fromString("/a/b")).getKeys().size(), 2); } private Fqn getSubFqn(String str) { return Fqn.fromString("/" + TEST_NODES_ROOT + str); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/api/pfer/0000755000175000017500000000000011376173764024160 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/api/pfer/PFERPessimisticReplSyncTest.java0000644000175000017500000000054510706155767032317 0ustar twernertwernerpackage org.jboss.cache.api.pfer; import org.jboss.cache.config.Configuration; import org.testng.annotations.Test; @Test(groups = {"functional"}) public class PFERPessimisticReplSyncTest extends PutForExternalReadTestBase { public PFERPessimisticReplSyncTest() { optimistic = false; cacheMode = Configuration.CacheMode.REPL_SYNC; } } ././@LongLink0000000000000000000000000000015100000000000011562 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/api/pfer/PFERPessimisticInvalidationSyncTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/api/pfer/PFERPessimisticInvalidationSyncTest.0000644000175000017500000000111110705726470033154 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.api.pfer; import org.jboss.cache.config.Configuration; /** * Test putForExternalRead with pessimistic locking and INVALIDATION_ASYNC. * * @author Brian Stansberry * @version $Revision$ */ public class PFERPessimisticInvalidationSyncTest extends PutForExternalReadTestBase { public PFERPessimisticInvalidationSyncTest() { optimistic = false; cacheMode = Configuration.CacheMode.INVALIDATION_SYNC; } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/api/pfer/PFEROptimisticReplAsyncTest.java0000644000175000017500000000054510706155767032310 0ustar twernertwernerpackage org.jboss.cache.api.pfer; import org.jboss.cache.config.Configuration; import org.testng.annotations.Test; @Test(groups = {"functional"}) public class PFEROptimisticReplAsyncTest extends PutForExternalReadTestBase { public PFEROptimisticReplAsyncTest() { optimistic = true; cacheMode = Configuration.CacheMode.REPL_ASYNC; } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/api/pfer/PFEROptimisticReplSyncTest.java0000644000175000017500000000054210706155767032144 0ustar twernertwernerpackage org.jboss.cache.api.pfer; import org.jboss.cache.config.Configuration; import org.testng.annotations.Test; @Test(groups = {"functional"}) public class PFEROptimisticReplSyncTest extends PutForExternalReadTestBase { public PFEROptimisticReplSyncTest() { optimistic = true; cacheMode = Configuration.CacheMode.REPL_SYNC; } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/api/pfer/PFERPessimisticReplAsyncTest.java0000644000175000017500000000055010706155767032454 0ustar twernertwernerpackage org.jboss.cache.api.pfer; import org.jboss.cache.config.Configuration; import org.testng.annotations.Test; @Test(groups = {"functional"}) public class PFERPessimisticReplAsyncTest extends PutForExternalReadTestBase { public PFERPessimisticReplAsyncTest() { optimistic = false; cacheMode = Configuration.CacheMode.REPL_ASYNC; } } ././@LongLink0000000000000000000000000000015000000000000011561 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/api/pfer/PFEROptimisticInvalidationSyncTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/api/pfer/PFEROptimisticInvalidationSyncTest.j0000644000175000017500000000120110706155767033164 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.api.pfer; import org.jboss.cache.config.Configuration; import org.testng.annotations.Test; /** * Test putForExternalRead with optimistic locking and INVALIDATION_SYNC. * * @author Brian Stansberry * @version $Revision$ */ @Test(groups = {"functional"}) public class PFEROptimisticInvalidationSyncTest extends PutForExternalReadTestBase { public PFEROptimisticInvalidationSyncTest() { optimistic = true; cacheMode = Configuration.CacheMode.INVALIDATION_SYNC; } } ././@LongLink0000000000000000000000000000015200000000000011563 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/api/pfer/PFERPessimisticInvalidationAsyncTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/api/pfer/PFERPessimisticInvalidationAsyncTest0000644000175000017500000000121010706155767033245 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.api.pfer; import org.jboss.cache.config.Configuration; import org.testng.annotations.Test; /** * Test putForExternalRead with pessimistic locking and INVALIDATION_SYNC. * * @author Brian Stansberry * @version $Revision$ */ @Test(groups = {"functional"}) public class PFERPessimisticInvalidationAsyncTest extends PutForExternalReadTestBase { public PFERPessimisticInvalidationAsyncTest() { optimistic = false; cacheMode = Configuration.CacheMode.INVALIDATION_ASYNC; } } ././@LongLink0000000000000000000000000000015100000000000011562 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/api/pfer/PFEROptimisticInvalidationAsyncTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/api/pfer/PFEROptimisticInvalidationAsyncTest.0000644000175000017500000000120610706155767033160 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.api.pfer; import org.jboss.cache.config.Configuration; import org.testng.annotations.Test; /** * Test putForExternalRead with optimistic locking and INVALIDATION_ASYNC. * * @author Brian Stansberry * @version $Revision$ */ @Test (groups = {"functional"}) public class PFEROptimisticInvalidationAsyncTest extends PutForExternalReadTestBase { public PFEROptimisticInvalidationAsyncTest() { optimistic = true; cacheMode = Configuration.CacheMode.INVALIDATION_ASYNC; } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/api/pfer/PutForExternalReadTestBase.java0000644000175000017500000004344711017455042032170 0ustar twernertwernerpackage org.jboss.cache.api.pfer; import org.easymock.EasyMock; import static org.easymock.EasyMock.*; import org.jboss.cache.Cache; import org.jboss.cache.CacheFactory; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.NodeSPI; import org.jboss.cache.RPCManager; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.util.internals.ReplicationListener; import org.jboss.cache.commands.ReplicableCommand; import org.jboss.cache.commands.write.*; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.factories.ComponentRegistry; import org.jboss.cache.factories.UnitTestCacheConfigurationFactory; import org.jboss.cache.lock.NodeLock; import org.jboss.cache.optimistic.TransactionWorkspace; import org.jboss.cache.transaction.GlobalTransaction; import org.jboss.cache.transaction.OptimisticTransactionEntry; import org.jgroups.Address; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.SystemException; import javax.transaction.Transaction; import javax.transaction.TransactionManager; import java.util.List; import java.util.Vector; @Test(groups = {"functional", "jgroups", "transaction"}) public abstract class PutForExternalReadTestBase { protected CacheSPI cache1, cache2; ReplicationListener replListener1; ReplicationListener replListener2; protected TransactionManager tm1, tm2; protected Fqn fqn = Fqn.fromString("/one/two"); protected Fqn parentFqn = fqn.getParent(); protected String key = "k", value = "v", value2 = "v2"; protected boolean useTx, optimistic; protected Configuration.CacheMode cacheMode; @BeforeMethod(alwaysRun = true) public void setUp() { CacheFactory cf = new DefaultCacheFactory(); cache1 = (CacheSPI) cf.createCache(UnitTestCacheConfigurationFactory.createConfiguration(cacheMode), false); cache1.getConfiguration().setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); cache1.getConfiguration().setNodeLockingScheme(optimistic ? Configuration.NodeLockingScheme.OPTIMISTIC : Configuration.NodeLockingScheme.PESSIMISTIC); cache1.start(); tm1 = cache1.getConfiguration().getRuntimeConfig().getTransactionManager(); cache2 = (CacheSPI) cf.createCache(UnitTestCacheConfigurationFactory.createConfiguration(cacheMode), false); cache2.getConfiguration().setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); cache2.getConfiguration().setNodeLockingScheme(optimistic ? Configuration.NodeLockingScheme.OPTIMISTIC : Configuration.NodeLockingScheme.PESSIMISTIC); cache2.start(); tm2 = cache2.getConfiguration().getRuntimeConfig().getTransactionManager(); TestingUtil.blockUntilViewsReceived(10000, cache1, cache2); } @AfterMethod(alwaysRun = true) public void tearDown() { TestingUtil.killCaches(cache1, cache2); } /** * Locks could only occur on the parent node is write locked since if the child node exists it is a no-op anyway. * If the parent node is read locked as well, there is no issue. */ public void testNoOpWhenLockedAnd0msTimeout() throws Exception { // create the parent node first ... cache1.put(parentFqn, key, value); tm1.begin(); cache1.put(parentFqn, key, value2); NodeSPI parentNode = null; TransactionWorkspace workspace = null; if (optimistic) workspace = extractTransactionWorkspace(cache1); else parentNode = (NodeSPI) cache1.getRoot().getChild(parentFqn); Transaction t = tm1.suspend(); assertLocked(parentFqn, parentNode, workspace, true); // parentFqn should be write-locked. long startTime = System.currentTimeMillis(); cache1.putForExternalRead(fqn, key, value); long waited = System.currentTimeMillis() - startTime; // crappy way to test that pFER does not block, but it is effective. assertTrue("Should not wait " + waited + " millis for lock timeout, should attempt to acquite lock with 0ms!", waited < cache1.getConfiguration().getLockAcquisitionTimeout()); // should not block. tm1.resume(t); tm1.commit(); asyncWait(); assertEquals("Parent node write should have succeeded", value2, cache1.get(parentFqn, key)); if (isUsingInvalidation()) assertNull("Parent node write should have invalidated", cache2.get(parentFqn, key)); else assertEquals("Parent node write should have replicated", value2, cache2.get(parentFqn, key)); if (!optimistic) { // doesn't apply with optimistic locking since both gtx2EntryMap will succeed here. assertNull("PFER should have been a no-op", cache1.get(fqn, key)); assertNull("PFER should have been a no-op", cache2.get(fqn, key)); } } public void testNoOpWhenNodePresent() { cache1.putForExternalRead(fqn, key, value); asyncWait(); assertEquals("PFER should have succeeded", value, cache1.get(fqn, key)); if (isUsingInvalidation()) assertNull("PFER should not have effected cache2", cache2.get(fqn, key)); else assertEquals("PFER should have replicated", value, cache2.get(fqn, key)); // reset cache1.removeNode(fqn); asyncWait(); assertFalse("Should have reset", cache1.getRoot().hasChild(fqn)); assertFalse("Should have reset", cache2.getRoot().hasChild(fqn)); cache1.put(fqn, key, value); asyncWait(); // now this pfer should be a no-op cache1.putForExternalRead(fqn, key, value2); assertEquals("PFER should have been a no-op", value, cache1.get(fqn, key)); if (isUsingInvalidation()) assertNull("PFER should have been a no-op", cache2.get(fqn, key)); else assertEquals("PFER should have been a no-op", value, cache2.get(fqn, key)); } private Vector

anyAddresses() { anyObject(); return null; } public void testAsyncForce() throws Exception { RPCManager rpcManager = EasyMock.createNiceMock(RPCManager.class); RPCManager originalRpcManager = cache1.getConfiguration().getRuntimeConfig().getRPCManager(); List
memberList = originalRpcManager.getMembers(); expect(rpcManager.getMembers()).andReturn(memberList).anyTimes(); // inject a mock RPC manager so that we can test whether calls made are sync or async. ComponentRegistry cr = TestingUtil.extractComponentRegistry(cache1); cr.registerComponent(rpcManager, RPCManager.class); cr.rewire(); // invalidations will not trigger any rpc call sfor PFER if (!isUsingInvalidation()) { // specify what we expect called on the mock Rpc Manager. For params we don't care about, just use ANYTHING. // setting the mock object to expect the "sync" param to be false. expect(rpcManager.callRemoteMethods(anyAddresses(), (ReplicableCommand) anyObject(), eq(false), anyLong(), anyBoolean())).andReturn(null); } replay(rpcManager); // now try a simple replication. Since the RPCManager is a mock object it will not actually replicate anything. cache1.putForExternalRead(fqn, key, value); verify(rpcManager); // cleanup TestingUtil.extractComponentRegistry(cache1).registerComponent(originalRpcManager, RPCManager.class); cache1.removeNode(fqn); } public void testTxSuspension() throws Exception { // create parent node first cache1.put(parentFqn, key, value); // start a tx and do some stuff. tm1.begin(); cache1.get(parentFqn, key); NodeSPI parentNode = null; TransactionWorkspace workspace = null; if (optimistic) workspace = extractTransactionWorkspace(cache1); else parentNode = (NodeSPI) cache1.getRoot().getChild(parentFqn); cache1.putForExternalRead(fqn, key, value); // should have happened in a separate tx and have committed already. Transaction t = tm1.suspend(); asyncWait(); assertLocked(parentFqn, parentNode, workspace, false); assertEquals("PFER should have completed", value, cache1.get(fqn, key)); if (isUsingInvalidation()) assertNull("PFER should not have effected cache2", cache2.get(fqn, key)); else assertEquals("PFER should have completed", value, cache2.get(fqn, key)); tm1.resume(t); tm1.commit(); asyncWait(); assertEquals("parent fqn tx should have completed", value, cache1.get(parentFqn, key)); if (isUsingInvalidation()) assertNull("parent fqn tx should have invalidated cache2", cache2.get(parentFqn, key)); else assertEquals("parent fqn tx should have completed", value, cache2.get(parentFqn, key)); } public void testExceptionSuppression() throws Exception { RPCManager barfingRpcManager = EasyMock.createNiceMock(RPCManager.class); RPCManager originalRpcManager = cache1.getConfiguration().getRuntimeConfig().getRPCManager(); try { List
memberList = originalRpcManager.getMembers(); expect(barfingRpcManager.getMembers()).andReturn(memberList).anyTimes(); expect(barfingRpcManager.getLocalAddress()).andReturn(originalRpcManager.getLocalAddress()).anyTimes(); expect(barfingRpcManager.callRemoteMethods(anyAddresses(), (ReplicableCommand) anyObject(), anyBoolean(), anyLong(), anyBoolean())).andThrow(new RuntimeException("Barf!")).anyTimes(); replay(barfingRpcManager); TestingUtil.extractComponentRegistry(cache1).registerComponent(barfingRpcManager, RPCManager.class); cache1.getConfiguration().getRuntimeConfig().setRPCManager(barfingRpcManager); TestingUtil.extractComponentRegistry(cache1).rewire(); try { cache1.put(fqn, key, value); if (!optimistic) fail("Should have barfed"); } catch (RuntimeException re) { } if (optimistic && !isUsingInvalidation()) { // proves that the put did, in fact, barf. Doesn't work for invalidations since the inability to invalidate will not cause a rollback. assertNull(cache1.get(fqn, key)); } else { // clean up any indeterminate state left over try { cache1.removeNode(fqn); // as above, the inability to invalidate will not cause an exception if (!isUsingInvalidation()) fail("Should have barfed"); } catch (RuntimeException re) { } } assertNull("Should have cleaned up", cache1.get(fqn, key)); // should not barf cache1.putForExternalRead(fqn, key, value); } finally { TestingUtil.extractComponentRegistry(cache1).registerComponent(originalRpcManager, RPCManager.class); } } public void testBasicPropagation() throws Exception { assert !cache1.exists(fqn); assert !cache2.exists(fqn); cache1.putForExternalRead(fqn, key, value); asyncWait(); assertEquals("PFER updated cache1", value, cache1.get(fqn, key)); Object expected = isUsingInvalidation() ? null : value; assertEquals("PFER propagated to cache2 as expected", expected, cache2.get(fqn, key)); cache2.putForExternalRead(fqn, key, value); asyncWait(); assertEquals("PFER updated cache2", value, cache2.get(fqn, key)); assertEquals("Cache1 should be unaffected", value, cache1.get(fqn, key)); } /** * Tests that setting a cacheModeLocal=true Option prevents propagation * of the putForExternalRead(). * * @throws Exception */ public void testSimpleCacheModeLocal() throws Exception { cacheModeLocalTest(false); } /** * Tests that setting a cacheModeLocal=true Option prevents propagation * of the putForExternalRead() when the call occurs inside a transaction. * * @throws Exception */ public void testCacheModeLocalInTx() throws Exception { cacheModeLocalTest(true); } /** * Tests that suspended transactions do not leak. See JBCACHE-1246. */ public void testMemLeakOnSuspendedTransactions() throws Exception { Fqn fqn2 = Fqn.fromString("/fqn/two"); tm1.begin(); cache1.putForExternalRead(fqn, key, value); tm1.commit(); asyncWait(); assert cache1.getTransactionTable().getNumGlobalTransactions() == 0 : "Cache 1 should have no stale global TXs"; assert cache1.getTransactionTable().getNumLocalTransactions() == 0 : "Cache 1 should have no stale local TXs"; assert cache2.getTransactionTable().getNumGlobalTransactions() == 0 : "Cache 2 should have no stale global TXs"; assert cache2.getTransactionTable().getNumLocalTransactions() == 0 : "Cache 2 should have no stale local TXs"; tm1.begin(); cache1.putForExternalRead(fqn, key, value); cache1.put(fqn2, key, value); tm1.commit(); asyncWait(); assert cache1.getTransactionTable().getNumGlobalTransactions() == 0 : "Cache 1 should have no stale global TXs"; assert cache1.getTransactionTable().getNumLocalTransactions() == 0 : "Cache 1 should have no stale local TXs"; assert cache2.getTransactionTable().getNumGlobalTransactions() == 0 : "Cache 2 should have no stale global TXs"; assert cache2.getTransactionTable().getNumLocalTransactions() == 0 : "Cache 2 should have no stale local TXs"; tm1.begin(); cache1.put(fqn2, key, value); cache1.putForExternalRead(fqn, key, value); tm1.commit(); asyncWait(); assert cache1.getTransactionTable().getNumGlobalTransactions() == 0 : "Cache 1 should have no stale global TXs"; assert cache1.getTransactionTable().getNumLocalTransactions() == 0 : "Cache 1 should have no stale local TXs"; assert cache2.getTransactionTable().getNumGlobalTransactions() == 0 : "Cache 2 should have no stale global TXs"; assert cache2.getTransactionTable().getNumLocalTransactions() == 0 : "Cache 2 should have no stale local TXs"; tm1.begin(); cache1.put(fqn2, key, value); cache1.putForExternalRead(fqn, key, value); cache1.put(fqn2, key, value); tm1.commit(); asyncWait(); assert cache1.getTransactionTable().getNumGlobalTransactions() == 0 : "Cache 1 should have no stale global TXs"; assert cache1.getTransactionTable().getNumLocalTransactions() == 0 : "Cache 1 should have no stale local TXs"; assert cache2.getTransactionTable().getNumGlobalTransactions() == 0 : "Cache 2 should have no stale global TXs"; assert cache2.getTransactionTable().getNumLocalTransactions() == 0 : "Cache 2 should have no stale local TXs"; } /** * Tests that setting a cacheModeLocal=true Option prevents propagation * of the putForExternalRead(). * * @throws Exception */ private void cacheModeLocalTest(boolean transactional) throws Exception { RPCManager rpcManager = EasyMock.createMock(RPCManager.class); RPCManager originalRpcManager = cache1.getConfiguration().getRuntimeConfig().getRPCManager(); // inject a mock RPC manager so that we can test whether calls made are sync or async. cache1.getConfiguration().getRuntimeConfig().setRPCManager(rpcManager); // specify that we expect nothing will be called on the mock Rpc Manager. replay(rpcManager); // now try a simple replication. Since the RPCManager is a mock object it will not actually replicate anything. if (transactional) tm1.begin(); cache1.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); cache1.putForExternalRead(fqn, key, value); if (transactional) tm1.commit(); verify(rpcManager); // cleanup cache1.getConfiguration().getRuntimeConfig().setRPCManager(originalRpcManager); cache1.removeNode(fqn); } protected void assertLocked(Fqn fqn, NodeSPI n, TransactionWorkspace workspace, boolean write_locked) throws Exception { // this needs to cater for "optimistically locked" nodes as well. if (workspace != null) { // scan workspaces for this node assertNotNull("node " + fqn + " should be in transaction workspace", workspace.getNode(fqn)); } else { NodeLock lock = n.getLock(); assertTrue("node " + fqn + " is not locked", lock.isLocked()); if (write_locked) { assertTrue("node " + fqn + " is not write-locked" + (lock.isReadLocked() ? " but is read-locked instead!" : "!"), lock.isWriteLocked()); } else { assertTrue("node " + fqn + " is not read-locked" + (lock.isWriteLocked() ? " but is write-locked instead!" : "!"), lock.isReadLocked()); } } } protected TransactionWorkspace extractTransactionWorkspace(Cache c) { CacheSPI cs = (CacheSPI) c; try { GlobalTransaction gtx = cs.getTransactionTable().get(cs.getTransactionManager().getTransaction()); OptimisticTransactionEntry entry = (OptimisticTransactionEntry) cs.getTransactionTable().get(gtx); return entry.getTransactionWorkSpace(); } catch (SystemException e) { e.printStackTrace(); fail("Unable to extract transaction workspace from cache"); } return null; } protected void asyncWait() { TestingUtil.sleepThread(500); } protected boolean isUsingInvalidation() { return cacheMode == CacheMode.INVALIDATION_ASYNC || cacheMode == CacheMode.INVALIDATION_SYNC; } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/api/CacheSPITest.java0000644000175000017500000000743411017455042026276 0ustar twernertwernerpackage org.jboss.cache.api; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.factories.UnitTestCacheConfigurationFactory; import org.jboss.cache.util.internals.ViewChangeListener; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.List; import java.util.concurrent.TimeUnit; @Test(groups = "functional") public class CacheSPITest { private CacheSPI cache1; private CacheSPI cache2; protected boolean optimistic = false; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { Configuration conf1 = UnitTestCacheConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC); Configuration conf2 = UnitTestCacheConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC); conf1.setNodeLockingScheme(optimistic ? Configuration.NodeLockingScheme.OPTIMISTIC : Configuration.NodeLockingScheme.PESSIMISTIC); conf2.setNodeLockingScheme(optimistic ? Configuration.NodeLockingScheme.OPTIMISTIC : Configuration.NodeLockingScheme.PESSIMISTIC); cache1 = (CacheSPI) new DefaultCacheFactory().createCache(conf1, false); cache2 = (CacheSPI) new DefaultCacheFactory().createCache(conf2, false); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { if (cache1 != null) { try { cache1.stop(); } catch (Exception e) { } } if (cache2 != null) { try { cache2.stop(); } catch (Exception e) { } } } public void testGetMembers() throws Exception { cache1.start(); List memb1 = cache1.getMembers(); assertEquals("View has one member", 1, memb1.size()); Object coord = memb1.get(0); cache2.start(); memb1 = cache1.getMembers(); TestingUtil.blockUntilViewsReceived(5000, false, cache1, cache2); List memb2 = cache2.getMembers(); assertEquals("View has two members", 2, memb1.size()); assertEquals("Both caches have same view", memb1, memb2); cache1.stop(); TestingUtil.blockUntilViewsReceived(5000, false, cache2); memb2 = cache2.getMembers(); assertEquals("View has one member", 1, memb2.size()); assertFalse("Coordinator changed", coord.equals(memb2.get(0))); } public void testIsCoordinator() throws Exception { Configuration conf1 = UnitTestCacheConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC); Configuration conf2 = UnitTestCacheConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC); cache1 = (CacheSPI) new DefaultCacheFactory().createCache(conf1, false); cache2 = (CacheSPI) new DefaultCacheFactory().createCache(conf2, false); cache1.start(); assertTrue("Cache1 is coordinator", cache1.getRPCManager().isCoordinator()); cache2.start(); assertTrue("Cache1 is still coordinator", cache1.getRPCManager().isCoordinator()); assertFalse("Cache2 is not coordinator", cache2.getRPCManager().isCoordinator()); ViewChangeListener viewChangeListener = new ViewChangeListener(cache2); cache1.stop(); // wait till cache2 gets the view change notification assert viewChangeListener.waitForViewChange(60, TimeUnit.SECONDS) : "Should have received a view change!"; assertTrue("Cache2 is coordinator", cache2.getRPCManager().isCoordinator()); } }jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/api/NodeAPITest.java0000644000175000017500000003341411012114603026121 0ustar twernertwernerpackage org.jboss.cache.api; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.Node; import org.jboss.cache.config.Configuration; import org.jboss.cache.interceptors.OptimisticNodeInterceptor; import org.jboss.cache.interceptors.PessimisticLockInterceptor; import org.jboss.cache.interceptors.base.CommandInterceptor; import org.jboss.cache.optimistic.TransactionWorkspace; import org.jboss.cache.transaction.OptimisticTransactionEntry; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.TransactionManager; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; /** * Tests {@link org.jboss.cache.Node}-centric operations * * @author Manik Surtani * @since 2.0.0 */ @Test(groups = "functional") public class NodeAPITest { private Node rootNode; private CacheSPI cache; private TransactionManager tm; private static final Fqn A = Fqn.fromString("/a"), B = Fqn.fromString("/b"), C = Fqn.fromString("/c"), D = Fqn .fromString("/d"); protected boolean optimistic = false; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { // start a single cache instance cache = (CacheSPI) new DefaultCacheFactory().createCache("META-INF/conf-test/local-tx-service.xml", false); cache.getConfiguration().setNodeLockingScheme(optimistic ? Configuration.NodeLockingScheme.OPTIMISTIC : Configuration.NodeLockingScheme.PESSIMISTIC); cache.start(); rootNode = cache.getRoot(); tm = cache.getTransactionManager(); } @AfterMethod(alwaysRun = true) public void tearDown() { if (cache != null) { if (cache.getTransactionManager() != null) { try { cache.getTransactionManager().rollback(); } catch (Exception e) { // don't care } } cache.stop(); } if (rootNode != null) { rootNode = null; } } private void assertOptimistic() { assert cache.getConfiguration().isNodeLockingOptimistic(); boolean interceptorChainOK = false; for (CommandInterceptor i : cache.getInterceptorChain()) { if (i instanceof PessimisticLockInterceptor) assert false : "Not an optimistic locking chain!!"; if (i instanceof OptimisticNodeInterceptor) interceptorChainOK = true; } assert interceptorChainOK : "Not an optimistic locking chain!!"; } public void testAddingData() { if (optimistic) assertOptimistic(); Node nodeA = rootNode.addChild(A); nodeA.put("key", "value"); assertEquals("value", nodeA.get("key")); } public void testAddingDataTx() throws Exception { tm.begin(); Node nodeA = rootNode.addChild(A); nodeA.put("key", "value"); assertEquals("value", nodeA.get("key")); tm.commit(); } public void testOverwritingDataTx() throws Exception { Node nodeA = rootNode.addChild(A); nodeA.put("key", "value"); assertEquals("value", nodeA.get("key")); tm.begin(); rootNode.removeChild(A); cache.put(A, "k2", "v2"); tm.commit(); assertNull(nodeA.get("key")); assertEquals("v2", nodeA.get("k2")); } /** * Remember, Fqns are relative!! */ public void testParentsAndChildren() { Node nodeA = rootNode.addChild(A); Node nodeB = nodeA.addChild(B); Node nodeC = nodeA.addChild(C); Node nodeD = rootNode.addChild(D); assertEquals(rootNode, nodeA.getParent()); assertEquals(nodeA, nodeB.getParent()); assertEquals(nodeA, nodeC.getParent()); assertEquals(rootNode, nodeD.getParent()); assertTrue(rootNode.hasChild(A)); assertFalse(rootNode.hasChild(B)); assertFalse(rootNode.hasChild(C)); assertTrue(rootNode.hasChild(D)); assertTrue(nodeA.hasChild(B)); assertTrue(nodeA.hasChild(C)); assertEquals(nodeA, rootNode.getChild(A)); assertEquals(nodeD, rootNode.getChild(D)); assertEquals(nodeB, nodeA.getChild(B)); assertEquals(nodeC, nodeA.getChild(C)); assertTrue(nodeA.getChildren().contains(nodeB)); assertTrue(nodeA.getChildren().contains(nodeC)); assertEquals(2, nodeA.getChildren().size()); assertTrue(rootNode.getChildren().contains(nodeA)); assertTrue(rootNode.getChildren().contains(nodeD)); assertEquals(2, rootNode.getChildren().size()); assertEquals(true, rootNode.removeChild(A)); assertFalse(rootNode.getChildren().contains(nodeA)); assertTrue(rootNode.getChildren().contains(nodeD)); assertEquals(1, rootNode.getChildren().size()); assertEquals("double remove", false, rootNode.removeChild(A)); assertEquals("double remove", false, rootNode.removeChild(A.getLastElement())); } public void testLocking() throws Exception { tm.begin(); Node nodeA = rootNode.addChild(A); Node nodeB = nodeA.addChild(B); Node nodeC = nodeB.addChild(C); if (!optimistic) { assertEquals(3, cache.getNumberOfNodes()); assertEquals(4, cache.getNumberOfLocksHeld()); } tm.commit(); tm.begin(); assertEquals(0, cache.getNumberOfLocksHeld()); nodeC.put("key", "value"); if (!optimistic) assertEquals(4, cache.getNumberOfLocksHeld()); tm.commit(); } public void testImmutabilityOfData() { rootNode.put("key", "value"); Map m = rootNode.getData(); try { m.put("x", "y"); fail("Map should be immutable!!"); } catch (Exception e) { // expected } try { rootNode.getKeys().add(new Object()); fail("Key set should be immutable"); } catch (Exception e) { // expected } } public void testDefensiveCopyOfData() { rootNode.put("key", "value"); Map data = rootNode.getData(); Set keys = rootNode.getKeys(); assert keys.size() == 1; assert keys.contains("key"); assert data.size() == 1; assert data.containsKey("key"); // now change stuff. rootNode.put("key2", "value2"); // assert that the collections we initially got have not changed. assert keys.size() == 1; assert keys.contains("key"); assert data.size() == 1; assert data.containsKey("key"); } public void testDefensiveCopyOfChildren() { Fqn childFqn = Fqn.fromString("/child"); rootNode.addChild(childFqn).put("k", "v"); Set> children = rootNode.getChildren(); Set childrenNames = rootNode.getChildrenNames(); assert childrenNames.size() == 1; assert childrenNames.contains(childFqn.getLastElement()); assert children.size() == 1; assert children.iterator().next().getFqn().equals(childFqn); // now change stuff. rootNode.addChild(Fqn.fromString("/child2")); // assert that the collections we initially got have not changed. assert childrenNames.size() == 1; assert childrenNames.contains(childFqn.getLastElement()); assert children.size() == 1; assert children.iterator().next().getFqn().equals(childFqn); } public void testImmutabilityOfChildren() { rootNode.addChild(A); try { rootNode.getChildren().clear(); fail("Collection of child nodes returned in getChildrenDirect() should be immutable"); } catch (Exception e) { // expected } } public void testGetChildrenUnderTx() throws Exception { Fqn A_B = Fqn.fromRelativeFqn(A, B); Fqn A_C = Fqn.fromRelativeFqn(A, C); tm.begin(); cache.put(A_B, "1", "1"); cache.put(A_C, "2", "2"); if (!optimistic) { assertEquals(3, cache.getNumberOfNodes()); assertEquals(4, cache.getNumberOfLocksHeld()); } else { TransactionWorkspace w = getTransactionWorkspace(); assert w.getNodes().size() == 4 : "Should be 4 nodes in the workspace, not " + w.getNodes().size(); // test deltas List> deltas = w.getNodes().get(Fqn.ROOT).getMergedChildren(); assert deltas.get(0).size() == 1 : "/ should have 1 child added"; assert deltas.get(1).size() == 0 : "/ should have 0 children removed"; deltas = w.getNodes().get(A).getMergedChildren(); assert deltas.get(0).size() == 2 : "/ should have 2 children added"; assert deltas.get(1).size() == 0 : "/ should have 0 children removed"; deltas = w.getNodes().get(A_B).getMergedChildren(); assert deltas.get(0).size() == 0 : "/a/b should have 0 children added"; assert deltas.get(1).size() == 0 : "/a/b should have 0 children removed"; deltas = w.getNodes().get(A_C).getMergedChildren(); assert deltas.get(0).size() == 0 : "/a/c should have 0 children added"; assert deltas.get(1).size() == 0 : "/a/c should have 0 children removed"; } assertEquals("Number of child", 2, cache.getRoot().getChild(A).getChildren().size()); tm.commit(); } @SuppressWarnings("unchecked") private TransactionWorkspace getTransactionWorkspace() throws Exception { return ((OptimisticTransactionEntry) cache.getTransactionTable().get(cache.getTransactionTable().get(tm.getTransaction()))).getTransactionWorkSpace(); } public void testGetChildAPI() { // creates a Node with fqn /a/b/c rootNode.addChild(A).addChild(B).addChild(C); rootNode.getChild(A).put("key", "value"); rootNode.getChild(A).getChild(B).put("key", "value"); rootNode.getChild(A).getChild(B).getChild(C).put("key", "value"); assertEquals("value", rootNode.getChild(A).get("key")); assertEquals("value", rootNode.getChild(A).getChild(B).get("key")); assertEquals("value", rootNode.getChild(A).getChild(B).getChild(C).get("key")); assertNull(rootNode.getChild(Fqn.fromElements("nonexistent"))); } public void testClearingData() { rootNode.put("k", "v"); rootNode.put("k2", "v2"); assertEquals(2, rootNode.getKeys().size()); rootNode.clearData(); assertEquals(0, rootNode.getKeys().size()); assertTrue(rootNode.getData().isEmpty()); } public void testClearingDataTx() throws Exception { tm.begin(); rootNode.put("k", "v"); rootNode.put("k2", "v2"); assertEquals(2, rootNode.getKeys().size()); rootNode.clearData(); assertEquals(0, rootNode.getKeys().size()); assertTrue(rootNode.getData().isEmpty()); tm.commit(); assertTrue(rootNode.getData().isEmpty()); } public void testPutData() { assertTrue(rootNode.getData().isEmpty()); Map map = new HashMap(); map.put("k1", "v1"); map.put("k2", "v2"); rootNode.putAll(map); assertEquals(2, rootNode.getData().size()); assertEquals("v1", rootNode.get("k1")); assertEquals("v2", rootNode.get("k2")); map.clear(); map.put("k3", "v3"); rootNode.putAll(map); assertEquals(3, rootNode.getData().size()); assertEquals("v1", rootNode.get("k1")); assertEquals("v2", rootNode.get("k2")); assertEquals("v3", rootNode.get("k3")); map.clear(); map.put("k4", "v4"); map.put("k5", "v5"); rootNode.replaceAll(map); assertEquals(2, rootNode.getData().size()); assertEquals("v4", rootNode.get("k4")); assertEquals("v5", rootNode.get("k5")); } public void testGetChildrenNames() throws Exception { rootNode.addChild(A).put("k", "v"); rootNode.addChild(B).put("k", "v"); Set childrenNames = new HashSet(); childrenNames.add(A.getLastElement()); childrenNames.add(B.getLastElement()); assertEquals(childrenNames, rootNode.getChildrenNames()); // now delete a child, within a tx tm.begin(); rootNode.removeChild(B); assertFalse(rootNode.hasChild(B)); childrenNames.remove(B.getLastElement()); assertEquals(childrenNames, rootNode.getChildrenNames()); tm.commit(); assertEquals(childrenNames, rootNode.getChildrenNames()); } public void testDoubleRemovalOfData() throws Exception { cache.put("/foo/1/2/3", "item", 1); tm.begin(); assertEquals(cache.get("/foo/1/2/3", "item"), 1); cache.removeNode("/foo/1"); assertNull(cache.getNode("/foo/1")); assertNull(cache.get("/foo/1", "item")); cache.removeNode("/foo/1/2/3"); assertNull(cache.get("/foo/1/2/3", "item")); assertNull(cache.get("/foo/1", "item")); tm.commit(); assertFalse(cache.exists("/foo/1")); assertNull(cache.get("/foo/1/2/3", "item")); assertNull(cache.get("/foo/1", "item")); } public void testDoubleRemovalOfData2() throws Exception { cache.put("/foo/1/2", "item", 1); tm.begin(); assertEquals(cache.get("/foo/1", "item"), null); cache.removeNode("/foo/1"); assertNull(cache.get("/foo/1", "item")); cache.removeNode("/foo/1/2"); assertNull(cache.get("/foo/1", "item")); tm.commit(); assertFalse(cache.exists("/foo/1")); assertNull(cache.get("/foo/1/2", "item")); assertNull(cache.get("/foo/1", "item")); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/mock/0000755000175000017500000000000011376173750023377 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/mock/MockNodesFixture.java0000644000175000017500000000433111013030101027436 0ustar twernertwernerpackage org.jboss.cache.mock; import org.jboss.cache.Fqn; /** * This class builds a tree of NodeSpiMocks that can be used through tests. *
 * It serves following purposes:
 * - having a common known structure through all the tests is useful for test redability
 * - not having to write again and again a new fixture for each test.
 * 
* Note: changing the fixture might cause tests depending on this class to fail, as the expect certain number of nodes, * number of child etc. * * @author Mircea.Markus@jboss.com * @since 2.2 */ public class MockNodesFixture { public NodeSpiMock root; public NodeSpiMock aNode; public Fqn a; public Fqn ab; public Fqn abc; public Fqn ad; public Fqn ade; public Fqn adf; public Fqn adfh; public Fqn adfg; public NodeSpiMock abNode; public NodeSpiMock abcNode; public NodeSpiMock adNode; public NodeSpiMock adeNode; public NodeSpiMock adfNode; public NodeSpiMock adfhNode; public NodeSpiMock adfgNode; public Fqn notExistent = Fqn.fromString("aaa" + System.currentTimeMillis()); public MockNodesFixture() { this(Fqn.ROOT); } /** * Will place the fqn prefix before all created nodes. */ public MockNodesFixture(Fqn prefix) { a = Fqn.fromRelativeElements(prefix, "a"); ab = Fqn.fromRelativeElements(prefix, "a", "b"); abc = Fqn.fromRelativeElements(prefix, "a", "b", "c"); ad = Fqn.fromRelativeElements(prefix, "a", "d"); ade = Fqn.fromRelativeElements(prefix, "a", "d", "e"); adf = Fqn.fromRelativeElements(prefix, "a", "d", "f"); adfh = Fqn.fromRelativeElements(prefix, "a", "d", "f", "h"); adfg = Fqn.fromRelativeElements(prefix, "a", "d", "f", "g"); root = new NodeSpiMock(Fqn.ROOT); aNode = (NodeSpiMock) root.addChild(a); abNode = (NodeSpiMock) root.addChild(ab); abcNode = (NodeSpiMock) root.addChild(abc); adNode = (NodeSpiMock) root.addChild(ad); adeNode = (NodeSpiMock) root.addChild(ade); adfNode = (NodeSpiMock) root.addChild(adf); adfhNode = (NodeSpiMock) root.addChild(adfh); adfgNode = (NodeSpiMock) root.addChild(adfg); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/mock/NodeSpiMock.java0000644000175000017500000002132211031017265026377 0ustar twernertwernerpackage org.jboss.cache.mock; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.Node; import org.jboss.cache.NodeSPI; import org.jboss.cache.lock.NodeLock; import org.jboss.cache.optimistic.DataVersion; import org.jboss.cache.transaction.GlobalTransaction; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; /** * @author Mircea.Markus@jboss.com * @since 2.2 */ public class NodeSpiMock implements NodeSPI { boolean isChildrenLoaded; boolean isDataLoaded; Map children = new HashMap(); boolean isDeleted = false; boolean isValid = true; DataVersion version = null; Map data = new HashMap(); NodeSpiMock parent; Fqn fqn; private boolean isResident = false; public NodeSpiMock(Fqn fqn) { this.fqn = fqn; } public boolean isChildrenLoaded() { return isChildrenLoaded; } public void setChildrenLoaded(boolean loaded) { this.isChildrenLoaded = loaded; } public boolean isDataLoaded() { return isDataLoaded; } public void setDataLoaded(boolean dataLoaded) { this.isDataLoaded = dataLoaded; } public Map getChildrenMapDirect() { return children; } public void setChildrenMapDirect(Map children) { this.children = new HashMap(children); } public NodeSPI getOrCreateChild(Object name, GlobalTransaction tx) { if (children.containsKey(name)) return children.get(name); NodeSpiMock child = newChild(name); return child; } private NodeSpiMock newChild(Object name) { NodeSpiMock child = new NodeSpiMock(Fqn.fromRelativeElements(fqn, name)); child.parent = this; children.put(name, child); return child; } public NodeLock getLock() { throw new UnsupportedOperationException(); } public void setFqn(Fqn f) { throw new UnsupportedOperationException(); } public boolean isDeleted() { return isDeleted; } public void markAsDeleted(boolean marker) { this.isDeleted = marker; } public void markAsDeleted(boolean marker, boolean recursive) { this.isDeleted = marker; if (recursive) { for (NodeSpiMock child : children.values()) { child.markAsDeleted(marker, true); } } } public void addChild(Object nodeName, Node nodeToAdd) { children.put(nodeName, (NodeSpiMock) nodeToAdd); ((NodeSpiMock) nodeToAdd).parent = this; ((NodeSpiMock) nodeToAdd).fqn = Fqn.fromRelativeElements(fqn, nodeName); } public void printDetails(StringBuilder sb, int indent) { //skip } public void print(StringBuilder sb, int indent) { //skip } public void setVersion(DataVersion version) { this.version = version; } public DataVersion getVersion() { return version; } public Set getChildrenDirect() { return new HashSet(children.values()); } public void removeChildrenDirect() { children.clear(); } public Set getChildrenDirect(boolean includeMarkedAsDeleted) { Set result = new HashSet(); for (NodeSpiMock child : children.values()) { if (!includeMarkedAsDeleted && child.isDeleted()) continue; result.add(child); } return result; } public NodeSPI getChildDirect(Object childName) { return children.get(childName); } public NodeSPI addChildDirect(Fqn childName) { if (childName.size() == 0) return this; Object directChildName = childName.get(0); NodeSpiMock directChild = children.get(directChildName); Fqn subFqn = childName.getSubFqn(1, childName.size()); if (directChild == null) { directChild = newChild(directChildName); } return directChild.addChildDirect(subFqn); } public NodeSPI addChildDirect(Fqn f, boolean notify) { return addChildDirect(f); } public NodeSPI addChildDirect(Object childName, boolean notify) { return newChild(childName); } public void addChildDirect(NodeSPI child) { throw new UnsupportedOperationException(); } public NodeSPI getChildDirect(Fqn childName) { return children.get(childName.getLastElement()); } public boolean removeChildDirect(Fqn fqn) { throw new UnsupportedOperationException(); } public boolean removeChildDirect(Object childName) { return children.remove(childName) != null; } public Object removeDirect(Object key) { return data.remove(key); } public Object putDirect(Object key, Object value) { return data.put(key, value); } public void putAllDirect(Map data) { this.data.putAll(data); } public Map getDataDirect() { return data; } public Object getDirect(Object key) { return data.get(key); } public void clearDataDirect() { data.clear(); } public Set getKeysDirect() { return data.keySet(); } public Set getChildrenNamesDirect() { return new HashSet(children.keySet()); } public CacheSPI getCache() { throw new UnsupportedOperationException(); } public NodeSPI getParent() { return parent; } public boolean hasChildrenDirect() { return !children.isEmpty(); } public Map getInternalState(boolean onlyInternalState) { throw new UnsupportedOperationException(); } public void setInternalState(Map state) { throw new UnsupportedOperationException(); } public void setValid(boolean valid, boolean recursive) { this.isValid = valid; if (recursive) { for (NodeSpiMock child : children.values()) { child.setValid(valid, true); child.isValid = valid; } } } public Set getChildren() { return getChildrenDirect(); } public Set getChildrenNames() { return getChildrenNamesDirect(); } public Map getData() { return getDataDirect(); } public Set getKeys() { return getKeysDirect(); } public Fqn getFqn() { return fqn; } public Node addChild(Fqn f) { return addChildDirect(f); } public boolean removeChild(Fqn f) { return removeChildDirect(f); } public boolean removeChild(Object childName) { return removeChildDirect(childName); } public Node getChild(Fqn f) { return getChildDirect(f); } public Node getChild(Object name) { return getChildDirect(name); } public Object put(Object key, Object value) { return putDirect(key, value); } public Object putIfAbsent(Object key, Object value) { if (data.containsKey(key)) return data.get(key); return data.put(key, value); } public Object replace(Object key, Object value) { return data.put(key, value); } public boolean replace(Object key, Object oldValue, Object newValue) { if (data.get(key).equals(oldValue)) { data.put(key, newValue); return true; } else return false; } public void putAll(Map map) { putAllDirect(map); } public void replaceAll(Map map) { data = map; } public Object get(Object key) { return getDirect(key); } public Object remove(Object key) { return removeDirect(key); } public void clearData() { clearDataDirect(); } public int dataSize() { return data.size(); } public boolean hasChild(Fqn f) { NodeSpiMock directChild = children.get(fqn.getLastElement()); return directChild != null && (fqn.size() == 1 || directChild.hasChild(f.getSubFqn(1, f.size()))); } public boolean hasChild(Object o) { return children.containsKey(o); } public boolean isValid() { return isValid; } public boolean isResident() { return isResident; } public void setResident(boolean resident) { this.isResident = resident; } public boolean isLockForChildInsertRemove() { return false; } public void setLockForChildInsertRemove(boolean lockForChildInsertRemove) { } public void releaseObjectReferences(boolean recursive) { } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/TreeNodeTest.java0000644000175000017500000000254210777645574025675 0ustar twernertwernerpackage org.jboss.cache; import static org.testng.AssertJUnit.assertFalse; import static org.testng.AssertJUnit.assertTrue; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * Tests restart (stop-destroy-create-start) of CacheSPI * * @author Bela Ban * @version $Id: TreeNodeTest.java 5530 2008-04-11 11:29:00Z manik.surtani@jboss.com $ */ @Test(groups = {"functional"}) public class TreeNodeTest { CacheSPI cache; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { cache = (CacheSPI) new DefaultCacheFactory().createCache(); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { cache.stop(); cache.destroy(); } public void testChildExist() throws Exception { Object key = 1; cache.put(Fqn.fromString("/a/b/c"), key, "test"); Node node = cache.getNode(Fqn.fromString("/a/b")); assertFalse(node.getChildren().isEmpty()); assertTrue(node.getChild(Fqn.fromElements("c")) != null); Fqn fqn = Fqn.fromString("/e/f"); cache.put(fqn, "1", "1"); node = cache.getNode(Fqn.fromString("/e")); assertFalse(node.getChildren().isEmpty()); assertTrue(node.getChild(Fqn.fromElements("f")) != null); } }jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/passivation/0000755000175000017500000000000011376173743025010 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/passivation/BasicPassivationTest.java0000644000175000017500000001063511017455042031745 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.passivation; import org.jboss.cache.CacheFactory; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.factories.XmlConfigurationParser; import org.jboss.cache.loader.DummyInMemoryCacheLoader; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.notifications.annotation.CacheListener; import org.jboss.cache.notifications.annotation.NodeActivated; import org.jboss.cache.notifications.annotation.NodePassivated; import org.jboss.cache.notifications.event.Event; import org.jboss.cache.notifications.event.NodeEvent; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * @author Ben Wang * @version $Revision: 5906 $ */ @Test(groups = {"functional"}) public class BasicPassivationTest { CacheSPI cache; int wakeupIntervalMillis_ = 0; final String ROOT_STR = "/test"; Throwable t1_ex, t2_ex; final long DURATION = 10000; boolean isTrue; final String FQNSTR = "/org/jboss/3"; int activationCount = 0; int passivationCount = 0; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { initCaches(); wakeupIntervalMillis_ = cache.getConfiguration().getEvictionConfig().getWakeupIntervalSeconds() * 1000; log("wakeupInterval is " + wakeupIntervalMillis_); if (wakeupIntervalMillis_ < 0) { fail("testEviction(): eviction thread wake up interval is illegal " + wakeupIntervalMillis_); } t1_ex = t2_ex = null; isTrue = true; } private void initCaches() { CacheFactory instance = new DefaultCacheFactory(); cache = (CacheSPI) instance.createCache(new XmlConfigurationParser().parseFile("META-INF/conf-test/local-passivation-service.xml"), false); cache.getConfiguration().setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); Object listener = new TestCacheListener(); cache.getConfiguration().getCacheLoaderConfig().getFirstCacheLoaderConfig().setClassName(DummyInMemoryCacheLoader.class.getName()); cache.start(); cache.getNotifier().addCacheListener(listener); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { cache.stop(); } public void testBasic() { activationCount = 0; passivationCount = 0; cache.put(FQNSTR, FQNSTR, FQNSTR); System.out.println(cache.toString()); TestingUtil.sleepThread(2100); System.out.println(cache.toString()); assert !(cache.exists(FQNSTR) && cache.getNode(FQNSTR).getKeys().contains(FQNSTR)) : "Should have been evicted!!"; Object val = cache.get(FQNSTR, FQNSTR); assertNotNull("DataNode should not be empty ", val); assertEquals("activation count:", 1, activationCount); assertEquals("passivation count:", 1, passivationCount); } public void testDualPassivation() throws Exception { Fqn fqn = Fqn.fromString(FQNSTR); cache.put(fqn, "key", "value"); cache.evict(fqn); cache.evict(fqn); assertEquals("Proper value after 2 passivations", "value", cache.get(fqn, "key")); } public void testIntermingledPassivation() throws Exception { Fqn fqn = Fqn.fromString(FQNSTR); cache.put(fqn, "key1", "value"); cache.evict(fqn); cache.put(fqn, "key2", "value"); cache.evict(fqn); assertEquals("Proper value after 2 passivations", "value", cache.get(fqn, "key1")); } private void log(String msg) { System.out.println("-- " + msg); } @CacheListener public class TestCacheListener { @SuppressWarnings("unchecked") @NodeActivated @NodePassivated public void callback(NodeEvent ne) { if (ne.isPre()) return;// we are not interested in postActivate event if (!ne.getFqn().isChildOrEquals(Fqn.fromString(FQNSTR))) return;// don't care about fqn that doesn't belong to me. log("got event " + ne); if (ne.getType() == Event.Type.NODE_ACTIVATED) activationCount++; else if (ne.getType() == Event.Type.NODE_PASSIVATED) passivationCount++; } } } ././@LongLink0000000000000000000000000000015700000000000011570 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/passivation/PassivationActivationCallbacksTestCase.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/passivation/PassivationActivationCallbacksTes0000644000175000017500000001651711005362317033525 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.passivation; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.CacheException; import org.jboss.cache.CacheFactory; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.RegionManager; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.config.EvictionConfig; import org.jboss.cache.config.EvictionRegionConfig; import org.jboss.cache.eviction.LRUConfiguration; import org.jboss.cache.loader.CacheLoader; import org.jboss.cache.loader.DummyInMemoryCacheLoader; import org.jboss.cache.lock.IsolationLevel; import org.jboss.cache.notifications.annotation.NodeActivated; import org.jboss.cache.notifications.annotation.NodePassivated; import org.jboss.cache.notifications.event.NodeEvent; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; /** * Tests that the TreeCacheListener implementation used by EJB3 SFSBs works. * * @author Brian Stansberry */ @Test(groups = {"functional"}) public class PassivationActivationCallbacksTestCase { private static final Fqn BASE = Fqn.fromString("/base"); private static final Log log = LogFactory.getLog(PassivationActivationCallbacksTestCase.class); //Cache Loader fields private CacheSPI cache; private CacheLoader loader = null; private CacheListener listener = null; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { log.debug(""); CacheFactory instance = new DefaultCacheFactory(); cache = (CacheSPI) instance.createCache(false); cache.getConfiguration().setCacheMode("local"); configureEviction(); configureCacheLoader(); cache.getConfiguration().setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); cache.getConfiguration().setIsolationLevel(IsolationLevel.SERIALIZABLE); listener = new CacheListener(); cache.addCacheListener(listener); cache.create(); cache.start(); loader = cache.getCacheLoaderManager().getCacheLoader(); } protected void configureCacheLoader() throws Exception { CacheLoaderConfig clc = new CacheLoaderConfig(); clc.setPassivation(true); clc.setShared(false); clc.setPreload("/"); CacheLoaderConfig.IndividualCacheLoaderConfig dummyConfig = new CacheLoaderConfig.IndividualCacheLoaderConfig(); dummyConfig.setAsync(false); dummyConfig.setFetchPersistentState(true); dummyConfig.setIgnoreModifications(false); dummyConfig.setClassName(DummyInMemoryCacheLoader.class.getName()); clc.addIndividualCacheLoaderConfig(dummyConfig); cache.getConfiguration().setCacheLoaderConfig(clc); } protected void configureEviction() throws Exception { EvictionConfig ec = new EvictionConfig(); ec.setWakeupIntervalSeconds(1); List ercs = new ArrayList(); EvictionRegionConfig erc = new EvictionRegionConfig(); erc.setRegionFqn(RegionManager.DEFAULT_REGION); LRUConfiguration epc = new LRUConfiguration(); epc.setMaxNodes(0); epc.setTimeToLiveSeconds(5); erc.setEvictionPolicyConfig(epc); ercs.add(erc); erc = new EvictionRegionConfig(); erc.setRegionFqn(BASE); epc = new LRUConfiguration(); epc.setMaxNodes(0); epc.setTimeToLiveSeconds(1); erc.setEvictionPolicyConfig(epc); ercs.add(erc); ec.setEvictionRegionConfigs(ercs); cache.getConfiguration().setEvictionConfig(ec); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { cache.removeNode(Fqn.ROOT); loader.remove(Fqn.fromString("/")); cache.stop(); cache.destroy(); } public void testSimpleLifecycle() throws Exception { Fqn fqn = Fqn.fromRelativeElements(BASE, "bean1"); cache.put(fqn, "bean", "A bean"); //TestingUtil.sleepThread(3000); cache.evict(fqn, false); assertNull("No activation exception", listener.activationException); assertNull("No passivation exception", listener.passivationException); assertTrue(listener.passivated.contains(fqn)); assertFalse(listener.activated.contains(fqn)); Object obj = cache.get(fqn, "bean"); assertEquals("Got bean", "A bean", obj); assertNull("No activation exception", listener.activationException); assertNull("No passivation exception", listener.passivationException); assertTrue(listener.activated.contains(fqn)); } /** * Mimics the CacheListener used by EJB3 SFSBs. */ @org.jboss.cache.notifications.annotation.CacheListener public class CacheListener { protected Log log = LogFactory.getLog(CacheListener.class); protected Set> passivated = new HashSet>(); protected Set> activated = new HashSet>(); protected Exception passivationException; protected Exception activationException; @SuppressWarnings("unchecked") @NodeActivated public void nodeActivated(NodeEvent e) { if (e.isPre()) return; // we are not interested in preActivate event if (!e.getFqn().isChildOrEquals(BASE)) return; // don't care about fqn that doesn't belong to me. Object bean = null; try { bean = cache.get(e.getFqn(), "bean"); } catch (CacheException ex) { log.error("nodeActivate(): can't retrieve bean instance from: " + e.getFqn() + " with exception: " + ex); activationException = ex; return; } if (bean == null) { activationException = new IllegalStateException("nodeActivate(): null bean instance."); throw (IllegalStateException) activationException; } if (log.isTraceEnabled()) { log.trace("nodeActivate(): saw postActivate event on fqn: " + e.getFqn()); } activated.add(e.getFqn()); } @SuppressWarnings("unchecked") @NodePassivated public void nodePassivated(NodeEvent e) { if (!e.isPre()) return; // we are not interested in postPassivate event Fqn fqn = e.getFqn(); if (!fqn.isChildOrEquals(BASE)) return; // don't care about fqn that doesn't belong to me. try { Object bean = cache.get(fqn, "bean"); if (bean != null) { if (log.isTraceEnabled()) { log.trace("nodePassivate(): send prePassivate event on fqn: " + fqn); } passivated.add(fqn); } } catch (CacheException ex) { log.error("nodePassivate(): can't retrieve bean instance from: " + fqn + " with exception: " + ex); passivationException = ex; return; } } } } ././@LongLink0000000000000000000000000000015100000000000011562 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/passivation/PassivationToFileCacheLoaderTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/passivation/PassivationToFileCacheLoaderTest.0000644000175000017500000000402610665031725033322 0ustar twernertwernerpackage org.jboss.cache.passivation; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.factories.XmlConfigurationParser; import org.jboss.cache.xml.XmlHelper; import org.w3c.dom.Element; /** * tests passivation using file cache loader * * @author {Hany Mesha} * @version $Id: PassivationToFileCacheLoaderTest.java 4444 2007-08-28 14:40:21Z jason.greene@jboss.com $ */ public class PassivationToFileCacheLoaderTest extends PassivationTestsBase { protected void configureCache() throws Exception { String tmp_location = null; String OS = System.getProperty("os.name").toLowerCase(); if (OS.contains("win") || OS.contains("nt")) { tmp_location = System.getProperty("java.io.tmpdir", "c:\\tmp"); } else { tmp_location = System.getProperty("jva.io.tmpdir", "/tmp"); } cache.getConfiguration().setCacheLoaderConfig(getCacheLoaderConfig(tmp_location)); } protected CacheLoaderConfig getCacheLoaderConfig(String loc) throws Exception { String xml = " \n" + " \n" + " true\n" + " \n" + "\n" + " \n" + " org.jboss.cache.loader.FileCacheLoader\n" + " \n" + " \n" + " false\n" + " false\n" + " false\n" + " \n" + " \n" + " "; Element element = XmlHelper.stringToElement(xml); return XmlConfigurationParser.parseCacheLoaderConfig(element); } } ././@LongLink0000000000000000000000000000015200000000000011563 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/passivation/PassivationToBdbjeCacheLoaderTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/passivation/PassivationToBdbjeCacheLoaderTest0000644000175000017500000000522610732771734033404 0ustar twernertwernerpackage org.jboss.cache.passivation; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.factories.XmlConfigurationParser; import org.jboss.cache.xml.XmlHelper; import org.testng.annotations.Test; import org.w3c.dom.Element; import java.io.File; import java.io.FileFilter; /** * Runs the same tests as {@link PassivationToFileCacheLoaderTest}, but with * Berkeley DB instead of a file-based CacheLoader * * @author {Hany Mesha} * @version $Id: PassivationToBdbjeCacheLoaderTest.java 4915 2007-12-21 17:11:56Z manik.surtani@jboss.com $ */ @Test(groups = "functional") public class PassivationToBdbjeCacheLoaderTest extends PassivationTestsBase { private String tmp_location = System.getProperty("java.io.tmpdir", "c:\\tmp"); private File dir = new File(tmp_location); public PassivationToBdbjeCacheLoaderTest() { if (!dir.exists()) dir.mkdirs(); } protected void configureCache() throws Exception { class MyFilter implements FileFilter { public boolean accept(File file) { return file.getName().endsWith(".jdb"); } } File[] files = dir.listFiles(new MyFilter()); if (files != null) { for (int i = 0; i < files.length; i += 1) { File file = files[i]; if (file.isFile()) { if (!file.delete()) { System.err.println("Unable to delete: " + file); } } } } cache.getConfiguration().setCacheLoaderConfig(getCacheLoaderConfig(tmp_location)); } protected CacheLoaderConfig getCacheLoaderConfig(String loc) throws Exception { String xml = " \n" + " \n" + " true\n" + " \n" + "\n" + " \n" + " org.jboss.cache.loader.bdbje.BdbjeCacheLoader\n" + " \n" + " \n" + " false\n" + " false\n" + " false\n" + " \n" + " \n" + " "; Element element = XmlHelper.stringToElement(xml); return XmlConfigurationParser.parseCacheLoaderConfig(element); } } ././@LongLink0000000000000000000000000000015100000000000011562 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/passivation/PassivationToJDBCCacheLoaderTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/passivation/PassivationToJDBCCacheLoaderTest.0000644000175000017500000000530310727545014033144 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.passivation; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.factories.XmlConfigurationParser; import org.jboss.cache.xml.XmlHelper; import org.w3c.dom.Element; import java.util.Properties; /** * Tests passivation using JDBC Cache Loader. * This test has MySQL hard-coded. To run it, run MySQL first: mysqld -u=root * * @author {Hany Mesha} * @version $Id: PassivationToJDBCCacheLoaderTest.java 4836 2007-12-11 17:34:04Z manik.surtani@jboss.com $ */ public class PassivationToJDBCCacheLoaderTest extends PassivationTestsBase { protected CacheLoaderConfig getCacheLoaderConfig() throws Exception { String xml = " \n" + " \n" + " true\n" + " \n" + "\n" + " \n" + " org.jboss.cache.loader.JDBCCacheLoader\n" + " \n" + getJDBCProps() + " \n" + " false\n" + " false\n" + " false\n" + " \n" + " \n" + " "; Element element = XmlHelper.stringToElement(xml); return XmlConfigurationParser.parseCacheLoaderConfig(element); } protected String getJDBCProps() { Properties prop = new Properties(); try { prop.load(this.getClass().getClassLoader().getResourceAsStream("cache-jdbc.properties")); } catch (Exception e) { System.out.println("Error loading jdbc properties "); } return "cache.jdbc.driver =" + prop.getProperty("cache.jdbc.driver") + "\n" + "cache.jdbc.url=" + prop.getProperty("cache.jdbc.url") + "\n" + "cache.jdbc.user=" + prop.getProperty("cache.jdbc.user") + "\n" + "cache.jdbc.password=" + prop.getProperty("cache.jdbc.password") + "\n" + "cache.jdbc.node.type=" + prop.getProperty("cache.jdbc.node.type") + "\n" + "cache.jdbc.sql-concat=" + prop.getProperty("cache.jdbc.sql-concat"); } protected void configureCache() throws Exception { cache.getConfiguration().setCacheLoaderConfig(getCacheLoaderConfig()); } } ././@LongLink0000000000000000000000000000015000000000000011561 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/passivation/LocalPassivationIntegrationTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/passivation/LocalPassivationIntegrationTest.j0000644000175000017500000001034711017455042033472 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.passivation; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.CacheFactory; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.factories.XmlConfigurationParser; import org.jboss.cache.loader.DummyInMemoryCacheLoader; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.notifications.annotation.CacheListener; import org.jboss.cache.notifications.annotation.NodeActivated; import org.jboss.cache.notifications.annotation.NodeLoaded; import org.jboss.cache.notifications.annotation.NodePassivated; import org.jboss.cache.notifications.event.Event; import org.jboss.cache.notifications.event.NodeEvent; import org.jboss.cache.transaction.DummyTransactionManagerLookup; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * @author Ben Wang, Feb 11, 2004 */ @Test(groups = {"functional"}) public class LocalPassivationIntegrationTest { CacheSPI cache; protected final static Log log = LogFactory.getLog(LocalPassivationIntegrationTest.class); int wakeupIntervalMillis_ = 0; PassivationListener listener_; private static final int LISTENER_WAIT_TIME = 200; // needed since notifications are delivered asynchronously @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { CacheFactory instance = new DefaultCacheFactory(); cache = (CacheSPI) instance.createCache(new XmlConfigurationParser().parseFile("META-INF/conf-test/local-passivation-service.xml"), false); cache.getConfiguration().setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName()); cache.getConfiguration().getCacheLoaderConfig().getFirstCacheLoaderConfig().setClassName(DummyInMemoryCacheLoader.class.getName()); cache.getConfiguration().setUseRegionBasedMarshalling(true); cache.start(); listener_ = new PassivationListener(); cache.getNotifier().addCacheListener(listener_); listener_.resetCounter(); wakeupIntervalMillis_ = cache.getConfiguration().getEvictionConfig().getWakeupIntervalSeconds() * 1000; log("wakeupInterval is " + wakeupIntervalMillis_); if (wakeupIntervalMillis_ <= 0) { fail("testEviction(): eviction thread wake up interval is illegal " + wakeupIntervalMillis_); } } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { cache.stop(); } /** */ public void testActivationEvent() throws Exception { String rootStr = "/org/jboss/test/data/"; String str = rootStr + "0"; cache.removeNode(Fqn.ROOT); listener_.resetCounter(); cache.put(str, str, str); TestingUtil.sleepThread(20000); assertFalse("UnversionedNode should not exist", cache.exists(str)); String val = cache.get(str, str); assertNotNull("DataNode should be activated ", val); TestingUtil.sleepThread(LISTENER_WAIT_TIME); assertEquals("Eviction counter ", 1, listener_.getCounter()); } private void log(String msg) { System.out.println("-- " + msg); } @CacheListener public class PassivationListener { int counter = 0; int loadedCounter = 0; public int getCounter() { return counter; } public void resetCounter() { counter = 0; loadedCounter = 0; } @NodeActivated public void nodeActivated(NodeEvent ne) { if (!ne.isPre()) { counter++; System.out.println("nodeActivate(): counter: " + counter); } } @NodePassivated public void nodePassivated(NodeEvent ne) { if (ne.isPre()) { System.out.println("nodePassivate(): " + ne.getFqn()); } } @NodeLoaded public void nodeLoaded(Event e) { if (!e.isPre()) { loadedCounter++; } } } } ././@LongLink0000000000000000000000000000016400000000000011566 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/passivation/PassivationToLocalDelegatingCacheLoaderTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/passivation/PassivationToLocalDelegatingCache0000644000175000017500000000357710741132571033422 0ustar twernertwernerpackage org.jboss.cache.passivation; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.config.Configuration; import org.jboss.cache.loader.CacheLoader; import org.jboss.cache.loader.LocalDelegatingCacheLoaderConfig; import org.testng.annotations.AfterMethod; import org.testng.annotations.Test; /** * Runs a test against using delegated cache loader * * @author {Hany Mesha} * @version $Id: PassivationToLocalDelegatingCacheLoaderTest.java 5103 2008-01-09 11:52:25Z manik.surtani@jboss.com $ */ @Test(groups = "functional") public class PassivationToLocalDelegatingCacheLoaderTest extends PassivationTestsBase { CacheSPI delegating_cache; CacheLoader cache_loader; protected void configureCache() throws Exception { delegating_cache = (CacheSPI) new DefaultCacheFactory().createCache(false); delegating_cache.getConfiguration().setCacheMode(Configuration.CacheMode.LOCAL); delegating_cache.create(); delegating_cache.start(); LocalDelegatingCacheLoaderConfig cfg = new LocalDelegatingCacheLoaderConfig(); cfg.setDelegate(delegating_cache); cfg.setAsync(false); cfg.setFetchPersistentState(false); CacheLoaderConfig cacheLoaderConfig = new CacheLoaderConfig(); cacheLoaderConfig.addIndividualCacheLoaderConfig(cfg); cacheLoaderConfig.setPassivation(true); cache.getConfiguration().setCacheLoaderConfig(cacheLoaderConfig); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { super.tearDown(); delegating_cache.stop(); delegating_cache.destroy(); } public void testLoadAndStore() throws Exception { //TODO intentional overload since this test does not pass //http://jira.jboss.com/jira/browse/JBCACHE-851 } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/passivation/PassivationTestsBase.java0000644000175000017500000015205111074466057031773 0ustar twernertwernerpackage org.jboss.cache.passivation; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.CacheException; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.Modification; import org.jboss.cache.Node; import org.jboss.cache.NodeSPI; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.factories.XmlConfigurationParser; import org.jboss.cache.loader.CacheLoader; import org.jboss.cache.loader.SamplePojo; import org.jboss.cache.lock.IsolationLevel; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.statetransfer.StateTransferManager; import org.jboss.cache.transaction.DummyTransactionManager; import org.jboss.cache.xml.XmlHelper; import org.jboss.util.stream.MarshalledValueInputStream; import org.jboss.util.stream.MarshalledValueOutputStream; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import org.w3c.dom.Element; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; /** * Base tests for passivation using any of the cache loaders * * @author {Hany Mesha} * @version $Id: PassivationTestsBase.java 6904 2008-10-12 21:29:19Z bstansberry@jboss.com $ */ @Test(groups = "functional") abstract public class PassivationTestsBase { Log log = LogFactory.getLog(getClass()); //Cache Loader fields CacheSPI cache; CacheLoader loader = null; static final Fqn FQN = Fqn.fromString("/key"); protected CacheLoaderConfig getCacheLoaderConfig(String preload, String cacheloaderClass, String properties, boolean async, boolean fetchPersistentState) throws Exception { String xml = "\n" + "true\n" + "" + preload + "\n" + "\n" + "" + cacheloaderClass + "\n" + "" + properties + "\n" + "" + async + "\n" + "" + fetchPersistentState + "\n" + "\n" + ""; Element element = XmlHelper.stringToElement(xml); return XmlConfigurationParser.parseCacheLoaderConfig(element); } @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { cache = (CacheSPI) new DefaultCacheFactory().createCache(false); cache.getConfiguration().setCacheMode("local"); configureCache(); cache.getConfiguration().setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); cache.getConfiguration().setIsolationLevel(IsolationLevel.SERIALIZABLE); cache.create(); cache.start(); loader = cache.getCacheLoaderManager().getCacheLoader(); } abstract protected void configureCache() throws Exception; @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { loader.remove(Fqn.ROOT); TestingUtil.killCaches(cache); } protected void addDelay() { // returns immediately in this case. Subclasses may override where a delay is needed. } /** * Helper method to test the existence of a key * * @param fqn * @param key */ protected boolean exists(String fqn, String key) { NodeSPI n = cache.peek(Fqn.fromString(fqn), false, false); if (key == null) return n != null; return n != null && n.getKeysDirect().contains(key); } protected boolean exists(String fqn) { return exists(fqn, null); } public void testPutPassivation() throws Exception { final Fqn NODE = Fqn.fromString("/test"); final String KEY = "key"; Object retval = null; cache.removeNode(NODE);// nothing to remove addDelay(); retval = cache.put(NODE, KEY, 10);// put in memory assertNull(retval); retval = cache.put(NODE, KEY, 20);// put in memory addDelay(); assertEquals(10, retval);// get from memory cache.evict(NODE, true);// passivate node addDelay(); log.debug("______________"); retval = cache.put(NODE, KEY, 30);// activate node then does put in memory assertFalse(loader.exists(NODE)); assertEquals(20, retval); } public void testPut2Passivation() throws CacheException { final Fqn NODE = Fqn.fromString("/a/b/c"); final String KEY = "key"; Object retval; cache.removeNode(NODE);// nothing to remove addDelay(); retval = cache.put(NODE, KEY, 10);// put in memory assertNull(retval); addDelay(); retval = cache.put(NODE, KEY, 20);// put in memory assertEquals(10, retval); cache.evict(NODE, true);// passivate node cache.evict(Fqn.fromString("/a/b"), true);// passivate parent node cache.evict(Fqn.fromString("/a"), true);// passivate parent node addDelay(); try { assertTrue(loader.exists(Fqn.fromString("/a/b/c"))); } catch (Exception e) { fail(e.toString()); } retval = cache.put(NODE, KEY, 30);// activate node, put in memory new value try { assertFalse(loader.exists(NODE)); } catch (Exception e) { fail(e.toString()); } assertEquals(20, retval); } public void testSerializationPassivation() throws CacheException { Fqn fqn = Fqn.fromString("/mypojo"); SamplePojo pojo = new SamplePojo(39, "Hany"); pojo.getHobbies().add("Running"); pojo.getHobbies().add("Beerathlon"); pojo.getHobbies().add("Triathlon"); cache.put(fqn, 322649, pojo);// put in memory addDelay(); assertNotNull(cache.get(fqn, 322649));// get from memory cache.evict(fqn, false);// passivate node try { assertTrue(loader.exists(fqn)); } catch (Exception e) { fail(e.toString()); } SamplePojo pojo2 = (SamplePojo) cache.get(fqn, 322649);// activate node try { assertFalse(loader.exists(fqn)); } catch (Exception e) { fail(e.toString()); } assertNotNull(pojo2); assertEquals(39, pojo2.getAge()); assertEquals("Hany", pojo2.getName()); assertEquals(3, pojo2.getHobbies().size()); } /** * Just adds some data that wil be later retrieved. This test has to be run first */ public void testPopulate() { try { Map m = new HashMap(); for (int i = 0; i < 10; i++) { m.put("key" + i, "val" + i); } cache.put("/a/b/c", m); // force preloading this node from the cache loader. cache.getCacheLoaderManager().preload(Fqn.fromString("/1/2/3/4/5"), true, true); cache.put("/1/2/3/4/5", null); cache.put("/1/2/3/4/5/a", null); cache.put("/1/2/3/4/5/b", null); cache.put("/1/2/3/4/5/c", null); cache.put("/1/2/3/4/5/d", null); cache.put("/1/2/3/4/5/e", null); cache.put("/1/2/3/4/5/d/one", null); cache.put("/1/2/3/4/5/d/two", null); cache.put("/1/2/3/4/5/d/three", null); // cache.put("/a/b/c", "newKey", "newValue"); System.out.println("cache: " + cache); assert (exists("/1/2/3/4")); assert (exists("/a/b/c")); assert (!exists("/a/b/c/d")); } catch (Exception e) { fail(e.toString()); } } public void testPreloadingPassivation() throws Exception { cache.removeNode(Fqn.ROOT);// remove nothing cache.put("1/2/3/4/5/d", "key", "val");// put in memory cache.evict(Fqn.fromString("1/2/3/4/5/d"));// passivate node System.out.println("-- checking for 1/2/3/4/5/d"); addDelay(); try { assertTrue(loader.exists(Fqn.fromString("1/2/3/4/5/d"))); } catch (Exception e) { fail(e.toString()); } cache.getNode("1/2/3/4/5/d");// get from loader but doesn't load attributes assertEquals(true, loader.exists(Fqn.fromString("1/2/3/4/5/d"))); assert (exists("1/2/3/4/5/d")); System.out.println("-- 1/2/3/4/5/d exists"); cache.get("1/2/3/4/5/d", "key");// activate node assertEquals(false, loader.exists(Fqn.fromString("1/2/3/4/5/d"))); } public void testCacheLoading2() throws Exception { Set keys = null; cache.put("/a/b/c", "key", "val"); keys = cache.getNode(Fqn.fromString("/a/b/c")).getKeys(); assertNotNull(keys); assertEquals(1, keys.size()); } public void testExists() throws Exception { cache.put("/eins/zwei/drei", "key1", "val1"); assert (exists("/eins/zwei/drei")); assert (exists("/eins/zwei/drei", "key1")); assert (!exists("/eins/zwei/drei", "key2")); assert (!exists("/uno/due/tre")); assert (!exists("/une/due/tre", "key1")); } public void testGetChildren() throws Exception { cache.put("/d/one", null); cache.put("/d/two", null); cache.put("/d/three", null); cache.getNode("/d"); Set children = cache.getNode("/d").getChildrenNames(); assertNotNull(children); assertEquals(3, children.size()); assertTrue(children.contains("one")); assertTrue(children.contains("two")); assertTrue(children.contains("three")); } public void testGetChildrenWithEvictionPassivation() throws Exception { cache.put("/a/b/c/1", null); cache.put("/a/b/c/2", null); cache.put("/a/b/c/3", null); cache.evict(Fqn.fromString("/a/b/c/1"));// passivate node cache.evict(Fqn.fromString("/a/b/c/2"));// passivate node cache.evict(Fqn.fromString("/a/b/c/3"));// passivate node cache.evict(Fqn.fromString("/a/b/c"));// passivate node cache.evict(Fqn.fromString("/a/b"));// passivate node cache.evict(Fqn.fromString("/a"));// passivate node cache.evict(Fqn.fromString("/"));// passivate node addDelay(); Set children = cache.getNode("/a/b/c").getChildrenNames();// load node children names assertNotNull(children); assertEquals(3, children.size()); assertTrue(children.contains("1")); assertTrue(children.contains("2")); assertTrue(children.contains("3")); assertTrue(loader.exists(Fqn.fromString("/a/b/c"))); cache.get("/a/b/c/1", "test");// load child cache.get("/a/b/c/2", "test");// load child cache.get("/a/b/c/3", "test");// load child cache.get("/a/b/c", "test");// load attributes assertFalse(loader.exists(Fqn.fromString("/a/b/c/1"))); assertFalse(loader.exists(Fqn.fromString("/a/b/c/2"))); assertFalse(loader.exists(Fqn.fromString("/a/b/c/3"))); assertFalse(loader.exists(Fqn.fromString("/a/b/c"))); } public void testGetChildren2() { try { cache.put("/1", null); cache.put("a", null); Set children = cache.getRoot().getChildrenNames();// get root node children names assertNotNull(children); assertEquals(2, children.size()); assertTrue(children.contains("1")); assertTrue(children.contains("a")); } catch (Exception e) { fail(e.toString()); } } public void testGetChildren3() { try { cache.put("/1", null); cache.put("a", null); Set children = cache.getRoot().getChildrenNames();// get children from root node assertNotNull(children); assertEquals(2, children.size()); assertTrue(children.contains("1")); assertTrue(children.contains("a")); } catch (Exception e) { fail(e.toString()); } } public void testGetChildren4() { try { if (!cache.exists("/a/b/c")) { cache.put("/a/b/c", null); } Set children = cache.getChildrenNames((Fqn) null);// get "null* node children names assertTrue(children.isEmpty()); } catch (Exception e) { fail(e.toString()); } } public void testGetChildren5() { try { cache.put("/a/1", null); cache.put("/a/2", null); cache.put("/a/3", null); Node n = cache.getNode("/a"); assertNotNull(n); Set children = n.getChildrenNames(); assertNotNull(children); assertEquals(3, children.size()); } catch (Exception e) { fail(e.toString()); } } public void testGetChildren6Passivation() throws Exception { cache.put("/a/1", null);// put node in memory cache.put("/a/2", null);// put node in memory cache.put("/a/3", null);// put node in memory cache.evict(Fqn.fromString("/a/1"));// passivate node cache.evict(Fqn.fromString("/a/2"));// passivate node cache.evict(Fqn.fromString("/a/3"));// passivate node cache.evict(Fqn.fromString("/a"));// passivate node assertTrue(loader.exists(Fqn.fromString("/a"))); addDelay(); assertNotNull(cache.getNode("/a"));// load node assertTrue(loader.exists(Fqn.fromString("/a")));// children haven't been loaded Set children = cache.getNode("/a").getChildrenNames(); assertNotNull("No children were loaded", children); System.out.println("children: " + children); assertEquals("3 children weren't loaded", 3, children.size()); cache.get("/a/1", "test");// activate node assertFalse(loader.exists(Fqn.fromString("/a/1"))); cache.get("/a/2", "test");// activate node assertFalse(loader.exists(Fqn.fromString("/a/2"))); cache.get("/a/3", "test");// activate node assertFalse(loader.exists(Fqn.fromString("/a/3"))); cache.get("/a", "test");// activate node assertFalse(loader.exists(Fqn.fromString("/a"))); } public void testGetChildren7Passivation() throws Exception { cache.put("/a/1", null); cache.put("/a/2", null); cache.put("/a/3", null); cache.put("/a", "test", "test"); cache.evict(Fqn.fromString("/a/1"));// passivate node cache.evict(Fqn.fromString("/a/2"));// passivate node cache.evict(Fqn.fromString("/a/3"));// passivate node cache.evict(Fqn.fromString("/a"));// passivate node assertTrue(loader.exists(Fqn.fromString("/a"))); addDelay(); Object val = cache.get("/a", "test");// load node's attributes but not children assertEquals("attributes weren't loaded", "test", val); Set children = cache.getNode("/a").getChildrenNames(); assertNotNull("No children were loaded", children); System.out.println("children: " + children); assertEquals("3 children weren't loaded", 3, children.size()); cache.get("/a/1", "test");// activate node assertFalse(loader.exists(Fqn.fromString("/a/1"))); cache.get("/a/2", "test");// activate node assertFalse(loader.exists(Fqn.fromString("/a/2"))); cache.get("/a/3", "test");// activate node assertFalse(loader.exists(Fqn.fromString("/a/3"))); assertTrue(loader.exists(Fqn.fromString("/a"))); } public void testGetChildren8Passivation() throws Exception { cache.put("/a/1", null); cache.put("/a/2", null); cache.put("/a/3", null); cache.evict(Fqn.fromString("/a/1"));// passivate node cache.evict(Fqn.fromString("/a/2"));// passivate node cache.evict(Fqn.fromString("/a/3"));// passivate node cache.evict(Fqn.fromString("/a"));// passivate node addDelay(); assertNull(cache.get("/a", "test"));// load attributes only assertTrue(loader.exists(Fqn.fromString("/a")));// loaded attibutes but not children assertNull(cache.get("/a/1", "test"));// activate node assertFalse(loader.exists(Fqn.fromString("/a/1")));// loaded attributes and has no children Set children = cache.getNode("/a").getChildrenNames();// load children names assertNotNull("No children were loaded", children); System.out.println("children: " + children); assertEquals("3 children weren't loaded", 3, children.size()); assertTrue(loader.exists(Fqn.fromString("/a")));//loaded children but didn't initalizae them } public void testGetChildren9Passivation() throws Exception { cache.put("/a/1", null); cache.put("/a/2", null); cache.put("/a/3", null); cache.evict(Fqn.fromString("/a/1"));// passivate node cache.evict(Fqn.fromString("/a/2"));// passivate node cache.evict(Fqn.fromString("/a/3"));// passivate node cache.evict(Fqn.fromString("/a"));// passivate node assertTrue(loader.exists(Fqn.fromString("/a"))); addDelay(); cache.get("/a/1", "test");// activate node assertFalse(loader.exists(Fqn.fromString("/a/1"))); cache.get("/a/2", "test");// activate node assertFalse(loader.exists(Fqn.fromString("/a/2"))); cache.get("/a/3", "test");// activate node assertFalse(loader.exists(Fqn.fromString("/a/3"))); Set children = cache.getNode("/a").getChildrenNames();// get node's children names assertNotNull("No children were loaded", children); System.out.println("children: " + children); assertEquals("3 children weren't loaded", 3, children.size()); assertNull(cache.get("/a", "test"));// load attributes and has no children by now, activation assertFalse(loader.exists(Fqn.fromString("/a"))); cache.evict(Fqn.fromString("/a/1"));// passivate node cache.evict(Fqn.fromString("/a/2"));// passivate node cache.evict(Fqn.fromString("/a/3"));// passivate node cache.evict(Fqn.fromString("/a"));// passivate node assertTrue(loader.exists(Fqn.fromString("/a"))); cache.get("/a/1", "test");// activate node assertFalse(loader.exists(Fqn.fromString("/a/1"))); cache.get("/a/2", "test");// activate node assertFalse(loader.exists(Fqn.fromString("/a/2"))); cache.get("/a/3", "test");// activate node assertFalse(loader.exists(Fqn.fromString("/a/3"))); children = cache.getNode("/a").getChildrenNames();// get children names assertNotNull("No children were loaded", children); System.out.println("children: " + children); assertEquals("3 children weren't loaded", 3, children.size()); assertNull(cache.get("/a", "test"));// load attributes and has no children by now, activation assertFalse(loader.exists(Fqn.fromString("/a"))); } public void testGetChildren10Passivation() throws Exception { cache.put("/a/1", null); cache.put("/a/2", null); cache.put("/a/3", null); cache.evict(Fqn.fromString("/a/1"));// passivate node cache.evict(Fqn.fromString("/a/2"));// passivate node cache.evict(Fqn.fromString("/a/3"));// passivate node cache.evict(Fqn.fromString("/a"));// passivate node addDelay(); assertTrue(loader.exists(Fqn.fromString("/a"))); assertNull(cache.get("/a", "test"));// load attributes from loader // don't remove from loader though since children may be present assertTrue(loader.exists(Fqn.fromString("/a"))); cache.get("/a/1", "test");// activate node assertFalse(loader.exists(Fqn.fromString("/a/1"))); cache.get("/a/2", "test");// activate node assertFalse(loader.exists(Fqn.fromString("/a/2"))); cache.get("/a/3", "test");// passivate node assertFalse(loader.exists(Fqn.fromString("/a/3"))); Set children = cache.getNode("/a").getChildrenNames(); assertNotNull("No children were loaded", children); System.out.println("children: " + children); assertEquals("3 children weren't loaded", 3, children.size()); assertNull(cache.get("/a", "test"));// activate node assertFalse(loader.exists(Fqn.fromString("/a"))); } public void testRemoveData() throws Exception { String key = "/x/y/z/"; cache.put(key, "keyA", "valA"); cache.put(key, "keyB", "valB"); cache.put(key, "keyC", "valC"); assertEquals(3, cache.getNode(key).getKeys().size()); cache.getNode(key).clearData(); Set keys = cache.getNode(key).getKeys(); assertEquals(0, keys.size()); cache.removeNode("/x"); Object val = cache.get(key, "keyA"); assertNull(val); } public void testRemoveData2Passivation() throws Exception { Set keys; Fqn key = Fqn.fromString("/x/y/z/"); cache.put(key, "keyA", "valA"); cache.put(key, "keyB", "valB"); cache.put(key, "keyC", "valC"); addDelay(); keys = cache.getNode(key).getKeys(); assertEquals(3, keys.size()); cache.getNode(key).clearData(); cache.evict(key);// passivate node addDelay(); keys = cache.getNode(key).getKeys();// activate node assertFalse(loader.exists(key)); assertEquals(0, keys.size()); } public void testRemoveData3Passivation() throws Exception { Set keys; Fqn key = Fqn.fromString("/x/y/z/"); cache.put(key, "keyA", "valA"); cache.put(key, "keyB", "valB"); cache.put(key, "keyC", "valC"); keys = cache.getNode(key).getKeys(); assertEquals(3, keys.size()); cache.evict(key);// passivate node assertTrue(loader.exists(key)); cache.getNode(key).clearData(); keys = cache.getNode(key).getKeys();// activate node assertFalse(loader.exists(key)); assertEquals(0, keys.size()); } public void testRemoveKey() throws Exception { String key = "/x/y/z/"; cache.put(key, "keyA", "valA"); cache.put(key, "keyB", "valB"); cache.put(key, "keyC", "valC"); cache.remove(key, "keyA"); assertEquals(2, cache.getNode(key).getKeys().size()); cache.removeNode("/x"); } public void testRemoveKey2() throws CacheException { final Fqn NODE = Fqn.fromString("/test"); final String KEY = "key"; Object retval = null; cache.removeNode(NODE); retval = cache.put(NODE, KEY, 10); assertNull(retval); addDelay(); retval = cache.remove(NODE, KEY); assertEquals(10, retval); addDelay(); retval = cache.remove(NODE, KEY); assertNull(retval); } public void testRemoveKey3Passivation() throws Exception { final Fqn NODE = Fqn.fromString("/test"); final String KEY = "key"; Object retval = null; cache.removeNode(NODE); retval = cache.put(NODE, KEY, 10); assertNull(retval); cache.evict(NODE);// passivate node addDelay(); assertTrue(loader.exists(NODE)); assertEquals(10, loader.get(NODE).get(KEY)); retval = cache.remove(NODE, KEY);// activate node assertEquals(10, retval); assertFalse(loader.exists(NODE)); cache.evict(NODE);// passiave node addDelay(); retval = cache.remove(NODE, KEY);// activate node assertFalse(loader.exists(NODE)); assertNull(retval); } public void testRemove() throws Exception { String key = "/x/y/z/"; cache.put(key, "keyA", "valA"); cache.put(key, "keyB", "valB"); cache.put(key, "keyC", "valC"); cache.removeNode("/x"); assertNull(cache.get(key, "keyA")); addDelay(); Set keys = cache.getKeys(key); assertNull(keys); cache.removeNode("/x"); } public void testRemoveRoot() throws Exception { assertEquals(0, cache.getRoot().getKeys().size()); cache.put("/1/2/3/4/5", null); cache.put("uno/due/tre", null); cache.put("1/2/3/a", null); cache.put("/eins/zwei/drei", null); cache.put("/one/two/three", null); cache.removeNode(Fqn.ROOT); assertEquals(0, cache.getRoot().getKeys().size()); } public void testEvictionWithCacheLoaderPassivation() throws Exception { cache.put("/first/second", "key1", "val1"); cache.put("/first/second/third", "key2", "val2"); cache.evict(Fqn.fromString("/first/second"));// pasivate node to cache loader addDelay(); assertTrue(loader.exists(Fqn.fromString("/first/second"))); assert (exists("/first")); String val = (String) cache.get("/first/second", "key1"); assertTrue(loader.exists(Fqn.fromString("/first/second"))); assertEquals("val1", val); String val2 = (String) cache.get("/first/second/third", "key2");// activate node assertFalse(loader.exists(Fqn.fromString("/first/second/third"))); assertEquals("val2", val2); assert (exists("/first/second/third")); assert (exists("/first/second")); assert (exists("/first")); } public void testEvictionWithCacheLoaderPassivation2() throws Exception { cache.put("/first/second/third", "key1", "val1");// stored in cache loader cache.evict(Fqn.fromString("/first/second/third"));// passivate node, note: it has no children addDelay(); assertTrue(loader.exists(Fqn.fromString("/first/second/third"))); assert (exists("/first/second")); assert (exists("/first")); String val = (String) cache.get("/first/second/third", "key1");// activate node assertFalse(loader.exists(Fqn.fromString("/first/second/third"))); assertEquals("val1", val); assert (exists("/first/second/third")); assert (exists("/first/second")); assert (exists("/first")); } public void testEvictionWithGetChildrenNamesPassivation() throws Exception { cache.put("/a/1", null); cache.put("/a/2", null); cache.put("/a/3", null); cache.evict(Fqn.fromString("/a/1"));// passivate node assertTrue(loader.exists(Fqn.fromString("/a/1"))); cache.evict(Fqn.fromString("/a/2"));// passivate node assertTrue(loader.exists(Fqn.fromString("/a/2"))); cache.evict(Fqn.fromString("/a/3"));// passivate node assertTrue(loader.exists(Fqn.fromString("/a/3"))); cache.evict(Fqn.fromString("/a"));// passivate node assertTrue(loader.exists(Fqn.fromString("/a"))); addDelay(); DummyTransactionManager mgr = DummyTransactionManager.getInstance(); mgr.begin(); mgr.getTransaction(); Set children = cache.getNode("/a").getChildrenNames(); assertEquals(3, children.size()); assertTrue(children.contains("1")); assertTrue(children.contains("2")); assertTrue(children.contains("3")); assertEquals(5, cache.getNumberOfLocksHeld()); mgr.commit(); } /** Test for JBCACHE-1423 */ public void testPutDataMapAfterPassivation() throws Exception { Fqn f = Fqn.fromString("/a"); assert !cache.exists(f); assert !loader.exists(f); Map input = new HashMap(); input.put("one", "one"); input.put("two", "two"); cache.put(f, input); cache.evict(f); input = new HashMap(); input.put("one", "oneA"); cache.put(f, input); Map data = cache.getRoot().getChild(f).getData(); assertEquals("incorrect # of entries", 2, data.size()); assertEquals("Has key 'one", "oneA", data.get("one")); assertEquals("Has key 'two", "two", data.get("two")); } public void testTxPutCommit() throws Exception { DummyTransactionManager mgr = DummyTransactionManager.getInstance(); mgr.begin(); cache.put("/one/two/three", "key1", "val1"); cache.put("/one/two/three/four", "key2", "val2"); mgr.commit(); assertNotNull(cache.getNode("/one/two/three").getKeys()); assertEquals("val1", cache.get(Fqn.fromString("/one/two/three"), "key1")); mgr.begin(); cache.evict(Fqn.fromString("/one/two/three")); cache.evict(Fqn.fromString("/one/two/three/four")); mgr.commit(); assertTrue(loader.exists(Fqn.fromString("/one/two/three"))); assertTrue(loader.exists(Fqn.fromString("/one/two/three/four"))); assertNotNull(cache.getNode("/one/two/three").getKeys()); Set children = cache.getNode("/one").getChildrenNames(); assertEquals(1, children.size()); cache.removeNode(Fqn.ROOT); } public void testTxPutRollback() throws Exception { DummyTransactionManager mgr = DummyTransactionManager.getInstance(); cache.removeNode("/one"); addDelay(); mgr.begin(); cache.put("/one/two/three", "key1", "val1"); cache.put("/one/two/three/four", "key2", "val2"); mgr.rollback(); addDelay(); assertNull(cache.getNode("/one/two/three")); assert cache.getNode("/one") == null; assertFalse(loader.exists(Fqn.fromString("/one/two/three"))); assertFalse(loader.exists(Fqn.fromString("/one/two/three/four"))); } public void testPassivationAndActivation() throws Exception { Object val, val2; Fqn NODE = Fqn.fromString("/test"); loader.remove(Fqn.fromString("/")); cache.put(NODE, "key", "val"); //val=loader.get(NODE).get("key"); assertNull("value cannot be passivated yet (only on eviction)", loader.get(NODE)); cache.evict(NODE); assertEquals(0, cache.getNumberOfNodes()); assertEquals(0, cache.getNumberOfAttributes()); val = loader.get(NODE).get("key"); assertNotNull("value must have been passivated on evict()", val); assertEquals(val, "val"); val2 = cache.get(NODE, "key"); assertNotNull(val2); assertEquals(val, val2); // val=loader.get(NODE).get("key"); assertNull("value should have been deleted from store on activation", loader.get(NODE)); } /** * Tests basic operations without a transaction. */ public void testBasicOperations() throws Exception { doTestBasicOperations(); } /** * Tests basic operations with a transaction. */ public void testBasicOperationsTransactional() throws Exception { DummyTransactionManager mgr = DummyTransactionManager.getInstance(); mgr.begin(); doTestBasicOperations(); mgr.commit(); } /** * Tests basic operations. */ private void doTestBasicOperations() throws Exception { /* One FQN only. */ doPutTests(Fqn.fromString("/key")); doRemoveTests(Fqn.fromString("/key")); // assertEquals(0, loader.loadEntireState().length); /* Add three FQNs, middle FQN last. */ doPutTests(Fqn.fromString("/key1")); doPutTests(Fqn.fromString("/key3")); doPutTests(Fqn.fromString("/key2")); assertEquals(4, loader.get(Fqn.fromString("/key1")).size()); assertEquals(4, loader.get(Fqn.fromString("/key2")).size()); assertEquals(4, loader.get(Fqn.fromString("/key3")).size()); /* Remove middle FQN first, then the others. */ doRemoveTests(Fqn.fromString("/key2")); doRemoveTests(Fqn.fromString("/key3")); doRemoveTests(Fqn.fromString("/key1")); assertEquals(null, loader.get(Fqn.fromString("/key1"))); assertEquals(null, loader.get(Fqn.fromString("/key2"))); assertEquals(null, loader.get(Fqn.fromString("/key3"))); // assertEquals(0, loader.loadEntireState().length); } /** * Do basic put tests for a given FQN. */ private void doPutTests(Fqn fqn) throws Exception { assertTrue(!loader.exists(fqn)); /* put(Fqn,Object,Object) and get(Fqn,Object) */ Object oldVal; oldVal = loader.put(fqn, "one", "two"); assertNull(oldVal); addDelay(); oldVal = loader.put(fqn, "three", "four"); assertNull(oldVal); addDelay(); assertEquals("two", loader.get(fqn).get("one")); assertEquals("four", loader.get(fqn).get("three")); addDelay(); oldVal = loader.put(fqn, "one", "xxx"); assertEquals("two", oldVal); addDelay(); oldVal = loader.put(fqn, "one", "two"); assertEquals("xxx", oldVal); /* get(Fqn) */ addDelay(); Map map = loader.get(fqn); assertEquals(2, map.size()); assertEquals("two", map.get("one")); assertEquals("four", map.get("three")); Map map2 = new HashMap(map); /* put(Fqn,Map) */ map2.put("five", "six"); map2.put("seven", "eight"); loader.put(fqn, map2); addDelay(); assertEquals("six", loader.get(fqn).get("five")); assertEquals("eight", loader.get(fqn).get("seven")); assertEquals(map2, loader.get(fqn)); assertEquals(4, map2.size()); assertTrue(loader.exists(fqn)); } /** * Do basic remove tests for a given FQN. */ private void doRemoveTests(Fqn fqn) throws Exception { /* remove(Fqn,Object) */ Object oldVal; oldVal = loader.remove(fqn, "one"); assertEquals("two", oldVal); addDelay(); oldVal = loader.remove(fqn, "five"); assertEquals("six", oldVal); addDelay(); assertEquals(null, loader.get(fqn).get("one")); assertEquals(null, loader.get(fqn).get("five")); assertEquals("four", loader.get(fqn).get("three")); assertEquals("eight", loader.get(fqn).get("seven")); Map map = loader.get(fqn); assertEquals(2, map.size()); assertEquals("four", map.get("three")); assertEquals("eight", map.get("seven")); /* remove(Fqn) */ assertTrue(loader.exists(fqn)); loader.remove(fqn); addDelay(); assertNull(loader.get(fqn)); assertTrue(!loader.exists(fqn)); } /** * Tests creating implicit intermediate nodes when a leaf node is created, * and tests removing subtrees. */ public void testMultiLevelTree() throws Exception { /* Create top level node implicitly. */ Fqn k0 = Fqn.fromString("/key0"); assertTrue(!loader.exists(k0)); loader.put(Fqn.fromString("/key0/level1/level2"), null); addDelay(); assertTrue(loader.exists(Fqn.fromString("/key0/level1/level2"))); assertTrue(loader.exists(Fqn.fromString("/key0/level1"))); assertTrue(loader.exists(k0)); /* Remove leaf, leaving implicitly created middle level. */ loader.put(Fqn.fromString("/key0/x/y"), null); addDelay(); assertTrue(loader.exists(Fqn.fromString("/key0/x/y"))); assertTrue(loader.exists(Fqn.fromString("/key0/x"))); loader.remove(Fqn.fromString("/key0/x/y")); addDelay(); assertTrue(!loader.exists(Fqn.fromString("/key0/x/y"))); assertTrue(loader.exists(Fqn.fromString("/key0/x"))); /* Delete top level to delete everything. */ loader.remove(k0); addDelay(); assertTrue(!loader.exists(k0)); assertTrue(!loader.exists(Fqn.fromString("/key0/level1/level2"))); assertTrue(!loader.exists(Fqn.fromString("/key0/level1"))); assertTrue(!loader.exists(Fqn.fromString("/key0/x"))); /* Add three top level nodes as context. */ loader.put(Fqn.fromString("/key1"), null); loader.put(Fqn.fromString("/key2"), null); loader.put(Fqn.fromString("/key3"), null); addDelay(); assertTrue(loader.exists(Fqn.fromString("/key1"))); assertTrue(loader.exists(Fqn.fromString("/key2"))); assertTrue(loader.exists(Fqn.fromString("/key3"))); /* Put /key3/level1/level2. level1 should be implicitly created. */ assertTrue(!loader.exists(Fqn.fromString("/key3/level1"))); assertTrue(!loader.exists(Fqn.fromString("/key3/level1/level2"))); loader.put(Fqn.fromString("/key3/level1/level2"), null); addDelay(); assertTrue(loader.exists(Fqn.fromString("/key3/level1/level2"))); assertTrue(loader.exists(Fqn.fromString("/key3/level1"))); /* Context nodes should still be intact. */ assertTrue(loader.exists(Fqn.fromString("/key1"))); assertTrue(loader.exists(Fqn.fromString("/key2"))); assertTrue(loader.exists(Fqn.fromString("/key3"))); /* Remove middle level only. */ loader.remove(Fqn.fromString("/key3/level1")); addDelay(); assertTrue(!loader.exists(Fqn.fromString("/key3/level1/level2"))); assertTrue(!loader.exists(Fqn.fromString("/key3/level1"))); /* Context nodes should still be intact. */ assertTrue(loader.exists(Fqn.fromString("/key1"))); assertTrue(loader.exists(Fqn.fromString("/key2"))); assertTrue(loader.exists(Fqn.fromString("/key3"))); /* Delete first root, leaving other roots. */ loader.remove(Fqn.fromString("/key1")); addDelay(); assertTrue(!loader.exists(Fqn.fromString("/key1"))); assertTrue(loader.exists(Fqn.fromString("/key2"))); assertTrue(loader.exists(Fqn.fromString("/key3"))); /* Delete last root, leaving other roots. */ loader.remove(Fqn.fromString("/key3")); addDelay(); assertTrue(loader.exists(Fqn.fromString("/key2"))); assertTrue(!loader.exists(Fqn.fromString("/key3"))); /* Delete final root, leaving none. */ loader.remove(Fqn.fromString("/key2")); addDelay(); assertTrue(!loader.exists(k0)); assertTrue(!loader.exists(Fqn.fromString("/key1"))); assertTrue(!loader.exists(Fqn.fromString("/key2"))); assertTrue(!loader.exists(Fqn.fromString("/key3"))); /* Repeat all tests above using put(Fqn,Object,Object) and get(Fqn) */ assertNull(loader.get(k0)); loader.put(Fqn.fromString("/key0/level1/level2"), "a", "b"); addDelay(); assertNotNull(loader.get(Fqn.fromString("/key0/level1/level2"))); assertNotNull(loader.get(Fqn.fromString("/key0/level1"))); assertTrue(loader.get(Fqn.fromString("/key0/level1")).isEmpty()); assertNotNull(loader.get(k0)); assertTrue(loader.get(Fqn.fromString("/key0")).isEmpty()); loader.put(Fqn.fromString("/key0/x/y"), "a", "b"); addDelay(); assertNotNull(loader.get(Fqn.fromString("/key0/x/y"))); assertNotNull(loader.get(Fqn.fromString("/key0/x"))); assertTrue(loader.get(Fqn.fromString("/key0/x")).isEmpty()); loader.remove(Fqn.fromString("/key0/x/y")); addDelay(); assertNull(loader.get(Fqn.fromString("/key0/x/y"))); assertNotNull(loader.get(Fqn.fromString("/key0/x"))); assertTrue(loader.get(Fqn.fromString("/key0/x")).isEmpty()); loader.remove(k0); addDelay(); assertNull(loader.get(k0)); assertNull(loader.get(Fqn.fromString("/key0/level1/level2"))); assertNull(loader.get(Fqn.fromString("/key0/level1"))); assertNull(loader.get(Fqn.fromString("/key0/x"))); loader.put(Fqn.fromString("/key1"), "a", "b"); loader.put(Fqn.fromString("/key2"), "a", "b"); loader.put(Fqn.fromString("/key3"), "a", "b"); addDelay(); assertNotNull(loader.get(Fqn.fromString("/key1"))); assertNotNull(loader.get(Fqn.fromString("/key2"))); assertNotNull(loader.get(Fqn.fromString("/key3"))); assertNull(loader.get(Fqn.fromString("/key3/level1"))); assertNull(loader.get(Fqn.fromString("/key3/level1/level2"))); loader.put(Fqn.fromString("/key3/level1/level2"), "a", "b"); addDelay(); assertNotNull(loader.get(Fqn.fromString("/key3/level1/level2"))); assertNotNull(loader.get(Fqn.fromString("/key3/level1"))); assertTrue(loader.get(Fqn.fromString("/key3/level1")).isEmpty()); assertNotNull(loader.get(Fqn.fromString("/key1"))); assertNotNull(loader.get(Fqn.fromString("/key2"))); assertNotNull(loader.get(Fqn.fromString("/key3"))); loader.remove(Fqn.fromString("/key3/level1")); addDelay(); assertNull(loader.get(Fqn.fromString("/key3/level1/level2"))); assertNull(loader.get(Fqn.fromString("/key3/level1"))); assertNotNull(loader.get(Fqn.fromString("/key1"))); assertNotNull(loader.get(Fqn.fromString("/key2"))); assertNotNull(loader.get(Fqn.fromString("/key3"))); loader.remove(Fqn.fromString("/key1")); addDelay(); assertNull(loader.get(Fqn.fromString("/key1"))); assertNotNull(loader.get(Fqn.fromString("/key2"))); assertNotNull(loader.get(Fqn.fromString("/key3"))); loader.remove(Fqn.fromString("/key3")); addDelay(); assertNotNull(loader.get(Fqn.fromString("/key2"))); assertNull(loader.get(Fqn.fromString("/key3"))); loader.remove(Fqn.fromString("/key2")); addDelay(); assertNull(loader.get(k0)); assertNull(loader.get(Fqn.fromString("/key1"))); assertNull(loader.get(Fqn.fromString("/key2"))); assertNull(loader.get(Fqn.fromString("/key3"))); } /** * Tests the getChildrenNames() method. */ public void testGetChildrenNames() throws Exception { checkChildren(Fqn.ROOT, null); checkChildren(Fqn.fromString("/key0"), null); loader.put(Fqn.fromString("/key0"), null); addDelay(); checkChildren(Fqn.ROOT, new String[]{"key0"}); loader.put(Fqn.fromString("/key1/x"), null); addDelay(); checkChildren(Fqn.ROOT, new String[]{"key0", "key1"}); checkChildren(Fqn.fromString("/key1"), new String[]{"x"}); loader.remove(Fqn.fromString("/key1/x")); addDelay(); checkChildren(Fqn.ROOT, new String[]{"key0", "key1"}); checkChildren(Fqn.fromString("/key0"), null); checkChildren(Fqn.fromString("/key1"), null); loader.put(Fqn.fromString("/key0/a"), null); loader.put(Fqn.fromString("/key0/ab"), null); loader.put(Fqn.fromString("/key0/abc"), null); addDelay(); checkChildren(Fqn.fromString("/key0"), new String[]{"a", "ab", "abc"}); loader.put(Fqn.fromString("/key0/xxx"), null); loader.put(Fqn.fromString("/key0/xx"), null); loader.put(Fqn.fromString("/key0/x"), null); addDelay(); checkChildren(Fqn.fromString("/key0"), new String[]{"a", "ab", "abc", "x", "xx", "xxx"}); loader.put(Fqn.fromString("/key0/a/1"), null); loader.put(Fqn.fromString("/key0/a/2"), null); loader.put(Fqn.fromString("/key0/a/2/1"), null); addDelay(); checkChildren(Fqn.fromString("/key0/a/2"), new String[]{"1"}); checkChildren(Fqn.fromString("/key0/a"), new String[]{"1", "2"}); checkChildren(Fqn.fromString("/key0"), new String[]{"a", "ab", "abc", "x", "xx", "xxx"}); // // loader.put(Fqn.fromString("/key0/\u0000"), null); // loader.put(Fqn.fromString("/key0/\u0001"), null); // checkChildren(Fqn.fromString("/key0"), // new String[] { "a", "ab", "abc", "x", "xx", "xxx", // "\u0000", "\u0001"}); // // loader.put(Fqn.fromString("/\u0001"), null); // checkChildren(new Fqn(), new String[] { "key0", "key1", "\u0001" }); // // loader.put(Fqn.fromString("/\u0001/\u0001"), null); // checkChildren(Fqn.fromString("/\u0001"), new String[] { "\u0001" }); // // loader.put(Fqn.fromString("/\u0001/\uFFFF"), null); // checkChildren(Fqn.fromString("/\u0001"), // new String[] { "\u0001", "\uFFFF" }); // // loader.put(Fqn.fromString("/\u0001/\uFFFF/\u0001"), null); // checkChildren(Fqn.fromString("/\u0001/\uFFFF"), // new String[] { "\u0001" }); } /** * Checks that the given list of children part names is returned. */ private void checkChildren(Fqn fqn, String[] names) throws Exception { Set set = loader.getChildrenNames(fqn); if (names != null) { assertEquals(names.length, set.size()); for (int i = 0; i < names.length; i += 1) { assertTrue(set.contains(names[i])); } } else { assertNull(set); } } /** * Tests basic operations without a transaction. */ public void testModifications() throws Exception { doTestModifications(); } /** * Tests basic operations with a transaction. */ public void testModificationsTransactional() throws Exception { DummyTransactionManager mgr = DummyTransactionManager.getInstance(); mgr.begin(); doTestModifications(); mgr.commit(); } /** * Tests modifications. */ private void doTestModifications() throws Exception { /* PUT_KEY_VALUE, PUT_DATA */ List list = createUpdates(); loader.put(list); addDelay(); checkModifications(list); /* REMOVE_KEY_VALUE */ list = new ArrayList(); Modification mod = new Modification(); mod.setType(Modification.ModificationType.REMOVE_KEY_VALUE); mod.setFqn(FQN); mod.setKey("one"); list.add(mod); loader.put(list); addDelay(); checkModifications(list); /* REMOVE_NODE */ list = new ArrayList(); mod = new Modification(); mod.setType(Modification.ModificationType.REMOVE_NODE); mod.setFqn(FQN); list.add(mod); loader.put(list); addDelay(); checkModifications(list); assertEquals(null, loader.get(FQN)); /* REMOVE_DATA */ loader.put(FQN, "one", "two"); list = new ArrayList(); mod = new Modification(); mod.setType(Modification.ModificationType.REMOVE_DATA); mod.setFqn(FQN); list.add(mod); loader.put(list); addDelay(); checkModifications(list); } /** * Tests a one-phase transaction. */ public void testOnePhaseTransaction() throws Exception { List mods = createUpdates(); loader.prepare(null, mods, true); checkModifications(mods); } /** * Tests a two-phase transaction. */ public void testTwoPhaseTransactionPassivation() throws Exception { Object txnKey = new Object(); List mods = createUpdates(); loader.prepare(txnKey, mods, false); // try { // checkModifications(mods); // // fail("Expected lock timeout"); // } catch (DeadlockException expected) {} loader.commit(txnKey); addDelay(); checkModifications(mods); } /** * Tests rollback of a two-phase transaction. */ public void testTransactionRollbackPassivation() throws Exception { loader.remove(Fqn.fromString("/")); ByteArrayOutputStream baos = new ByteArrayOutputStream(1024); MarshalledValueOutputStream os = new MarshalledValueOutputStream(baos); loader.loadEntireState(os); os.close(); int num = baos.size(); Object txnKey = new Object(); List mods = createUpdates(); loader.prepare(txnKey, mods, false); loader.rollback(txnKey); baos = new ByteArrayOutputStream(1024); os = new MarshalledValueOutputStream(baos); loader.loadEntireState(os); os.close(); assertEquals(num, baos.size()); } /** * Creates a set of update (PUT_KEY_VALUE, PUT_DATA) modifications. */ private List createUpdates() { List list = new ArrayList(); Modification mod = new Modification(); mod.setType(Modification.ModificationType.PUT_KEY_VALUE); mod.setFqn(FQN); mod.setKey("one"); mod.setValue("two"); list.add(mod); mod = new Modification(); mod.setType(Modification.ModificationType.PUT_KEY_VALUE); mod.setFqn(FQN); mod.setKey("three"); mod.setValue("four"); list.add(mod); Map map = new HashMap(); map.put("five", "six"); map.put("seven", "eight"); mod = new Modification(); mod.setType(Modification.ModificationType.PUT_DATA); mod.setFqn(FQN); mod.setData(map); list.add(mod); return list; } /** * Checks that a list of modifications was applied. */ private void checkModifications(List list) throws Exception { for (int i = 0; i < list.size(); i += 1) { Modification mod = list.get(i); @SuppressWarnings("unchecked") Fqn fqn = mod.getFqn(); switch (mod.getType()) { case PUT_KEY_VALUE: assertEquals(mod.getValue(), loader.get(fqn).get(mod.getKey())); break; case PUT_DATA: Map map = mod.getData(); for (Iterator iter = map.keySet().iterator(); iter.hasNext();) { Object key = iter.next(); assertEquals(map.get(key), loader.get(fqn).get(key)); } break; case REMOVE_KEY_VALUE: assertEquals(null, loader.get(fqn).get(mod.getKey())); break; case REMOVE_DATA: map = loader.get(fqn); assertNotNull(map); assertTrue(map.isEmpty()); break; case REMOVE_NODE: assertEquals(null, loader.get(fqn)); break; default: fail("unknown type: " + mod); break; } } } /** * Tests that null keys and values work as for a standard Java Map. */ public void testNullKeysAndValues() throws Exception { loader.put(FQN, null, "x"); addDelay(); assertEquals("x", loader.get(FQN).get(null)); Map map = loader.get(FQN); assertEquals(1, map.size()); assertEquals("x", map.get(null)); loader.put(FQN, "y", null); addDelay(); assertEquals(null, loader.get(FQN).get("y")); map = loader.get(FQN); assertEquals(2, map.size()); assertEquals("x", map.get(null)); assertEquals(null, map.get("y")); loader.remove(FQN, null); addDelay(); assertEquals(null, loader.get(FQN).get(null)); assertEquals(1, loader.get(FQN).size()); loader.remove(FQN, "y"); addDelay(); assertNotNull(loader.get(FQN)); assertNull(loader.get(FQN).get("y")); assertEquals(0, loader.get(FQN).size()); map = new HashMap(); map.put(null, null); loader.put(FQN, map); addDelay(); assertEquals(map, loader.get(FQN)); loader.remove(FQN); addDelay(); assertNull(loader.get(FQN)); map = new HashMap(); map.put("xyz", null); map.put(null, "abc"); loader.put(FQN, map); addDelay(); assertEquals(map, loader.get(FQN)); loader.remove(FQN); addDelay(); assertNull(loader.get(FQN)); } /** * Test non-default database name. */ public void testDatabaseNamePassivation() throws Exception { loader.put(FQN, "one", "two"); addDelay(); assertEquals("two", loader.get(FQN).get("one")); } /** * Test load/store state. */ public void testLoadAndStore() throws Exception { /* Empty state. */ loader.remove(Fqn.fromString("/")); // assertEquals(0, loader.loadEntireState().length); // loader.storeEntireState(new byte[0]); // assertEquals(0, loader.loadEntireState().length); // loader.storeEntireState(null); // assertEquals(0, loader.loadEntireState().length); // assertEquals(null, loader.get(FQN)); /* Use a complex object to ensure that the class catalog is used. */ Complex c1 = new Complex(); Complex c2 = new Complex(c1); /* Add objects. */ loader.put(FQN, 1, c1); loader.put(FQN, 2, c2); addDelay(); assertEquals(c1, loader.get(FQN).get(1)); assertEquals(c2, loader.get(FQN).get(2)); assertEquals(2, loader.get(FQN).size()); /* Save state. */ ByteArrayOutputStream baos = new ByteArrayOutputStream(1024); MarshalledValueOutputStream os = new MarshalledValueOutputStream(baos); loader.loadEntireState(os); cache.getMarshaller().objectToObjectStream(StateTransferManager.STREAMING_DELIMITER_NODE, os); os.close(); assertTrue(baos.size() > 0); byte[] savedState = baos.toByteArray(); /* Restore state. */ ByteArrayInputStream bais = new ByteArrayInputStream(savedState); MarshalledValueInputStream is = new MarshalledValueInputStream(bais); loader.storeEntireState(is); is.close(); addDelay(); assertEquals(c1, loader.get(FQN).get(1)); assertEquals(c2, loader.get(FQN).get(2)); assertEquals(2, loader.get(FQN).size()); } /** * Complex object whose class description is stored in the class catalog. */ private static class Complex implements Serializable { /** * The serialVersionUID */ private static final long serialVersionUID = 8950692199236424832L; Complex nested; Complex() { this(null); } Complex(Complex nested) { this.nested = nested; } public boolean equals(Object o) { try { Complex x = (Complex) o; return (nested != null) ? nested.equals(x.nested) : (x.nested == null); } catch (ClassCastException e) { return false; } } public int hashCode() { if (nested == null) { return super.hashCode(); } else { return 13 + nested.hashCode(); } } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/passivation/ConcurrentPassivationTest.java0000644000175000017500000000562310777521744033066 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.passivation; import org.jboss.cache.CacheFactory; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.factories.XmlConfigurationParser; import org.jboss.cache.loader.DummyInMemoryCacheLoader; import static org.testng.AssertJUnit.assertNotNull; import static org.testng.AssertJUnit.fail; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * Tests cache behavior in the presence of concurrent passivation. * * @author Brian Stansberry * @version $Revision: 5529 $ */ @Test(groups = {"functional"}) public class ConcurrentPassivationTest { private CacheSPI cache; private int wakeupIntervalMillis_ = 0; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { initCaches(); wakeupIntervalMillis_ = cache.getConfiguration().getEvictionConfig().getWakeupIntervalSeconds() * 1000; if (wakeupIntervalMillis_ < 0) { fail("testEviction(): eviction thread wake up interval is illegal " + wakeupIntervalMillis_); } } private void initCaches() { CacheFactory instance = new DefaultCacheFactory(); cache = (CacheSPI) instance.createCache(new XmlConfigurationParser().parseFile("META-INF/conf-test/local-passivation-service.xml"), false); cache.getConfiguration().setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); cache.getConfiguration().getCacheLoaderConfig().getFirstCacheLoaderConfig().setClassName(DummyInMemoryCacheLoader.class.getName()); cache.start(); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { cache.stop(); cache = null; } public void testConcurrentPassivation() throws Exception { Fqn base = Fqn.fromElements("/org/jboss/test/data/concurrent/passivation"); // Create a bunch of nodes; more than the /org/jboss/test/data // region's maxNodes so we know eviction will kick in for (int i = 0; i < 35000; i++) { cache.put(Fqn.fromRelativeElements(base, i / 100), i, "value"); } // Loop for long enough to have 5 runs of the eviction thread long loopDone = System.currentTimeMillis() + (5 * wakeupIntervalMillis_); System.out.println("Initialised; Loop for " + (5 * wakeupIntervalMillis_) + " millis"); while (System.currentTimeMillis() < loopDone) { // If any get returns null, that's a failure for (int i = 0; i < 35000; i++) { Fqn fqn = Fqn.fromRelativeElements(base, i / 100); assertNotNull("Get on Fqn " + fqn + " returned null", cache.getNode(fqn)); } } } } ././@LongLink0000000000000000000000000000016200000000000011564 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/passivation/PassivationToDummyInMemoryCacheLoaderTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/passivation/PassivationToDummyInMemoryCacheLo0000644000175000017500000000140510732023122033425 0ustar twernertwernerpackage org.jboss.cache.passivation; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.loader.DummyInMemoryCacheLoader; import org.testng.annotations.Test; @Test(groups = {"functional"}) public class PassivationToDummyInMemoryCacheLoaderTest extends PassivationTestsBase { protected void configureCache() throws Exception { CacheLoaderConfig clc = new CacheLoaderConfig(); cache.getConfiguration().setCacheLoaderConfig(clc); CacheLoaderConfig.IndividualCacheLoaderConfig iclc = new CacheLoaderConfig.IndividualCacheLoaderConfig(); clc.setPassivation(true); clc.addIndividualCacheLoaderConfig(iclc); iclc.setClassName(DummyInMemoryCacheLoader.class.getName()); iclc.setProperties("debug=true"); } } ././@LongLink0000000000000000000000000000015500000000000011566 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/passivation/ReplicatedPassivationIntegrationTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/passivation/ReplicatedPassivationIntegrationT0000644000175000017500000001703411027753345033560 0ustar twernertwerner/* * JBoss, Home of Professional Open Source. * Copyright 2006, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.cache.passivation; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.CacheFactory; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.Node; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.EvictionConfig; import org.jboss.cache.config.EvictionRegionConfig; import org.jboss.cache.eviction.LRUConfiguration; import org.jboss.cache.eviction.LRUPolicy; import org.jboss.cache.factories.UnitTestCacheConfigurationFactory; import org.jboss.cache.loader.DummyInMemoryCacheLoader; import org.jboss.cache.loader.DummySharedInMemoryCacheLoader; import org.jboss.cache.notifications.annotation.CacheListener; import org.jboss.cache.notifications.annotation.NodeActivated; import org.jboss.cache.notifications.annotation.NodeLoaded; import org.jboss.cache.notifications.annotation.NodePassivated; import org.jboss.cache.notifications.event.NodeEvent; import org.jboss.cache.util.TestingUtil; import static org.testng.AssertJUnit.assertNotNull; import static org.testng.AssertJUnit.fail; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.LinkedList; import java.util.List; @Test(groups = "functional") public class ReplicatedPassivationIntegrationTest { private CacheSPI cache1; private CacheSPI cache2; protected final static Log log = LogFactory.getLog(ReplicatedPassivationIntegrationTest.class); int wakeupIntervalMillis = 0; PassivationListener listener; Fqn base = Fqn.fromString("/org/jboss/test/data"); public ReplicatedPassivationIntegrationTest() { listener = new ReplicatedPassivationIntegrationTest.PassivationListener(); } @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { CacheFactory instance = new DefaultCacheFactory(); cache1 = (CacheSPI) instance.createCache(getCfg(), false); cache1.getConfiguration().setUseRegionBasedMarshalling(true); cache1.start(); cache2 = (CacheSPI) instance.createCache(getCfg(), false); cache2.getConfiguration().setUseRegionBasedMarshalling(true); cache2.start(); cache2.getNotifier().addCacheListener(listener); listener.resetCounter(); wakeupIntervalMillis = cache2.getConfiguration().getEvictionConfig().getWakeupIntervalSeconds() * 1000; log("wakeupInterval is " + wakeupIntervalMillis); if (wakeupIntervalMillis <= 0) { fail("testEviction(): eviction thread wake up interval is illegal " + wakeupIntervalMillis); } } Configuration getCfg() throws Exception { Configuration cfg = UnitTestCacheConfigurationFactory.createConfiguration(Configuration.CacheMode.REPL_SYNC); cfg.setEvictionConfig(buildEvictionConfig()); cfg.setCacheLoaderConfig(buildCacheLoaderConfig()); cfg.setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); cfg.getCacheLoaderConfig().getFirstCacheLoaderConfig().setClassName(DummyInMemoryCacheLoader.class.getName()); return cfg; } private CacheLoaderConfig buildCacheLoaderConfig() throws Exception { CacheLoaderConfig clc = new CacheLoaderConfig(); IndividualCacheLoaderConfig iclc = new IndividualCacheLoaderConfig(); iclc.setClassName(DummySharedInMemoryCacheLoader.class.getName()); clc.addIndividualCacheLoaderConfig(iclc); clc.setPassivation(true); return clc; } private EvictionConfig buildEvictionConfig() throws Exception { EvictionConfig cfg = new EvictionConfig(); cfg.setWakeupIntervalSeconds(1); cfg.setDefaultEventQueueSize(200000); cfg.setDefaultEvictionPolicyClass(LRUPolicy.class.getName()); List erc = new LinkedList(); cfg.setEvictionRegionConfigs(erc); EvictionRegionConfig region1 = new EvictionRegionConfig(); region1.setRegionFqn(Fqn.ROOT); LRUConfiguration epc1 = new LRUConfiguration(); epc1.setMaxNodes(5000); epc1.setTimeToLiveSeconds(3); region1.setEvictionPolicyConfig(epc1); EvictionRegionConfig region2 = new EvictionRegionConfig(); region2.setRegionFqn(base); LRUConfiguration epc2 = new LRUConfiguration(); epc2.setMaxNodes(100); epc2.setTimeToLiveSeconds(3); region2.setEvictionPolicyConfig(epc2); erc.add(region1); erc.add(region2); return cfg; } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { cache1.stop(); cache2.stop(); } public void testActivationEvent() throws Exception { Fqn internalFqn = Fqn.fromString("/__JBossInternal__/5c4o12-pzhlhj-esnuy3sg-1-esnuy3sg-2"); Fqn fqn = Fqn.fromRelativeElements(base, "0"); cache1.removeNode(Fqn.ROOT); cache1.put(fqn, fqn.toString(), fqn.toString()); cache1.put(internalFqn, fqn.toString(), fqn.toString()); TestingUtil.sleepThread(wakeupIntervalMillis + 100); Node n = cache2.peek(fqn, false); assert n == null || !n.getKeys().contains(fqn) : "UnversionedNode should not exist"; String val; val = cache2.get(fqn, fqn.toString()); val = cache2.get(internalFqn, fqn.toString()); assertNotNull("Node should be activated ", val); } void log(String msg) { System.out.println("-- " + msg); } @CacheListener public class PassivationListener { int counter = 0; int loadedCounter = 0; public int getCounter() { return counter; } public void resetCounter() { counter = 0; loadedCounter = 0; } @NodeActivated public void nodeActivated(NodeEvent ne) { if (!ne.isPre()) { counter++; System.out.println("nodeActivate(): counter: " + counter); System.out.println("nodeActivate(): " + ne.getFqn()); } } @NodePassivated public void nodePassivated(NodeEvent ne) { if (ne.isPre()) { System.out.println("nodePassivate(): " + ne.getFqn()); } } @NodeLoaded public void nodeLoaded(NodeEvent ne) { if (!ne.isPre()) { loadedCounter++; } } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/lock/0000755000175000017500000000000011376173741023376 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/lock/PessimisticLockTest.java0000644000175000017500000000721511017455776030216 0ustar twernertwernerpackage org.jboss.cache.lock; import org.jboss.cache.Cache; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.NodeSPI; import static org.jboss.cache.lock.LockType.READ; import static org.jboss.cache.lock.LockType.WRITE; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.transaction.DummyTransactionManagerLookup; import static org.testng.AssertJUnit.assertFalse; import static org.testng.AssertJUnit.assertTrue; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.TransactionManager; /** * basic locking test * * @author Manik Surtani * @since 2.0.0 */ @Test(groups = "functional") public class PessimisticLockTest { private Cache cache; private TransactionManager tm; private Fqn fqn = Fqn.fromString("/a/b/c"); private LockManager lockManager; @BeforeMethod(alwaysRun = true) public void setUp() { cache = new DefaultCacheFactory().createCache(false); cache.getConfiguration().setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName()); cache.start(); tm = cache.getConfiguration().getRuntimeConfig().getTransactionManager(); lockManager = TestingUtil.extractLockManager(cache); } @AfterMethod(alwaysRun = true) public void tearDown() { cache.stop(); } private void assertNoStaleLocks() { CacheSPI spi = (CacheSPI) cache; assert spi.getNumberOfLocksHeld() == 0 : "Should have no stale locks!"; } public void testPut() throws Exception { cache.put(fqn, "k", "v"); assertNoStaleLocks(); tm.begin(); cache.put(fqn, "k2", "v2"); NodeSPI n = (NodeSPI) cache.getRoot().getChild(fqn); assertFalse(lockManager.isLocked(n, READ)); assertTrue(lockManager.isLocked(n, WRITE)); assertTrue(lockManager.isLocked(n.getParent(), READ)); assertFalse(lockManager.isLocked(n.getParent(), WRITE)); assertTrue(lockManager.isLocked(n.getParent().getParent(), READ)); assertFalse(lockManager.isLocked(n.getParent().getParent(), WRITE)); tm.commit(); assertNoStaleLocks(); } public void testGet() throws Exception { cache.put(fqn, "k", "v"); assertNoStaleLocks(); tm.begin(); cache.get(fqn, "k2"); NodeSPI n = (NodeSPI) cache.getRoot().getChild(fqn); assertTrue(lockManager.isLocked(n, READ)); assertFalse(lockManager.isLocked(n, WRITE)); assertTrue(lockManager.isLocked(n.getParent(), READ)); assertFalse(lockManager.isLocked(n.getParent(), WRITE)); assertTrue(lockManager.isLocked(n.getParent().getParent(), READ)); assertFalse(lockManager.isLocked(n.getParent().getParent(), WRITE)); tm.commit(); assertNoStaleLocks(); } public void testRemove() throws Exception { cache.put(fqn, "k", "v"); assertNoStaleLocks(); tm.begin(); cache.remove(fqn, "k2"); NodeSPI n = (NodeSPI) cache.getRoot().getChild(fqn); assertFalse(lockManager.isLocked(n, READ)); assertTrue(lockManager.isLocked(n, WRITE)); assertTrue(lockManager.isLocked(n.getParent(), READ)); assertFalse(lockManager.isLocked(n.getParent(), WRITE)); assertTrue(lockManager.isLocked(n.getParent().getParent(), READ)); assertFalse(lockManager.isLocked(n.getParent().getParent(), WRITE)); tm.commit(); assertNoStaleLocks(); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/lock/BreakDeadMemberLocksTest.java0000644000175000017500000001313311017455042031015 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * Copyright 2005, JBoss Inc., and individual contributors as indicated * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.cache.lock; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.factories.UnitTestCacheConfigurationFactory; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertTrue; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.Synchronization; import javax.transaction.Transaction; import javax.transaction.TransactionManager; import java.util.HashMap; import java.util.Map; /** * Tests the breaking of locks held by dead members. * * @author Brian Stansberry * @version $Revision$ */ @Test(groups = {"functional"}) public class BreakDeadMemberLocksTest { private Map> caches; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { caches = new HashMap>(); } public void testBreakDeadMemberLocks() throws Exception { CacheSPI cacheA = createCache("A"); cacheA.put("/1/A", "1", "A"); cacheA.put("/1/A", "2", "A"); cacheA.put("/2/A", "1", "A"); cacheA.put("/2/A", "2", "A"); cacheA.put("/1/A/I", "1", "A"); cacheA.put("/1/A/I", "2", "A"); CacheSPI cacheB = createCache("B"); // Pause to give caches time to see each other TestingUtil.blockUntilViewsReceived(new CacheSPI[]{cacheA, cacheB}, 60000); final TransactionManager tm = cacheB.getTransactionManager(); tm.begin(); final Transaction tx = tm.getTransaction(); cacheB.put("/1/A", "1", "B"); cacheB.put("/1/A", "2", "B"); cacheB.put("/2/A", "1", "B"); cacheB.put("/2/A", "2", "B"); cacheB.put("/1/A/I", "1", "B"); cacheB.put("/1/A/I", "2", "B"); cacheB.put("/EXISTS", "KEY", "B"); Object monitor = new Object(); HangSync sync = new HangSync(monitor); tx.registerSynchronization(sync); Thread t = new Thread() { public void run() { try { tm.resume(tx); tm.commit(); } catch (Exception e) { } } }; synchronized (monitor) { t.start(); while (!sync.hung) { monitor.wait(500); } } tm.suspend(); // Confirm that B's tx replicated assertTrue(cacheA.peek(Fqn.fromString("/EXISTS"), false, false) != null); cacheB.stop(); cacheB.destroy(); while (cacheA.getMembers().size() > 1) { try { Thread.sleep(100); } catch (InterruptedException e) { } } assertEquals("A", cacheA.get("/1/A", "1")); assertEquals("A", cacheA.get("/1/A", "2")); assertEquals("A", cacheA.get("/2/A", "1")); assertEquals("A", cacheA.get("/2/A", "2")); assertEquals("A", cacheA.get("/1/A/I", "1")); assertEquals("A", cacheA.get("/1/A/I", "2")); if (t.isAlive()) { t.interrupt(); } } protected CacheSPI createCache(String cacheID) throws Exception { if (caches.get(cacheID) != null) { throw new IllegalStateException(cacheID + " already created"); } CacheSPI cache = (CacheSPI) new DefaultCacheFactory().createCache(UnitTestCacheConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC), false); cache.create(); cache.start(); caches.put(cacheID, cache); return cache; } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { for (String cacheID : caches.keySet()) { stopCache(cacheID); } caches.clear(); } protected void stopCache(String id) { TestingUtil.killCaches(caches.get(id)); } class HangSync implements Synchronization { private boolean hung = false; private Object monitor; HangSync(Object monitor) { this.monitor = monitor; } public void afterCompletion(int arg0) { } public void beforeCompletion() { hung = true; synchronized (monitor) { monitor.notifyAll(); } try { Thread.sleep(30000); } catch (InterruptedException e) { } } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/lock/LockTest.java0000644000175000017500000003417211017455042025765 0ustar twernertwernerpackage org.jboss.cache.lock; import static org.testng.AssertJUnit.assertEquals; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import org.jboss.cache.util.TestingUtil; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * Various tests that test isolation level semantics provided by locks * * @author Bela Ban * @version $Id: LockTest.java 5906 2008-05-29 07:24:18Z mircea.markus $ */ @Test(groups = {"functional"}) public class LockTest { int value = 10; Throwable t1_ex, t2_ex; long start = 0; final long TIMEOUT = 5000; final long SLEEP = 500; volatile boolean committed; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { value = 10; t1_ex = t2_ex = null; committed = false; } static class MyFIFOSemaphore extends Semaphore { private static final long serialVersionUID = 3247961778517846603L; public MyFIFOSemaphore(int permits) { super(permits); } public void acquire() throws InterruptedException { super.acquire(); } public void release() { super.release(); } } /** * Thread1 reads data, thread2 changes and - before thread2 commits - t1 should see t2's changes. * Timeline: *
    * T1 reads data - 10 * T2 writes data - 20 * T1 reads data - 20 (see's T2's uncommitted modfication) * T2 commits (releases its lock) * T1 reads data - 20 *
*/ public void testReadUncommitted() throws Throwable { final LockStrategy s = new LockStrategyReadUncommitted(); final Semaphore sem = new MyFIFOSemaphore(1); final CyclicBarrier barrier = new CyclicBarrier(2); Thread t1 = new Thread("t1") { Lock lock = null; public void run() { try { sem.acquire(); // we're first to the semaphore // log("waiting on barrier"); barrier.await(); // wait until t2 joins us // log("passed barrier"); lock = s.readLock(); lock.tryLock(TIMEOUT, TimeUnit.MILLISECONDS); log("1st read: value is " + value); assertEquals(10, value); sem.release(); // give t2 time to make the modification TestingUtil.sleepThread(100); sem.acquire(); // to read the uncommitted modification by t2 log("2nd read: value is " + value + "; we should see t2's uncommitted change (20)"); assertEquals(20, value); // we're seeing the modification by t2 before t2 committed (a.k.a. released the lock) sem.release(); TestingUtil.sleepThread(100); sem.acquire(); // to read the committed change by t2 log("3rd read: value is still " + value + "; we should see t2's committed change"); assertEquals(20, value); } catch (Throwable ex) { t1_ex = ex; } finally { if (lock != null) lock.unlock(); sem.release(); } } }; Thread t2 = new Thread("t2") { Lock lock = null; public void run() { try { TestingUtil.sleepThread(100); barrier.await(); sem.acquire(); lock = s.writeLock(); lock.tryLock(TIMEOUT, TimeUnit.MILLISECONDS); log("changing value from " + value + " to 20"); value = 20; sem.release(); // now t1 can read the uncommitted modification TestingUtil.sleepThread(100); sem.acquire(); // to unlock the lock log("committing the TX"); lock.unlock(); } catch (Throwable ex) { t2_ex = ex; } finally { if (lock != null) lock.unlock(); sem.release(); } } }; t1.start(); t2.start(); t1.join(); t2.join(); if (t1_ex != null) throw t1_ex; if (t2_ex != null) throw t2_ex; } /** * Thread1 reads data, thread2 changes and - before thread2 commits - t1 should *not* see t2's changes. * Timeline: *
    * T1 reads data - 10 * T2 writes data - 20 (*not* visible to T1) * T1 reads data - 10 (should *not* see T2's uncommitted modfication) * T2 commits (releases its lock) * T1 sees T2's committed modification - 20 *
* Commented for now, until we get the right semantics * See http://www-128.ibm.com/developerworks/java/library/j-jtp0514.html for a discussion of * isolation levels */ // removed in favor of o.j.c.transaction.IsolationLevelReadCommittedTest.testReadCommitted() // The lock interceptor makes one request a read lock before *each* read. /*public void testReadCommitted() throws Throwable { final IdentityLock identity_lock=new IdentityLock(IsolationLevel.READ_COMMITTED); Thread t1=new Thread("t1") { public void run() { try { identity_lock.acquireReadLock(this, TIMEOUT); log("1st read: value is " + value); assertEquals(10, value); TestingUtil.sleepThread(SLEEP); log("2nd read: value is " + value + "; we should *not* see t2's uncommitted change (20)"); // we're seeing the modification by t2 before t2 committed (a.k.a. released the lock) assertEquals("This is due to incorrect impl of READ_COMMITTED", 10, value); TestingUtil.sleepThread(SLEEP); log("3rd read: value is still " + value + "; we should see t2's committed change"); assertEquals(20, value); } catch(Throwable ex) { t1_ex=ex; } finally { identity_lock.unlock(this); } } }; Thread t2=new Thread("t2") { public void run() { try { TestingUtil.sleepThread(100); identity_lock.acquireWriteLock(this, TIMEOUT); log("changing value from " + value + " to 20"); value=20; TestingUtil.sleepThread(SLEEP * 2); log("committing the TX"); identity_lock.unlock(this); } catch(Throwable ex) { t2_ex=ex; } finally { identity_lock.unlock(this); } } }; t1.start(); t2.start(); t1.join(); t2.join(); if(t1_ex != null) throw t1_ex; if(t2_ex != null) throw t2_ex; }*/ public void testWriteThanRead() throws Throwable { final LockStrategy s = new LockStrategyReadCommitted(); Thread t1 = new Thread("t1") { Lock lock = null; public void run() { try { TestingUtil.sleepThread(100); lock = s.readLock(); lock.tryLock(TIMEOUT, TimeUnit.MILLISECONDS); log("1st read: value is " + value); assertEquals(20, value); TestingUtil.sleepThread(SLEEP); log("2nd read: value is " + value + "; we should see t2's uncommitted change (20)"); assertEquals(20, value); // we're seeing the modification by t2 before t2 committed (a.k.a. released the lock) TestingUtil.sleepThread(SLEEP); } catch (Throwable ex) { t1_ex = ex; } finally { lock.unlock(); } } }; Thread t2 = new Thread("t2") { Lock lock = null; public void run() { try { lock = s.writeLock(); lock.tryLock(TIMEOUT, TimeUnit.MILLISECONDS); log("changing value from " + value + " to 20"); value = 20; TestingUtil.sleepThread(SLEEP); log("committing the TX"); lock.unlock(); } catch (Throwable ex) { t2_ex = ex; } finally { lock.unlock(); } } }; t2.start(); t1.start(); t2.join(); t1.join(); if (t1_ex != null) throw t1_ex; if (t2_ex != null) throw t2_ex; } /** * Thread1 reads data, thread2 changes and - before thread2 commits - t1 should *not* see t2's changes. * In addition, Thread1 should *not* see thread2's changes even after thread2 commits, until thread1 commits. * Timeline: *
    * T1 reads data - 10 * T2 writes data - 20 (*not* visible to T1) * T1 reads data - 10 (should *not* see T2's uncommitted modfication) * T2 commits (releases its lock) * T1 reads data, should *not* see T2's committed modification - 10 * T1 commits * T1 starts a new TX - should see 20 *
* Note: because we use pessimistic locking, the above sequence will effectively be serialized into sequential * execution: thread1 will acquire the read lock on the data and hold on to it until TX commit, only then will * thread2 be able to access the data with a write lock. */ public void testRepeatableRead() throws Throwable { final LockStrategy s = new LockStrategyRepeatableRead(); Thread t1 = new Thread("t1") { Lock lock = null; public void run() { try { lock = s.readLock(); lock.tryLock(TIMEOUT, TimeUnit.MILLISECONDS); log("1st read: value is " + value); assertEquals(10, value); TestingUtil.sleepThread(SLEEP); log("2nd read: value is " + value + "; we should *not* see t2's uncommitted change (20)"); assertEquals(10, value); TestingUtil.sleepThread(SLEEP); log("3rd read: value is still " + value + "; we should not see t2's committed change"); assertEquals(10, value); lock.unlock(); TestingUtil.sleepThread(SLEEP); lock.tryLock(TIMEOUT, TimeUnit.MILLISECONDS); log("4th read: value is now " + value + "; we should see t2's committed change in our new TX"); assertEquals(20, value); } catch (Throwable ex) { t1_ex = ex; } finally { lock.unlock(); } } }; Thread t2 = new Thread("t2") { Lock lock = null; public void run() { try { TestingUtil.sleepThread(100); lock = s.writeLock(); lock.tryLock(TIMEOUT, TimeUnit.MILLISECONDS); log("changing value from " + value + " to 20"); value = 20; TestingUtil.sleepThread(SLEEP); log("committing the TX"); lock.unlock(); } catch (Throwable ex) { t2_ex = ex; } finally { lock.unlock(); } } }; t1.start(); t2.start(); t1.join(); t2.join(); if (t1_ex != null) throw t1_ex; if (t2_ex != null) throw t2_ex; } /** * Because only 1 reader or writer can hold the lock at any given time, since thread1 is the first to get the lock, * it will hold on to it until it commits. The the writer thread (thread2) will have a chance to change the value. * Timeline: *
    * T1 reads data - 10 * T1 commits * T2 writes data - 20 * T2 commits * T1 starts a new TX and reads data - 20 * T2 commits (releases its lock) *
*/ public void testSerializable() throws Throwable { final LockStrategy s = new LockStrategySerializable(); Thread t1 = new Thread("t1") { Lock lock = null; public void run() { try { lock = s.readLock(); lock.tryLock(TIMEOUT, TimeUnit.MILLISECONDS); log("1st read: value is " + value); assertEquals(10, value); lock.unlock(); TestingUtil.sleepThread(SLEEP); lock.tryLock(TIMEOUT, TimeUnit.MILLISECONDS); log("2nd read: value is " + value + "; we should see t2's committed change (20)"); assertEquals(20, value); } catch (Throwable ex) { t1_ex = ex; } finally { lock.unlock(); } } }; Thread t2 = new Thread("t2") { Lock lock = null; public void run() { try { TestingUtil.sleepThread(100); lock = s.writeLock(); lock.tryLock(TIMEOUT, TimeUnit.MILLISECONDS); log("changing value from " + value + " to 20"); value = 20; log("committing the TX"); lock.unlock(); } catch (Throwable ex) { t2_ex = ex; } finally { lock.unlock(); } } }; t1.start(); t2.start(); t1.join(); t2.join(); if (t1_ex != null) throw t1_ex; if (t2_ex != null) throw t2_ex; } void log(String s) { long now; if (start == 0) start = System.currentTimeMillis(); now = System.currentTimeMillis(); System.out.println("[" + Thread.currentThread().getName() + "] [" + (now - start) + "] " + s); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/lock/LockMapTest.java0000644000175000017500000000254410665031725026427 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.lock; import static org.testng.AssertJUnit.assertTrue; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * @author Ben Wang */ @Test(groups = {"functional"}) public class LockMapTest { private LockMap map_; /** * Constructor for LockMapTest. * * @param arg0 */ @BeforeMethod(alwaysRun = true) protected void setUp() throws Exception { map_ = new LockMap(); } @AfterMethod(alwaysRun = true) protected void tearDown() throws Exception { map_.removeAll(); } final public void testIsOwner() { map_.addReader(this); assertTrue(map_.isOwner(this, LockMap.OWNER_READ)); map_.setWriterIfNotNull(this); assertTrue(map_.isOwner(this, LockMap.OWNER_WRITE)); assertTrue(map_.isOwner(this, LockMap.OWNER_ANY)); map_.removeAll(); } final public void testAddReader() { map_.addReader(this); assertTrue(map_.isOwner(this, LockMap.OWNER_READ)); map_.removeReader(this); } final public void testAddWriter() { map_.setWriterIfNotNull(this); assertTrue(map_.writerOwner().equals(this)); map_.removeWriter(); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/lock/LockReleaseTest.java0000644000175000017500000002236510776366774027317 0ustar twernertwerner/* * * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.lock; import org.apache.commons.logging.Log; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.transaction.TransactionSetup; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.fail; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.UserTransaction; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; /** * Verifies that there are no read locks held when a transaction ends. * * @author Bela Ban * @version $Id: LockReleaseTest.java 5505 2008-04-07 09:48:44Z manik.surtani@jboss.com $ */ @Test(groups = {"functional"}) public class LockReleaseTest { CacheSPI cache = null; UserTransaction tx = null; Log log; final Fqn NODE1 = Fqn.fromString("/test"); final Fqn NODE2 = Fqn.fromString("/my/test"); final String KEY = "key"; final String VAL1 = "val1"; final String VAL2 = "val2"; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { tx = TransactionSetup.getUserTransaction(); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { if (cache != null) { cache.stop(); } // BW. kind of a hack to destroy jndi binding and thread local tx before next run. TransactionSetup.cleanup(); if (tx != null) { try { tx.rollback(); } catch (Throwable t) { } tx = null; } } CacheSPI createCache(IsolationLevel level) throws Exception { CacheSPI c = (CacheSPI) new DefaultCacheFactory().createCache(false); c.getConfiguration().setClusterName("test"); c.getConfiguration().setStateRetrievalTimeout(10000); c.getConfiguration().setTransactionManagerLookupClass("org.jboss.cache.transaction.GenericTransactionManagerLookup"); c.getConfiguration().setLockAcquisitionTimeout(500); c.getConfiguration().setIsolationLevel(level); c.create(); c.start(); return c; } public void testReadWithReadUncommitted() throws Exception { testReadLockRelease(IsolationLevel.READ_UNCOMMITTED); } public void testWriteWithReadUncommitted() throws Exception { testWriteLockRelease(IsolationLevel.READ_UNCOMMITTED); } public void testReadWithReadCommitted() throws Exception { testReadLockRelease(IsolationLevel.READ_COMMITTED); } public void testWriteWithReadCommitted() throws Exception { testWriteLockRelease(IsolationLevel.READ_COMMITTED); } public void testReadWithRepeatableRead() throws Exception { testReadLockRelease(IsolationLevel.REPEATABLE_READ); } public void testWriteWithRepeatableRead() throws Exception { testWriteLockRelease(IsolationLevel.REPEATABLE_READ); } public void testReadWithSerialzable() throws Exception { testReadLockRelease(IsolationLevel.SERIALIZABLE); } public void testWriteWithSerializable() throws Exception { testWriteLockRelease(IsolationLevel.SERIALIZABLE); } public void testGetKeys() throws Exception { cache = createCache(IsolationLevel.REPEATABLE_READ); // add initial values outside of TX cache.put(NODE1, KEY, VAL1); cache.put(NODE2, KEY, VAL1); assertEquals("we ran outside of a TX, locks should have been released: ", 0, cache.getNumberOfLocksHeld()); Set keys = cache.getNode(NODE1).getKeys(); System.out.println("keys of " + NODE1 + " are " + keys); assertEquals("getKeys() called outside the TX should have released all locks", 0, cache.getNumberOfLocksHeld()); tx.begin(); keys = cache.getNode(NODE1).getKeys(); assertEquals("we should hold 1 read locks now: ", 2, cache.getNumberOfLocksHeld()); keys = cache.getNode(NODE2).getKeys(); assertEquals("we should hold 3 read locks now: ", 4, cache.getNumberOfLocksHeld()); tx.commit(); assertEquals("we should have released all 3 read locks: ", 0, cache.getNumberOfLocksHeld()); } public void testGetChildrenNames() throws Exception { cache = createCache(IsolationLevel.REPEATABLE_READ); // add initial values outside of TX cache.put(NODE1, KEY, VAL1); cache.put(NODE2, KEY, VAL1); assertEquals("we ran outside of a TX, locks should have been released: ", 0, cache.getNumberOfLocksHeld()); Set keys = cache.getNode(NODE2).getChildrenNames(); System.out.println("keys of " + NODE2 + " are " + keys); assertEquals("getChildrenNames() called outside the TX should have released all locks", 0, cache.getNumberOfLocksHeld()); tx.begin(); keys = cache.getNode(NODE1).getChildrenNames(); assertEquals("we should hold 1 read locks now: ", 2, cache.getNumberOfLocksHeld()); keys = cache.getNode(NODE2).getChildrenNames(); assertEquals("we should hold 3 read locks now: ", 4, cache.getNumberOfLocksHeld()); tx.commit(); assertEquals("we should have released all 3 read locks: ", 0, cache.getNumberOfLocksHeld()); } public void testPrint() throws Exception { cache = createCache(IsolationLevel.REPEATABLE_READ); // add initial values outside of TX cache.put(NODE1, KEY, VAL1); cache.put(NODE2, KEY, VAL1); assertEquals("we ran outside of a TX, locks should have been released: ", 0, cache.getNumberOfLocksHeld()); System.out.println(cache.getNode(NODE1)); assertEquals("print() called outside the TX should have released all locks", 0, cache.getNumberOfLocksHeld()); tx.begin(); System.out.println(cache.getNode(NODE1)); assertEquals("we should hold 1 read locks now (for print()): ", 2, cache.getNumberOfLocksHeld()); System.out.println(cache.getNode(NODE2)); assertEquals("we should hold 3 read locks now (for print()): ", 4, cache.getNumberOfLocksHeld()); tx.commit(); assertEquals("we should have released all 3 read locks: ", 0, cache.getNumberOfLocksHeld()); } private void testReadLockRelease(IsolationLevel level) throws Exception { cache = createCache(level); // add initial values outside of TX cache.put(NODE1, KEY, VAL1); cache.put(NODE2, KEY, VAL1); assertEquals("we ran outside of a TX, locks should have been released: ", 0, cache.getNumberOfLocksHeld()); tx.begin(); assertEquals(VAL1, cache.get(NODE1, KEY)); assertEquals(VAL1, cache.get(NODE2, KEY)); assertEquals("we should hold 3 read locks now: ", 4, cache.getNumberOfLocksHeld()); tx.commit(); assertEquals("we should have released all 3 read locks: ", 0, cache.getNumberOfLocksHeld()); } private void testWriteLockRelease(IsolationLevel level) throws Exception { cache = createCache(level); // add initial values outside of TX cache.put(NODE1, KEY, VAL1); cache.put(NODE2, KEY, VAL1); assertEquals("we ran outside of a TX, locks should have been released: ", 0, cache.getNumberOfLocksHeld()); tx.begin(); cache.put(NODE1, KEY, VAL1); cache.put(NODE2, KEY, VAL1); assertEquals("we should hold 3 write locks now: ", 4, cache.getNumberOfLocksHeld()); tx.commit(); assertEquals("we should have released all 3 write locks: ", 0, cache.getNumberOfLocksHeld()); } /** * Tests that when an acquisition timeout occurs locks are being released. */ @Test(invocationCount = 10) public void testNodeReleaseOnAcquisitionTimeout() throws Exception { cache = createCache(IsolationLevel.REPEATABLE_READ); cache.put("/a/b", "key", "value"); cache.put("/c", "key", "value"); final CountDownLatch rLockAcquired = new CountDownLatch(1); final CountDownLatch wlTimeouted = new CountDownLatch(1); final CountDownLatch txLocksReleased = new CountDownLatch(1); Thread thread = new Thread() { public void run() { try { cache.getTransactionManager().begin(); cache.get("/a/b", "key"); //at this point we have an RL on /c and /c/d rLockAcquired.countDown(); wlTimeouted.await(60, TimeUnit.SECONDS); //wait a long time but die eventually cache.getTransactionManager().commit();//here we are releasing locks txLocksReleased.countDown(); } catch (Exception ex) { ex.printStackTrace(); } } }; thread.start(); rLockAcquired.await(); try { cache.move("/a/b", "c"); //acquired RL on /a and /a/b fail("expected timeout here"); } catch (TimeoutException e) { wlTimeouted.countDown(); } txLocksReleased.await(); //wait for tx locks to be released assertEquals(0, cache.getNumberOfLocksHeld()); System.out.println("LockReleaseTest.testNodeReleaseOnAcquisitionTimeout finished!"); } } ././@LongLink0000000000000000000000000000015400000000000011565 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/lock/ReentrantWriterPreferenceReadWriteLockTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/lock/ReentrantWriterPreferenceReadWriteLockTe0000755000175000017500000001544211017455042033366 0ustar twernertwernerpackage org.jboss.cache.lock; import org.jboss.cache.util.TestingUtil; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; /** * Tests ReentrantWriterPreferenceReadWriteLock * * @author Bela Ban * @version $Id: ReentrantWriterPreferenceReadWriteLockTest.java 5906 2008-05-29 07:24:18Z mircea.markus $ */ @Test(groups = {"functional"}) public class ReentrantWriterPreferenceReadWriteLockTest { // ReentrantWriterPreferenceReadWriteLock lock; SimpleReadWriteLock lock; Lock rl, wl; Exception thread_ex = null; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { // lock=new ReentrantWriterPreferenceReadWriteLock(); lock = new SimpleReadWriteLock(); rl = lock.readLock(); wl = lock.writeLock(); thread_ex = null; } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { lock = null; if (thread_ex != null) throw thread_ex; } public void testMultipleReadLockAcquisitions() throws InterruptedException { rl.lock(); rl.lock(); } public void testInterruptedLockAcquisition() { Thread.currentThread().interrupt(); try { rl.lockInterruptibly(); fail("thread should be in interrupted status"); } catch (InterruptedException e) { } finally { try { rl.unlock(); fail("unlock() should throw an IllegalStateException"); } catch (IllegalMonitorStateException illegalStateEx) { assertTrue(true); } } } public void testMultipleWriteLockAcquisitions() throws InterruptedException { wl.lock(); wl.lock(); } public void testMultipleReadLockReleases() throws InterruptedException { rl.lock(); rl.unlock(); try { rl.unlock(); fail("we should not get here, cannot lock RL once but unlock twice"); } catch (IllegalMonitorStateException illegalState) { // this is as expected } } public void testMultipleWriteLockReleases() throws InterruptedException { wl.lock(); wl.unlock(); try { wl.unlock(); fail("expected"); } catch (IllegalMonitorStateException e) { } } public void testAcquireWriteLockAfterReadLock() throws InterruptedException { rl.lock(); rl.unlock(); wl.lock(); } public void testAcquiringReadLockedLockWithRead() throws InterruptedException { new Thread() { public void run() { try { rl.lockInterruptibly(); } catch (InterruptedException e) { } } }.start(); TestingUtil.sleepThread(500); // now we have a RL by another thread boolean flag = rl.tryLock(3000, TimeUnit.MILLISECONDS); assertTrue(flag); flag = wl.tryLock(3000, TimeUnit.MILLISECONDS); assertFalse(flag); } public void testAcquiringReadLockedLock() throws InterruptedException { new Thread() { public void run() { try { rl.lockInterruptibly(); } catch (InterruptedException e) { } } }.start(); TestingUtil.sleepThread(500); // now we have a RL by another thread boolean flag = wl.tryLock(3000, TimeUnit.MILLISECONDS); assertFalse(flag); } public void testWriteThenReadByDifferentTx() throws InterruptedException { Writer writer = new Writer("Writer"); Reader reader = new Reader("Reader"); writer.start(); TestingUtil.sleepThread(500); reader.start(); TestingUtil.sleepThread(1000); synchronized (writer) { log("terminating Writer"); writer.notify(); } TestingUtil.sleepThread(500); synchronized (reader) { reader.notify(); } writer.join(); reader.join(); } public void testReadThenWriteByDifferentTx() throws InterruptedException { Writer writer = new Writer("Writer"); Reader reader = new Reader("Reader"); reader.start(); TestingUtil.sleepThread(500); writer.start(); TestingUtil.sleepThread(1000); synchronized (reader) { log("terminating Reader"); reader.notify(); } TestingUtil.sleepThread(500); synchronized (writer) { writer.notify(); } writer.join(); reader.join(); } private static void log(String msg) { System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + " [" + Thread.currentThread().getName() + "]: " + msg); } class Reader extends Thread { public Reader(String name) { super(name); } public void run() { try { log("acquiring RL"); rl.lock(); log("acquired RL"); synchronized (this) { this.wait(); } log("releasing RL"); rl.unlock(); log("released RL"); } catch (InterruptedException e) { } } } class Writer extends Thread { public Writer(String name) { super(name); } public void run() { try { log("acquiring WL"); wl.lock(); log("acquired WL"); synchronized (this) { this.wait(); } log("releasing WL"); wl.unlock(); log("released WL"); } catch (InterruptedException e) { } } } class Upgrader extends Thread { boolean upgradeSuccessful = false; public Upgrader(String name) { super(name); } public boolean wasUpgradeSuccessful() { return upgradeSuccessful; } public void run() { try { log("acquiring RL"); rl.lock(); log("acquired RL"); synchronized (this) { this.wait(); } log("attempting to lock WL"); // rl.unlock(); wl.lock(); upgradeSuccessful = true; log("acquired WL"); log("releasing WL/RL"); wl.unlock(); log("released WL/RL"); } catch (InterruptedException e) { } } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/lock/ReadWriteLockWithUpgradeTest.java0000644000175000017500000005550011017455042031736 0ustar twernertwernerpackage org.jboss.cache.lock; import org.jboss.cache.util.TestingUtil; import static org.testng.AssertJUnit.assertTrue; import static org.testng.AssertJUnit.fail; import org.testng.annotations.AfterMethod; import org.testng.annotations.Test; import java.util.Vector; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; /** * NonBlockingWriterLock is a read/write lock (with upgrade) that has * non-blocking write lock acquisition on existing read lock(s). *

Note that the write lock is exclusive among write locks, e.g., * only one write lock can be granted at one time, but the write lock * is independent of the read locks. For example, * a read lock to be acquired will be blocked if there is existing write lock, but * will not be blocked if there are mutiple read locks already granted to other * owners. On the other hand, a write lock can be acquired as long as there * is no existing write lock, regardless how many read locks have been * granted. * * @author Cavin Song April 22, 2004 * @version 1.0 */ @Test(groups = { "functional" }) public class ReadWriteLockWithUpgradeTest { static final ReadWriteLockWithUpgrade lock_ = new ReadWriteLockWithUpgrade(); static long SLEEP_MSECS = 500; Vector lockResult = new Vector(); int NO_MORE_OP = 0; int INVOKE_READ = 1; int INVOKE_WRITE = 2; int INVOKE_UPGRADE = 3; @AfterMethod public void tearDown() { cleanLockingResult(); } private static void log(String str) { System.out.println(Thread.currentThread() + ": " + java.util.Calendar.getInstance().getTime() + " : " + str); } /***************************************************************/ /* Utility functions to creat threads for RL, WL and UL */ /***************************************************************/ /** * Creates a new thread and acquires a read lock with a timeout * value specified by the caller. Optionally, the caller can request * a second read or write lock after the first read lock request. * The locking result is stored in a vector with the following * format: *

*
'case number'-'thread name'-[RL|WL|UL]-[0|1] *
*

where: *

*
'case number' is the passed in test case # by the caller. *
'thread name' is the passed in thread name by the caller. *
RL - indicating was doing read lock request. *
WL - indicating was doing write lock request. *
UL - indicating was doing upgrade lock request. *
0 - indicating the locking request failed. *
1 - indicating the locking request succeeded. *
*

* After all threads in each test case terminate, the test case * should make the following call to verify the test result: *

*
asssertTrue(checkLockingResult(expected-result-string); *
*

* 'expected-result-string' is the locking result string * described above. For example, "8-t1-RL-0" means that thread * t1 in test case #8 doing a Read Lock request expects the * operation to fail. If the expected result string can't be * found then the test case is considered FAILED (ie, either * the read lock request was successful or did not complete). *

* Each test case should also call cleanLockingResult() to reset * result vector for the next test cases. * * @param caseNum Arbitrary string for the test case number. * @param name Arbitrary string for the calling thread name. * @param msecs Milliseconds that the thread should sleep after * acquiring the read lock. * @param errMsg Error msg to log in case of error. * @param secondOP Set to NO_MORE_OP if a 2nd lock request is not required. * Set to INVOKE_READ, INVOKE_READ or INVOKE_UPGRADE * respectively if the 2nd lock is a read, write or * upgrade request respectively. */ protected Thread readThread(final String caseNum, final String name, final long msecs, final long sleepSecs, final String errMsg, final int secondOP) { return new Thread(name) { public void run() { Lock rlock = lock_.readLock(); try { if (!rlock.tryLock(msecs, TimeUnit.MILLISECONDS)) { log(caseNum + "-" + name + " requesting read lock failed!\n"); String str = caseNum + "-" + name + "-RL-0"; postLockingResult(str); return; } // OK, read lock obtained, sleep and release it. log(caseNum + "-" + name + " requesting read lock succeeded!\n"); String str = caseNum + "-" + name + "-RL-1"; postLockingResult(str); TestingUtil.sleepThread(sleepSecs); if (secondOP == INVOKE_READ) { acquireReadLock(caseNum, name, msecs, errMsg); } else if (secondOP == INVOKE_WRITE) { acquireWriteLock(caseNum, name, msecs, errMsg); } else if (secondOP == INVOKE_UPGRADE) { acquireUpgradeLock(caseNum, name, msecs, errMsg); } rlock.unlock(); log(caseNum + "-" + name + " releasing read lock.\n"); } catch (Exception ex) { } } }; } /** * Creates a new thread and acquires a write lock with a timeout * value specified by the caller. Similar to {@link #readThread readThread()} * except it's used for write locks. * * @see #readThread readThread() */ protected Thread writeThread(final String caseNum, final String name, final long msecs, final long sleepSecs, final String errMsg, final int secondOP) { return new Thread(name) { public void run() { try { Lock wlock = lock_.writeLock(); if (!wlock.tryLock(msecs, TimeUnit.MILLISECONDS)) { log(caseNum + "-" + name + " requesting write lock failed!\n"); String str = caseNum + "-" + name + "-WL-0"; postLockingResult(str); return; } // OK, write lock obtained, sleep and release it. log(caseNum + "-" + name + " requesting write lock succeeded!\n"); String str = caseNum + "-" + name + "-WL-1"; postLockingResult(str); TestingUtil.sleepThread(sleepSecs); if (secondOP == INVOKE_READ) { acquireReadLock(caseNum, name, msecs, errMsg); } else if (secondOP == INVOKE_WRITE) { acquireWriteLock(caseNum, name, msecs, errMsg); } else if (secondOP == INVOKE_UPGRADE) { acquireUpgradeLock(caseNum, name, msecs, errMsg); } wlock.unlock(); log(caseNum + "-" + name + " releasing write lock.\n"); } catch (Exception ex) { ex.printStackTrace(); } } }; } /** * Creates a new thread, acquires a read lock, sleeps for a while * and then tries to upgrade the read lock to a write one. Similar * to {@link #readThread readThread()} except it's used for upgrading * locks. * * @see #readThread readThread() */ protected Thread upgradeThread(final String caseNum, final String name, final long msecs, final String errMsg) { return new Thread(name) { public void run() { try { Lock rlock = lock_.readLock(); Lock wlock = null; if (!rlock.tryLock(msecs, TimeUnit.MILLISECONDS)) { log(caseNum + "-" + name + " requesting read lock failed!\n"); String str = caseNum + "-" + name + "-RL-0"; postLockingResult(str); return; } // OK, read lock obtained, sleep and upgrade it later. log(caseNum + "-" + name + " requesting read lock succeeded (upgrade later)!\n"); TestingUtil.sleepThread(SLEEP_MSECS / 2); String str = caseNum + "-" + name + "-UL-"; if ((wlock = lock_.upgradeLockAttempt(msecs)) == null) { log(caseNum + "-" + name + " requesting upgrade lock failed!\n"); str += "0"; } else { log(caseNum + "-" + name + " requesting upgrade lock succeeded!\n"); str += "1"; } postLockingResult(str); // Sleep again and then release the lock. TestingUtil.sleepThread(SLEEP_MSECS); if (wlock != null) { wlock.unlock(); log(caseNum + "-" + name + " releasing upgrade lock.\n"); } rlock.unlock(); } catch (Exception ex) { } } }; } /***************************************************************/ /* Utility functions to acquire RL and WL (no thread) */ /***************************************************************/ /** * This routine tries to acquire a read lock with a timeout value * passed in by the caller. Like {@link #readThread readThread()} * it then stores the locking result in the result vector depending * on the outcome of the request. */ protected void acquireReadLock(final String caseNum, final String name, final long msecs, final String errMsg) { try { Lock rlock = lock_.readLock(); if (!rlock.tryLock(msecs, TimeUnit.MILLISECONDS)) { log(caseNum + "-" + name + " requesting read lock failed!\n"); String str = caseNum + "-" + name + "-RL-0"; postLockingResult(str); return; } // OK, read lock obtained, sleep and release it. log(caseNum + "-" + name + " requesting read lock succeeded!\n"); String str = caseNum + "-" + name + "-RL-1"; postLockingResult(str); TestingUtil.sleepThread(SLEEP_MSECS); rlock.unlock(); log(caseNum + "-" + name + " releasing read lock.\n"); } catch (Exception ex) { } } /** * Same as {@link #acquireReadLock acquireReadLock()} except * it's for write lock request. */ protected void acquireWriteLock(final String caseNum, final String name, final long msecs, final String errMsg) { try { Lock wlock = lock_.writeLock(); if (!wlock.tryLock(msecs, TimeUnit.MILLISECONDS)) { log(caseNum + "-" + name + " requesting write lock failed!\n"); String str = caseNum + "-" + name + "-WL-0"; postLockingResult(str); return; } // OK, write lock obtained, sleep and release it. log(caseNum + "-" + name + " requesting write lock succeeded!\n"); String str = caseNum + "-" + name + "-WL-1"; postLockingResult(str); TestingUtil.sleepThread(SLEEP_MSECS); wlock.unlock(); log(caseNum + "-" + name + " releasing write lock.\n"); } catch (Exception ex) { } } /** * Same as {@link #acquireReadLock acquireReadLock()} except * it's for upgrade lock request. */ protected void acquireUpgradeLock(final String caseNum, final String name, final long msecs, final String errMsg) { try { Lock ulock = null; if ((ulock = lock_.upgradeLockAttempt(msecs)) == null) { log(caseNum + "-" + name + " requesting upgrade lock failed!\n"); String str = caseNum + "-" + name + "-UL-0"; postLockingResult(str); return; } // OK, write lock obtained, sleep and release it. log(caseNum + "-" + name + " requesting upgrade lock succeeded!\n"); String str = caseNum + "-" + name + "-UL-1"; postLockingResult(str); TestingUtil.sleepThread(SLEEP_MSECS); ulock.unlock(); log(caseNum + "-" + name + " releasing upgrade lock.\n"); } catch (Exception ex) { } } /***************************************************************/ /* Synchronized methods handling locking result vector */ /***************************************************************/ /** * Clean/remove all locking results in the vector. */ protected synchronized void cleanLockingResult() { lockResult.removeAllElements(); } /** * Post a locking result to the vector for later verification. */ protected synchronized void postLockingResult(Object obj) { log(" Added *" + obj + "* to the result vector\n"); // Make sure we only have one in the vector //if (!checkLockingResult((String)obj)) lockResult.addElement(obj); } /** * Check if a given expected locking result is in the vector. */ protected synchronized boolean checkLockingResult(String expected) { boolean rc = false; for (int i = 0; i < lockResult.size(); i++) { Object ele = lockResult.elementAt(i); String str = (String) ele; if (expected.equals(str)) { rc = true; break; } } if (rc) { log(" Searching for *" + expected + "* SUCCEEDED.\n"); } else { log(" Searching for *" + expected + "* FAILED.\n"); } return rc; } /***************************************************************/ /* T e s t C a s e s */ /***************************************************************/ /** * Case #10 - T1 acquires RL, T2 acquires RL followed by WL. */ public void testWriteWithMultipleReaders() throws Exception { String caseNum = "10"; Thread t1 = readThread(caseNum, "t1", 0, SLEEP_MSECS * 2, "1st read lock attempt failed", NO_MORE_OP); Thread t2 = readThread(caseNum, "t2", 0, SLEEP_MSECS, "2nd read lock attempt failed", INVOKE_WRITE); t1.start(); t2.start(); t1.join(3000); t2.join(3000); assertTrue(checkLockingResult(caseNum + "-t1-RL-1") && checkLockingResult(caseNum + "-t2-RL-1") && checkLockingResult(caseNum + "-t2-WL-0")); cleanLockingResult(); // possilbe deadlock check if (t1.isAlive() || t2.isAlive()) { fail("Possible deadlock resulted in testRead."); } } /** * Case #11 - T1 acquires RL followed by WL, T2 acquires RL. */ public void testUpgradeWithMultipleReadersOn1() throws Exception { String caseNum = "11"; Thread t1 = readThread(caseNum, "t1", 0, SLEEP_MSECS, "1st read lock attempt failed", INVOKE_WRITE); Thread t2 = readThread(caseNum, "t2", 0, SLEEP_MSECS * 2, "2nd read lock attempt failed", NO_MORE_OP); t1.start(); t2.start(); t1.join(3000); t2.join(3000); assertTrue(checkLockingResult(caseNum + "-t1-RL-1") && checkLockingResult(caseNum + "-t2-RL-1") && checkLockingResult(caseNum + "-t1-WL-0")); cleanLockingResult(); // possilbe deadlock check if (t1.isAlive() || t2.isAlive()) { fail("Possible deadlock resulted in testRead."); } } /** * Case #2 - T1 acquires RL followed by UL. */ public void testUpgradeReadLock() throws Exception { String caseNum = "2"; Thread t1 = readThread(caseNum, "t1", 0, SLEEP_MSECS, "1st read lock attempt failed", INVOKE_UPGRADE); t1.start(); t1.join(3000); assertTrue(checkLockingResult(caseNum + "-t1-RL-1") && checkLockingResult(caseNum + "-t1-UL-1")); cleanLockingResult(); } /** * Case #3 - T1 acquires RL followed by WL. */ public void testReadThenWrite() throws Exception { String caseNum = "3"; acquireReadLock(caseNum, "t1", 0, "1st read lock attempt failed"); acquireWriteLock(caseNum, "t1.1", 0, "2nd write lock attempt failed"); assertTrue(checkLockingResult(caseNum + "-t1-RL-1") && checkLockingResult(caseNum + "-t1.1-WL-1")); cleanLockingResult(); } /** * Case #5 - T1 acquires WL followed by RL. */ public void testWriteThenRead() throws Exception { String caseNum = "5"; acquireWriteLock(caseNum, "t1", 0, "1st write lock attempt failed"); acquireReadLock(caseNum, "t1.1", 0, "2nd read lock attempt failed"); assertTrue(checkLockingResult(caseNum + "-t1-WL-1") && checkLockingResult(caseNum + "-t1.1-RL-1")); cleanLockingResult(); } /** * Case #6 - T1 acquires RL, T2 acquires RL. */ public void testMultipleReadlock() throws Exception { String caseNum = "6"; Thread t1 = readThread(caseNum, "t1", 0, SLEEP_MSECS, "1st read lock attempt failed", NO_MORE_OP); Thread t2 = readThread(caseNum, "t2", 0, SLEEP_MSECS, "2nd read lock attempt failed", NO_MORE_OP); t1.start(); t2.start(); t1.join(3000); t2.join(3000); assertTrue(checkLockingResult(caseNum + "-t1-RL-1") && checkLockingResult(caseNum + "-t2-RL-1")); cleanLockingResult(); // possilbe deadlock check if (t1.isAlive() || t2.isAlive()) { fail("Possible deadlock resulted in testRead."); } } /** * Case #8 - T1 acquires WL, T2 acquires RL. */ public void testWriteWithExistingReader() throws Exception { String caseNum = "8"; Thread t1 = readThread(caseNum, "t1", 0, SLEEP_MSECS, "1st write lock attempt failed", NO_MORE_OP); Thread t2 = writeThread(caseNum, "t2", 0, SLEEP_MSECS, "2nd read lock attempt failed", NO_MORE_OP); t1.start(); t2.start(); t1.join(3000); t2.join(3000); // there is NO guarantee as to which thread will get the lock!! boolean t0GetsLock = checkLockingResult(caseNum + "-t1-RL-1") && checkLockingResult(caseNum + "-t2-WL-0"); boolean t1GetsLock = checkLockingResult(caseNum + "-t1-RL-0") && checkLockingResult(caseNum + "-t2-WL-1"); assert !(t0GetsLock && t1GetsLock); // both can't be true assert t0GetsLock || t1GetsLock; // one must be true cleanLockingResult(); // possilbe deadlock check if (t1.isAlive() || t2.isAlive()) { fail("Possible deadlock resulted in testRead."); } } /** * Case #13 - T1 acquires RL, T2 acquires WL. */ public void testReadWithExistingWriter() throws Exception { String caseNum = "13"; Thread t1 = writeThread(caseNum, "t1", 0, SLEEP_MSECS, "1st write lock attempt failed", NO_MORE_OP); Thread t2 = readThread(caseNum, "t2", 0, SLEEP_MSECS, "2nd read lock attempt failed", NO_MORE_OP); t1.start(); t2.start(); t1.join(3000); t2.join(3000); // there is NO guarantee as to which thread will get the lock!! boolean t0GetsLock = checkLockingResult(caseNum + "-t1-WL-1") && checkLockingResult(caseNum + "-t2-RL-0"); boolean t1GetsLock = checkLockingResult(caseNum + "-t1-WL-0") && checkLockingResult(caseNum + "-t2-RL-1"); assert !(t0GetsLock && t1GetsLock); // both can't be true assert t0GetsLock || t1GetsLock; // one must be true cleanLockingResult(); // possilbe deadlock check if (t1.isAlive() || t2.isAlive()) { fail("Possible deadlock resulted in testRead."); } } /** * Case #14 - T1 acquires WL, T2 acquires WL. */ public void testMultipleWritelocks() throws Exception { String caseNum = "14"; Thread t1 = writeThread(caseNum, "t1", 0, SLEEP_MSECS, "1st write lock attempt failed", NO_MORE_OP); Thread t2 = writeThread(caseNum, "t2", 0, SLEEP_MSECS, "2nd write lock attempt failed", NO_MORE_OP); t1.start(); t2.start(); t1.join(3000); t2.join(3000); // there is NO guarantee as to which thread will get the lock!! boolean t0GetsLock = checkLockingResult(caseNum + "-t1-WL-1") && checkLockingResult(caseNum + "-t2-WL-0"); boolean t1GetsLock = checkLockingResult(caseNum + "-t1-WL-0") && checkLockingResult(caseNum + "-t2-WL-1"); assert !(t0GetsLock && t1GetsLock); // both can't be true assert t0GetsLock || t1GetsLock; // one must be true cleanLockingResult(); // possilbe deadlock check if (t1.isAlive() || t2.isAlive()) { fail("Possible deadlock resulted in testRead."); } } /** * Case #7 - T1 acquires RL, T2 acquires UL. */ public void testUpgradeWithExistingReader() throws Exception { String caseNum = "7"; Thread t1 = readThread(caseNum, "t1", 0, SLEEP_MSECS, "1st read lock attempt failed", NO_MORE_OP); Thread t2 = upgradeThread(caseNum, "t2", 0, "2nd upgrade lock attempt failed"); t1.start(); t2.start(); t1.join(3000); t2.join(3000); assertTrue(checkLockingResult(caseNum + "-t1-RL-1") && checkLockingResult(caseNum + "-t2-UL-0")); cleanLockingResult(); // possilbe deadlock check if (t1.isAlive() || t2.isAlive()) { fail("Possible deadlock resulted in testRead."); } } /** * Case #9 - T1 acquires RL, T2 acquires RL followed by UL. */ public void testUpgradeWithMultipleReaders() throws Exception { String caseNum = "9"; Thread t1 = readThread(caseNum, "t1", 0, SLEEP_MSECS * 2, "1st read lock attempt failed", NO_MORE_OP); Thread t2 = readThread(caseNum, "t2", 0, SLEEP_MSECS, "2nd read lock attempt failed", INVOKE_UPGRADE); t1.start(); t2.start(); t1.join(3000); t2.join(3000); assertTrue(checkLockingResult(caseNum + "-t1-RL-1") && checkLockingResult(caseNum + "-t2-RL-1") && checkLockingResult(caseNum + "-t2-UL-0")); cleanLockingResult(); // possilbe deadlock check if (t1.isAlive() || t2.isAlive()) { fail("Possible deadlock resulted in testRead."); } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/lock/IdentityLockTest.java0000644000175000017500000003553611031017377027505 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.lock; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.NodeSPI; import org.jboss.cache.transaction.GlobalTransaction; import org.jboss.cache.util.TestingUtil; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * Testing of different locking semantics. * * @author Bela Ban * @author Ben Wang * @version $Revision: 6073 $ */ @Test(groups = {"functional"}) public class IdentityLockTest { NodeLock lock_; Object other_ = new Object(); Log logger_ = LogFactory.getLog(IdentityLockTest.class); static Throwable thread_ex = null; final NodeSPI NODE = null; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { lock_ = new IdentityLock(new LockStrategyFactory(), NODE); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { lock_.releaseAll(); lock_ = null; thread_ex = null; } private void setLevelRW() { log("set lock level to RWUpgrade ..."); LockStrategyFactory lsf = new LockStrategyFactory(); lsf.setIsolationLevel(IsolationLevel.REPEATABLE_READ); lock_ = new IdentityLock(lsf, NODE); } private void setLevelSerial() { log("set lock level to SimpleLock ..."); LockStrategyFactory lsf = new LockStrategyFactory(); lsf.setIsolationLevel(IsolationLevel.SERIALIZABLE); lock_ = new IdentityLock(lsf, NODE); } private GlobalTransaction getGlobalTransactionFromThread() { return GlobalTransaction.create(null); } public void testNullOwner_RWLock() throws InterruptedException { setLevelRW(); nullOwner(); } public void testNullOwner_SimpleLock() throws InterruptedException { setLevelSerial(); nullOwner(); } private void nullOwner() throws InterruptedException { log("testNullOwner ..."); try { GlobalTransaction gtx = getGlobalTransactionFromThread(); lock_.acquireWriteLock(gtx, 50); lock_.release(gtx); lock_.acquireReadLock(gtx, 50); lock_.release(gtx); } catch (LockingException e) { fail(e.toString()); } catch (TimeoutException e) { fail(e.toString()); } } public void testNullOwner2_RWLock() throws InterruptedException { setLevelRW(); nullOwner2(); } public void testNullOwner2_SimpleLock() throws InterruptedException { setLevelSerial(); nullOwner2(); } private void nullOwner2() throws InterruptedException { log("testNullOwner2 ..."); try { GlobalTransaction gtx = getGlobalTransactionFromThread(); lock_.acquireReadLock(gtx, 50); lock_.acquireWriteLock(gtx, 50);// this should succeed lock_.release(gtx); } catch (LockingException e) { fail(e.toString()); } catch (TimeoutException e2) { fail(e2.toString()); } } public void testNullOwner3_RWLock() throws InterruptedException { setLevelRW(); nullOwner3(); } public void testNullOwner3_SimpleLock() throws InterruptedException { setLevelSerial(); nullOwner3(); } private void nullOwner3() throws InterruptedException { log("testNullOwner3 ..."); try { GlobalTransaction gtx = getGlobalTransactionFromThread(); lock_.acquireWriteLock(gtx, 50); lock_.acquireReadLock(gtx, 50);// this should succeed lock_.release(gtx); } catch (LockingException e) { fail(e.toString()); } catch (TimeoutException e2) { fail(e2.toString()); } } public void testAcquireAndRelease_RWLock() throws InterruptedException { setLevelRW(); acquireAndRelease(); } public void testAcquireAndRelease_SimpleLock() throws InterruptedException { setLevelSerial(); acquireAndRelease(); } private void acquireAndRelease() throws InterruptedException { log("testAcquireAndRelease ..."); try { lock_.acquireReadLock(this, 50); assertTrue("Is the lock owner", lock_.isOwner(this)); assertTrue(lock_.getReaderOwners().contains(this)); lock_.acquireReadLock(this, 50);// this should succeed assertTrue("Is the lock owner", lock_.isOwner(this)); assertTrue(lock_.getReaderOwners().contains(this)); lock_.acquireWriteLock(this, 50);// this should succeed assertTrue("Is the lock owner", lock_.isOwner(this)); assertTrue(!lock_.getReaderOwners().contains(this)); assertTrue(lock_.getWriterOwner().equals(this)); lock_.release(this); assertTrue(!lock_.isOwner(this)); } catch (LockingException e) { fail(e.toString()); } catch (TimeoutException e2) { fail(e2.toString()); } } public void testThreadedAccess_RWLock() throws Throwable { setLevelRW(); log("testThreadedAccess_RWLock ..."); final Object o1 = new Object(); final Object o2 = new Object(); System.out.println(""); // 1. o1 acquires the lock -- succeeds Thread t1 = new Thread() { public void run() { try { log("o1 acquiring lock"); lock_.acquireReadLock(o1, 50); log("o1: OK"); } catch (Throwable e) { log("o1: FAIL"); thread_ex = e; } } }; // 2. o2 wants to acquire the lock -- this will fail and o2 will block for 2 secs Thread t2 = new Thread() { public void run() { try { log("o2 acquiring lock"); lock_.acquireWriteLock(o2, 2000); log("o2: OK"); } catch (Throwable e) { log("o2: FAIL"); thread_ex = e; } } }; // 3. o1 acquires the lock a second time -- succeeds Thread t3 = new Thread() { public void run() { try { log("o1 acquiring lock"); lock_.acquireWriteLock(o1, 10); log("o1: OK"); } catch (Throwable e) { log("o1: FAIL"); thread_ex = e; } } }; t1.start(); TestingUtil.sleepThread(100); t2.start(); TestingUtil.sleepThread(1000); // o1 must be the owner of the lock assertTrue(lock_.isOwner(o1)); TestingUtil.sleepThread(100); // o1 must still be the owner of the lock assertTrue(lock_.isOwner(o1)); t3.start(); TestingUtil.sleepThread(100); // o1 must still be the owner of the lock assertTrue(lock_.isOwner(o1)); // 4. o1 releases the lock; now o2 will succeed in acquiring the lock log("o1 releasing lock"); lock_.release(o1); log("o1: OK"); TestingUtil.sleepThread(200); //log("o2: " + o2.hashCode() + ", lock_.getOwner()=" + lock_.getOwner()); // assertTrue(lock_.isOwner(o2)); // lock_.release(o2); t1.join(20000); t2.join(20000); t3.join(20000); if (thread_ex != null) { throw thread_ex; } } public void testThreadedAccess_SimpleLock() throws Throwable { setLevelSerial(); log("testThreadedAccess_SimpleLock() ..."); final Object o1 = new Object(); final Object o2 = new Object(); System.out.println(""); // 1. o1 acquires the lock -- succeeds Thread t1 = new Thread() { public void run() { try { log("o1 acquiring lock"); lock_.acquireReadLock(o1, 50); log("o1: OK"); } catch (Throwable e) { log("o1: FAIL"); thread_ex = e; } } }; // 2. o2 wants to acquire the lock -- this will fail and o2 will block for 2 secs Thread t2 = new Thread() { public void run() { try { log("o2 acquiring lock"); lock_.acquireWriteLock(o2, 2000); log("o2: OK"); } catch (Throwable e) { log("o2: FAIL"); thread_ex = e; } } }; // 3. o1 acquires the lock a second time -- succeeds Thread t3 = new Thread() { public void run() { try { log("o1 acquiring lock"); lock_.acquireWriteLock(o1, 10); log("o1: OK"); } catch (Throwable e) { log("o1: FAIL"); thread_ex = e; } } }; t1.start(); // make sure t1 has the WL! t1.join(); t2.start(); TestingUtil.sleepThread(1000); // o1 must be the owner of the lock assertTrue(lock_.isOwner(o1)); TestingUtil.sleepThread(100); // o1 must still be the owner of the lock assertTrue(lock_.isOwner(o1)); t3.start(); TestingUtil.sleepThread(100); // o1 must still be the owner of the lock assertTrue(lock_.isOwner(o1)); // 4. o1 releases the lock; now o2 will succeed in acquiring the lock log("o1 releasing lock"); lock_.release(o1); log("o1: OK"); TestingUtil.sleepThread(200); //log("o2: " + o2.hashCode() + ", lock_.getOwner()=" + lock_.getOwner()); // assertTrue(lock_.isOwner(o2)); // lock_.release(o2); // t1.join(20000); t2.join(20000); t3.join(20000); if (thread_ex != null) { throw thread_ex; } } public void testReadAndReleaseAll() { setLevelRW(); log("testReadAndReleaseAll() ..."); final Object o1 = new Object(); final Object o2 = new Object(); System.out.println(""); // 1. o1 acquires the lock -- succeeds try { log("o1: acquiring"); lock_.acquireReadLock(o1, 50); log("o1: OK"); log("o2: acquiring"); lock_.acquireReadLock(o2, 50); log("o2: OK"); } catch (Throwable t) { log("read lock: FAIL"); fail(t.getMessage()); } Thread t1 = new Thread() { public void run() { try { log("calling releaseAll()"); lock_.releaseAll(); log("releaseAll(): OK"); } catch (Throwable e) { log("releaseAll(): FAIL"); thread_ex = e; } } }; try { t1.setDaemon(true); t1.start(); TestingUtil.sleepThread(1000); assertFalse("Lock map cleared", lock_.isReadLocked()); } finally { // Manually release the locks so tearDown() will not fail // if there is a problem with releaseAll() lock_.release(o1); lock_.release(o2); } } public void testWriteAndReleaseAll() { setLevelSerial(); log("testWriteAndReleaseAll() ..."); final Object o1 = new Object(); System.out.println(""); // 1. o1 acquires the lock -- succeeds try { log("o1: acquiring"); lock_.acquireWriteLock(o1, 50); log("o1: OK"); } catch (Throwable t) { log("write lock: FAIL"); fail(t.getMessage()); } Thread t1 = new Thread() { public void run() { try { log("calling releaseAll()"); lock_.releaseAll(); log("releaseAll(): OK"); } catch (Throwable e) { log("releaseAll(): FAIL"); thread_ex = e; } } }; try { t1.setDaemon(true); t1.start(); TestingUtil.sleepThread(1000); assertFalse("Lock map cleared", lock_.isReadLocked()); } finally { // Manually release the lock so tearDown() will not fail // if there is a problem with releaseAll() lock_.release(o1); } } void log(String msg) { // System.out.println("-- [" + Thread.currentThread() + "]: " + msg); logger_.info("-- [" + Thread.currentThread() + "]: " + msg); } /** * When IdentityLock.toString is called and readLockOwners are modified an ConcurrentModificationException exception * might be thrown. */ public void testConcurrentModificationOfReadLocksAndToString() throws Exception { final IdentityLock iLock = (IdentityLock) lock_; final LockMap lockMap = iLock.getLockMap(); final int opCount = 100000; final Thread readLockChanger = new Thread() { public void run() { for (int i = 0; i < opCount; i++) { if (i % 10 == 0) System.out.println("readLockChanger loop# " + i); lockMap.addReader(new Object()); } } }; final boolean[] flags = new boolean[]{false, false}; //{testFailure, stopTheThread} Thread toStringProcessor = new Thread() { public void run() { long initialTime = System.currentTimeMillis(); for (int i = 0; i < opCount; i++) { if (i % 10 == 0) { System.out.println("toStringProcessor loop# " + i + ", " + (System.currentTimeMillis() - initialTime)); initialTime = System.currentTimeMillis(); } try { iLock.toString(new StringBuilder(), false); } catch (Exception e) { e.printStackTrace(); flags[0] = true; break; } if (flags[1]) break; } } }; toStringProcessor.start(); System.out.println("toStringProcessor started"); readLockChanger.start(); System.out.println("readLockChanger started"); readLockChanger.join(); flags[1] = true;//stopping the toStringProcessor System.out.println("readLockChanger stopped"); toStringProcessor.join(); System.out.println("toStringProcessor stopped"); System.out.println("flags[0]=" + flags[0]); assertFalse(flags[0]); } } ././@LongLink0000000000000000000000000000016200000000000011564 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/lock/ReentrantWriterPreference2Readers1WriterLockTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/lock/ReentrantWriterPreference2Readers1Writer0000644000175000017500000001240710727545014033324 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.lock; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantReadWriteLock; /** * Tests ReentrantWriterPreferenceReadWriteLock * * @author Bela Ban * @version $Id: ReentrantWriterPreference2Readers1WriterLockTest.java 4836 2007-12-11 17:34:04Z manik.surtani@jboss.com $ */ @Test(groups = {"functional"}, enabled = false) // historical disabled - and wont fix. See JBCACHE-461 public class ReentrantWriterPreference2Readers1WriterLockTest { ReentrantReadWriteLock lock; ReentrantReadWriteLock.ReadLock rl; ReentrantReadWriteLock.WriteLock wl; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { lock = new ReentrantReadWriteLock(); rl = lock.readLock(); wl = lock.writeLock(); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { lock = null; } private void log(String msg) { System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + " [" + Thread.currentThread().getName() + "]: " + msg); } public void testSimpleUpgradeFromReadLockToWriteLock() { int readers, writers; try { rl.lock(); readers = lock.getReadLockCount(); assertEquals(1, readers); boolean wl_acquired = wl.tryLock(500, TimeUnit.MILLISECONDS); if (!wl_acquired) { fail("write lock could not be acquired"); return; } readers = lock.getReadLockCount(); assertEquals(1, readers); writers = lock.getWriteHoldCount(); assertEquals(1, writers); } catch (InterruptedException e) { } finally { rl.unlock(); if (lock.getWriteHoldCount() > 0) wl.unlock(); } } public void test2ReadersAnd1Writer() throws InterruptedException { int readers, writers; Upgrader upgrader = new Upgrader("Upgrader"); Reader reader = new Reader("Reader"); reader.start(); sleepThread(500); readers = lock.getReadLockCount(); assertEquals(1, readers); upgrader.start(); sleepThread(500); readers = lock.getReadLockCount(); assertEquals(2, readers); synchronized (upgrader) { // writer upgrades from RL to WL, this should fail upgrader.notify(); } sleepThread(500); readers = lock.getReadLockCount(); assertEquals(2, readers); writers = lock.getWriteHoldCount(); assertEquals(0, writers); synchronized (reader) { // reader unlocks its RL, now writer should be able to upgrade to a WL reader.notify(); } reader.join(); readers = lock.getReadLockCount(); assertEquals(1, readers); writers = lock.getWriteHoldCount(); assertEquals(1, writers); synchronized (upgrader) { // writer releases WL upgrader.notify(); } sleepThread(500); readers = lock.getReadLockCount(); assertEquals(0, readers); writers = lock.getWriteHoldCount(); assertEquals(0, writers); upgrader.join(3000); assertTrue("Known failure. See JBCACHE-461; This is due to a potential bug in ReentrantWriterPreferenceReadWriteLock !", upgrader.wasUpgradeSuccessful()); } private class Reader extends Thread { public Reader(String name) { super(name); } public void run() { try { log("acquiring RL"); rl.lock(); log("acquired RL"); synchronized (this) { this.wait(); } } catch (InterruptedException e) { } finally { log("releasing RL"); rl.unlock(); log("released RL"); } } } private class Upgrader extends Thread { boolean upgradeSuccessful = false; public Upgrader(String name) { super(name); } public boolean wasUpgradeSuccessful() { return upgradeSuccessful; } public void run() { try { log("acquiring RL"); rl.lock(); log("acquired RL"); synchronized (this) { this.wait(); } log("attempting to acquire WL"); wl.lock(); upgradeSuccessful = true; log("acquired WL"); synchronized (this) { this.wait(); } log("releasing WL"); rl.unlock(); log("released WL"); } catch (InterruptedException e) { } finally { wl.unlock(); rl.unlock(); } } } static void sleepThread(long timeout) { try { Thread.sleep(timeout); } catch (InterruptedException e) { } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/lock/ReadWriteLockTest.java0000644000175000017500000000737510665031725027607 0ustar twernertwernerpackage org.jboss.cache.lock; import static org.testng.AssertJUnit.fail; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * Tests the various ReadWriteLock implementations * @author Bela Ban * @version $Id: ReadWriteLockTest.java 4444 2007-08-28 14:40:21Z jason.greene@jboss.com $ */ @Test(groups = { "functional" }) public class ReadWriteLockTest { ReadWriteLock lock; Exception ex = null; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { ex = null; } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { lock = null; if (ex != null) throw ex; } public void testMoreWriteReleasesThanAcquisitions() throws InterruptedException { lock = new ReentrantReadWriteLock(); lock.writeLock().lock(); lock.writeLock().unlock(); try { lock.writeLock().unlock(); fail("Should have barfed"); } catch (IllegalMonitorStateException imse) { // should barf } } public void testMoreReadReleasesThanAcquisitions() throws InterruptedException { lock = new ReentrantReadWriteLock(); lock.readLock().lock(); lock.readLock().unlock(); try { lock.readLock().unlock(); fail("read locks cannot be released more than acquired"); } catch (IllegalMonitorStateException illegalStateEx) { } } public void testSimple() throws InterruptedException { lock = new ReentrantReadWriteLock(); lock.readLock().lock(); // upgrades must be manual; involving giving up the RL first. Sucks. // see http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/locks/ReentrantReadWriteLock.html lock.readLock().unlock(); lock.writeLock().lock(); lock.writeLock().lock(); lock.readLock().lock(); lock.readLock().lock(); // since the thread currently has a WL and we are reentrant, this works without a manual upgrade. lock.writeLock().lock(); lock.writeLock().lock(); } public void testOneWriterMultipleReaders() throws InterruptedException { lock = new ReentrantReadWriteLock(); Writer writer = new Writer("writer"); Reader reader1 = new Reader("reader1"); Reader reader2 = new Reader("reader2"); writer.start(); reader1.start(); reader2.start(); writer.join(); reader1.join(); reader2.join(); } class Writer extends Thread { public Writer(String name) { super(name); } public void run() { try { log("acquiring WL"); lock.writeLock().lock(); log("acquired WL successfully"); sleep(1000); } catch (InterruptedException e) { ex = e; } finally { log("releasing WL"); lock.writeLock().unlock(); } } } class Reader extends Thread { public Reader(String name) { super(name); } public void run() { try { log("acquiring RL"); lock.readLock().lock(); log("acquired RL successfully"); sleep(500); } catch (InterruptedException e) { ex = e; } finally { log("releasing RL"); lock.readLock().unlock(); } } } static void log(String msg) { System.out.println(Thread.currentThread().getName() + ": " + msg); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/lock/pessimistic/0000755000175000017500000000000011376173741025732 5ustar twernertwerner././@LongLink0000000000000000000000000000014500000000000011565 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/lock/pessimistic/ConcurrentPutRemoveTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/lock/pessimistic/ConcurrentPutRemoveTest.java0000644000175000017500000000772711017455042033430 0ustar twernertwernerpackage org.jboss.cache.lock.pessimistic; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.Cache; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.config.Configuration; import org.jboss.cache.lock.IsolationLevel; import org.jboss.cache.transaction.DummyTransactionManagerLookup; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.TransactionManager; import java.util.ArrayList; import java.util.List; // This is disabled because the fix is not absolute and will require pretty bug architectural changes. // There is an edge case where a NodeNotFoundException may occur, and this is due to parent nodes not being // write locked when children are added/removed. // // The problem is in the way READ_COMMITTED is implemented, i.e., writers are not blocked by readers and this // allows a reader to hold a lock when a writer comes in and deletes the node in question. @Test(groups = "functional", enabled = false) // Known issue - See JBCACHE-1164 and JBCACHE-1165 public class ConcurrentPutRemoveTest { private TransactionManager tm; static int count = 0; private Cache cache; private final Log log = LogFactory.getLog(ConcurrentPutRemoveTest.class); private List threads; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { cache = new DefaultCacheFactory().createCache(false); cache.getConfiguration().setCacheMode(Configuration.CacheMode.LOCAL); cache.getConfiguration().setIsolationLevel(IsolationLevel.READ_COMMITTED); cache.getConfiguration().setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName()); cache.getConfiguration().setLockAcquisitionTimeout(10000); cache.start(); tm = cache.getConfiguration().getRuntimeConfig().getTransactionManager(); threads = new ArrayList(); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { TestingUtil.killCaches(cache); for (SeparateThread st : threads) { st.interrupt(); st.join(); } } @Test(invocationCount = 500, enabled = false) // TODO: 3.0.0: This is still a known failure. MVCC in 3.0.0 will fix this; enable it in 3.0.0. public void testLock() throws Exception { System.out.println("ConcurrentPutRemoveTest.testLock count = " + (++count)); for (int x = 0; x < 2; x++) { SeparateThread t = new SeparateThread(x); threads.add(t); t.start(); } for (SeparateThread separateThread : threads) { separateThread.join(); if (separateThread.getException() != null) { throw separateThread.getException(); } } } private class SeparateThread extends Thread { Exception e = null; private int num = 0; public SeparateThread(int num) { this.num = num; } public Exception getException() { return e; } public void run() { Thread.currentThread().setName("Thread:" + num); try { for (int x = 0; x < 1000; x++) { tm.begin(); log.warn("Before Remove (" + x + ")"); //inside transaction cache.removeNode(Fqn.fromString("/a")); log.warn("After Remove (" + x + ")"); tm.commit(); //outside transaction log.warn("Before Put (" + x + ")"); cache.put(Fqn.fromString("/a/b/c/d"), "text" + x, "b"); log.warn("After Put (" + x + ")"); } } catch (Exception e) { log.error("*** error on a thread", e); // System.exit(1); this.e = e; } } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/lock/UpgradeLockTest.java0000644000175000017500000001210510732232543027267 0ustar twernertwerner/* * * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.lock; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.transaction.DummyTransactionManager; import static org.testng.AssertJUnit.assertEquals; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.naming.Context; import javax.naming.InitialContext; import javax.transaction.UserTransaction; import java.util.Properties; /** * Tests upgrade locks from read -> write * * @author Bela Ban * @version $Id: UpgradeLockTest.java 4880 2007-12-19 15:14:43Z manik.surtani@jboss.com $ */ @Test(groups = {"functional"}) public class UpgradeLockTest { CacheSPI cache = null; UserTransaction tx = null; Properties p = null; String old_factory = null; final String FACTORY = "org.jboss.cache.transaction.DummyContextFactory"; final String NODE1 = "/test"; final String NODE2 = "/my/test"; final String KEY = "key"; final String VAL1 = "val1"; final String VAL2 = "val2"; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { old_factory = System.getProperty(Context.INITIAL_CONTEXT_FACTORY); System.setProperty(Context.INITIAL_CONTEXT_FACTORY, FACTORY); DummyTransactionManager.getInstance(); if (p == null) { p = new Properties(); p.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.cache.transaction.DummyContextFactory"); } tx = (UserTransaction) new InitialContext(p).lookup("UserTransaction"); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { if (cache != null) { cache.stop(); } // BW. kind of a hack to destroy jndi binding and thread local tx before next run. DummyTransactionManager.destroy(); if (old_factory != null) { System.setProperty(Context.INITIAL_CONTEXT_FACTORY, old_factory); old_factory = null; } if (tx != null) { try { tx.rollback(); } catch (Throwable t) { } tx = null; } } private CacheSPI createCache(IsolationLevel level) { CacheSPI c = (CacheSPI) new DefaultCacheFactory().createCache(false); c.getConfiguration().setClusterName("test"); c.getConfiguration().setStateRetrievalTimeout(10000); c.getConfiguration().setTransactionManagerLookupClass("org.jboss.cache.transaction.JBossTransactionManagerLookup"); c.getConfiguration().setLockAcquisitionTimeout(500); c.getConfiguration().setIsolationLevel(level); c.create(); c.start(); return c; } public void testUpgradeWithNone() throws Exception { runTestWithIsolationLevel(IsolationLevel.NONE); } public void testUpgradeWithReadUncommitted() throws Exception { runTestWithIsolationLevel(IsolationLevel.READ_UNCOMMITTED); } public void testUpgradeWithReadCommitted() throws Exception { runTestWithIsolationLevel(IsolationLevel.READ_COMMITTED); } public void testUpgradeWithRepeatableRead() throws Exception { runTestWithIsolationLevel(IsolationLevel.REPEATABLE_READ); } public void testUpgradeWithSerializable() throws Exception { runTestWithIsolationLevel(IsolationLevel.SERIALIZABLE); } public void testIsolationLevelSerializable() throws Exception { _testIsolationLevel(IsolationLevel.SERIALIZABLE); } public void testIsolationLevelNone() throws Exception { _testIsolationLevel(IsolationLevel.NONE); } private void _testIsolationLevel(IsolationLevel l) throws Exception { cache = createCache(l); tx.begin(); int expected_num_locks = l == IsolationLevel.NONE ? 0 : 2; cache.put(NODE1, null); assertEquals(expected_num_locks, cache.getNumberOfLocksHeld()); cache.put(NODE1, null); assertEquals(expected_num_locks, cache.getNumberOfLocksHeld()); tx.rollback(); assertEquals(0, cache.getNumberOfLocksHeld()); } private void runTestWithIsolationLevel(IsolationLevel level) throws Exception { cache = createCache(level); // add initial values outside of TX cache.put(NODE1, KEY, VAL1); cache.put(NODE2, KEY, VAL1); tx.begin(); try { assertEquals(VAL1, cache.get(NODE1, KEY)); assertEquals(VAL1, cache.get(NODE2, KEY)); cache.put(NODE1, KEY, VAL2);// causes read lock to upgrade to r/w lock cache.put(NODE2, KEY, VAL2);// causes read lock to upgrade to r/w lock assertEquals(VAL2, cache.get(NODE1, KEY)); assertEquals(VAL2, cache.get(NODE2, KEY)); tx.commit(); } catch (Throwable t) { if (tx != null) { tx.rollback(); } } assertEquals(VAL2, cache.get(NODE1, KEY)); assertEquals(VAL2, cache.get(NODE2, KEY)); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/lock/WriteLockOnParentTest.java0000644000175000017500000001635610732232543030455 0ustar twernertwernerpackage org.jboss.cache.lock; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.Transaction; import javax.transaction.TransactionManager; import java.util.Collections; @Test(groups = {"functional"}) public class WriteLockOnParentTest { private CacheSPI cache; private TransactionManager tm; private Fqn a = Fqn.fromString("/a"), a_b = Fqn.fromString("/a/b"), a_c = Fqn.fromString("/a/c"); @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { cache = (CacheSPI) new DefaultCacheFactory().createCache(false); cache.getConfiguration().setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); // reduce LAT so the test runs faster cache.getConfiguration().setLockAcquisitionTimeout(500); cache.start(); tm = cache.getTransactionManager(); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { if (tm.getTransaction() != null) { try { tm.rollback(); } catch (Exception e) { // do sweet F.A. } } cache.stop(); } public void testDefaultCfg() { assertFalse("Locking of parent nodes for child inserts and removes should be false by default", cache.getConfiguration().isLockParentForChildInsertRemove()); } public void testDefaultChildInsert() throws Exception { cache.put(a, Collections.emptyMap()); assertNotNull("/a should exist", cache.peek(a, false)); // concurrent insert of /a/b and /a/c tm.begin(); cache.put(a_b, Collections.emptyMap()); Transaction t1 = tm.suspend(); tm.begin(); cache.put(a_c, Collections.emptyMap()); tm.commit(); tm.resume(t1); tm.commit(); assertNotNull("/a/b should exist", cache.peek(a_b, false)); assertNotNull("/a/c should exist", cache.peek(a_c, false)); } public void testLockParentChildInsert() throws Exception { cache.getConfiguration().setLockParentForChildInsertRemove(true); cache.put(a, Collections.emptyMap()); assertNotNull("/a should exist", cache.peek(a, false)); // concurrent insert of /a/b and /a/c tm.begin(); cache.put(a_b, Collections.emptyMap()); Transaction t1 = tm.suspend(); tm.begin(); try { cache.put(a_c, Collections.emptyMap()); fail("Should not get here."); } catch (TimeoutException e) { // expected } tm.commit(); tm.resume(t1); tm.commit(); assertNotNull("/a/b should exist", cache.peek(a_b, false)); assertNull("/a/c should not exist", cache.peek(a_c, false)); } public void testDefaultChildRemove() throws Exception { cache.put(a, Collections.emptyMap()); cache.put(a_b, Collections.emptyMap()); cache.put(a_c, Collections.emptyMap()); assertNotNull("/a should exist", cache.peek(a, false)); assertNotNull("/a/b should exist", cache.peek(a_b, false)); assertNotNull("/a/c should exist", cache.peek(a_c, false)); // concurrent remove of /a/b and /a/c tm.begin(); cache.removeNode(a_b); Transaction t1 = tm.suspend(); tm.begin(); cache.removeNode(a_c); tm.commit(); tm.resume(t1); tm.commit(); assertNotNull("/a should exist", cache.peek(a, false)); assertNull("/a/b should not exist", cache.peek(a_b, false)); assertNull("/a/c should not exist", cache.peek(a_c, false)); } public void testLockParentChildRemove() throws Exception { cache.getConfiguration().setLockParentForChildInsertRemove(true); cache.put(a, Collections.emptyMap()); cache.put(a_b, Collections.emptyMap()); cache.put(a_c, Collections.emptyMap()); assertNotNull("/a should exist", cache.peek(a, false)); assertNotNull("/a/b should exist", cache.peek(a_b, false)); assertNotNull("/a/c should exist", cache.peek(a_c, false)); // concurrent remove of /a/b and /a/c tm.begin(); cache.removeNode(a_b); Transaction t1 = tm.suspend(); tm.begin(); try { cache.removeNode(a_c); fail("Should not get here."); } catch (TimeoutException e) { // expected } tm.commit(); tm.resume(t1); tm.commit(); assertNotNull("/a should exist", cache.peek(a, false)); assertNull("/a/b should not exist", cache.peek(a_b, false)); assertNotNull("/a/c should exist", cache.peek(a_c, false)); } public void testPerNodeConfigurationDefaultLock() throws Exception { testPerNodeConfiguration(true); } public void testPerNodeConfigurationDefaultNoLock() throws Exception { testPerNodeConfiguration(false); } private void testPerNodeConfiguration(boolean defaultLock) throws Exception { cache.getConfiguration().setLockParentForChildInsertRemove(defaultLock); cache.put(a, Collections.emptyMap()); if (!defaultLock) { // set a per-node lock for a cache.getRoot().getChild(a).setLockForChildInsertRemove(true); } cache.put(a_b, Collections.emptyMap()); cache.put(a_c, Collections.emptyMap()); assertNotNull("/a should exist", cache.peek(a, false)); assertNotNull("/a/b should exist", cache.peek(a_b, false)); assertNotNull("/a/c should exist", cache.peek(a_c, false)); // concurrent remove of /a/b and /a/c tm.begin(); cache.removeNode(a_b); Transaction t1 = tm.suspend(); tm.begin(); try { cache.removeNode(a_c); fail("Should not get here."); } catch (TimeoutException e) { // expected } tm.commit(); tm.resume(t1); tm.commit(); assertNotNull("/a should exist", cache.peek(a, false)); assertNull("/a/b should not exist", cache.peek(a_b, false)); assertNotNull("/a/c should exist", cache.peek(a_c, false)); Fqn b = Fqn.fromString("/b"); Fqn b_b = Fqn.fromString("/b/b"); Fqn b_c = Fqn.fromString("/b/c"); cache.put(b, Collections.emptyMap()); if (defaultLock) { // set a per-node locking config for node b cache.getRoot().getChild(b).setLockForChildInsertRemove(false); } cache.put(b_b, Collections.emptyMap()); cache.put(b_c, Collections.emptyMap()); assertNotNull("/a should exist", cache.peek(b, false)); assertNotNull("/a/b should exist", cache.peek(b_b, false)); assertNotNull("/a/c should exist", cache.peek(b_c, false)); // concurrent remove of /a/b and /a/c tm.begin(); cache.removeNode(b_b); t1 = tm.suspend(); tm.begin(); cache.removeNode(b_c); tm.commit(); tm.resume(t1); tm.commit(); assertNotNull("/b should exist", cache.peek(b, false)); assertNull("/b/b should not exist", cache.peek(b_b, false)); assertNull("/b/c should not exist", cache.peek(b_c, false)); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/lock/AcquireAllTest.java0000644000175000017500000000531111017455042027110 0ustar twernertwernerpackage org.jboss.cache.lock; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.NodeSPI; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.config.Configuration; import org.jboss.cache.transaction.DummyTransactionManagerLookup; import static org.testng.AssertJUnit.assertEquals; import org.testng.annotations.AfterMethod; import org.testng.annotations.Test; /** * @author Bela Ban * @version $Id: AcquireAllTest.java 5906 2008-05-29 07:24:18Z mircea.markus $ */ @Test(groups = {"functional"}) public class AcquireAllTest { CacheSPI cache = null, cache2; final Fqn FQN = Fqn.fromString("/myNode"); final String KEY = "key"; final String VALUE = "value"; @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { TestingUtil.killCaches(cache, cache2); } public void testAcquireAll() throws Exception { NodeSPI root; Object owner = Thread.currentThread(); cache = createCache(Configuration.CacheMode.LOCAL, IsolationLevel.SERIALIZABLE); cache.put("/a/b/c", null); cache.put("/1/2/3", null); root = cache.getRoot(); NodeLock lock = root.getLock(); lock.acquireAll(owner, 2000, LockType.READ); lock.releaseAll(owner); assertEquals(0, cache.getNumberOfLocksHeld()); lock.acquireAll(owner, 2000, LockType.WRITE); lock.releaseAll(owner); assertEquals(0, cache.getNumberOfLocksHeld()); } public void testAcquireAllReplicated() throws Exception { NodeSPI root; Object owner = Thread.currentThread(); cache2 = createCache(Configuration.CacheMode.REPL_ASYNC, IsolationLevel.SERIALIZABLE); cache2.put("/a/b/c", null); cache2.put("/1/2/3", null); cache = createCache(Configuration.CacheMode.REPL_ASYNC, IsolationLevel.SERIALIZABLE); root = cache.getRoot(); NodeLock lock = root.getLock(); lock.acquireAll(owner, 2000, LockType.READ); lock.releaseAll(owner); assertEquals(0, cache.getNumberOfLocksHeld()); lock.acquireAll(owner, 2000, LockType.WRITE); lock.releaseAll(owner); assertEquals(0, cache.getNumberOfLocksHeld()); } private CacheSPI createCache(Configuration.CacheMode mode, IsolationLevel level) { CacheSPI c = (CacheSPI) new DefaultCacheFactory().createCache(false); c.getConfiguration().setCacheMode(mode); c.getConfiguration().setIsolationLevel(level); c.getConfiguration().setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName()); c.create(); c.start(); return c; } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/lock/StripedLockTest.java0000644000175000017500000000376110665031725027326 0ustar twernertwernerpackage org.jboss.cache.lock; import static org.testng.AssertJUnit.assertTrue; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Random; import java.util.concurrent.locks.ReentrantReadWriteLock; import org.jboss.cache.Fqn; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; @Test(groups = { "functional" }) public class StripedLockTest { private StripedLock stripedLock; @BeforeMethod(alwaysRun = true) public void setUp() { stripedLock = new StripedLock(); } public void testHashingDistribution() { // ensure even bucket distribution of lock stripes List fqns = createRandomFqns(1000); Map distribution = new HashMap(); for (Fqn f : fqns) { ReentrantReadWriteLock lock = stripedLock.getLock(f); if (distribution.containsKey(lock)) { int count = distribution.get(lock) + 1; distribution.put(lock, count); } else { distribution.put(lock, 1); } } System.out.println(distribution); // cannot be larger than the number of locks System.out.println("dist size: " + distribution.size()); System.out.println("num shared locks: " + stripedLock.sharedLocks.length); assertTrue(distribution.size() <= stripedLock.sharedLocks.length); // assume at least a 2/3rd spread assertTrue(distribution.size() * 1.5 >= stripedLock.sharedLocks.length); } private List createRandomFqns(int number) { List f = new ArrayList(number); Random r = new Random(); while (f.size() < number) { Fqn fqn = Fqn.fromString("/" + ((char) (65 + r.nextInt(26))) + "/" + ((char) (65 + r.nextInt(26))) + "/" + ((char) (65 + r.nextInt(26)))); f.add(fqn); } System.out.println("Fqns: " + f); return f; } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/lock/LockParentRootFlagTest.java0000644000175000017500000000430011071367645030576 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * Copyright 2008, Red Hat Middleware LLC, and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.cache.lock; import org.jboss.cache.Cache; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.util.TestingUtil; import org.testng.annotations.Test; @Test(groups = "functional") public class LockParentRootFlagTest { // to test https://jira.jboss.org/jira/browse/JBCACHE-1420 public void testPessimisticSet() { doTest(NodeLockingScheme.PESSIMISTIC, true); } public void testPessimisticUnset() { doTest(NodeLockingScheme.PESSIMISTIC, false); } public void testOptimisticSet() { doTest(NodeLockingScheme.OPTIMISTIC, true); } public void testOptimisticUnset() { doTest(NodeLockingScheme.OPTIMISTIC, false); } private void doTest(NodeLockingScheme nls, boolean set) { Cache c = null; try { c = new DefaultCacheFactory().createCache(false); c.getConfiguration().setNodeLockingScheme(nls); c.getConfiguration().setLockParentForChildInsertRemove(set); c.start(); assert c.getRoot().isLockForChildInsertRemove() == set; } finally { TestingUtil.killCaches(c); } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/lock/NonBlockingWriterLockTest.java0000644000175000017500000005514611017455042031312 0ustar twernertwernerpackage org.jboss.cache.lock; import org.jboss.cache.util.TestingUtil; import static org.testng.AssertJUnit.assertTrue; import static org.testng.AssertJUnit.fail; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.Vector; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; /** * NonBlockingWriterLock is a read/write lock (with upgrade) that has * non-blocking write lock acquisition on existing read lock(s). *

Note that the write lock is exclusive among write locks, e.g., * only one write lock can be granted at one time, but the write lock * is independent of the read locks. For example, * a read lock to be acquired will be blocked if there is existing write lock, but * will not be blocked if there are mutiple read locks already granted to other * owners. On the other hand, a write lock can be acquired as long as there * is no existing write lock, regardless how many read locks have been * granted. * * @author Cavin Song April 22, 2004 * @version 1.0 */ @Test(groups = {"functional"}, enabled = false) // TODO: 2.2.0: This is not a very good test. There is a lot of timing related stuff with regards to the order of execution of reader and writer threads that is not taken into account, producing variable results. Needs to be rewritten. public class NonBlockingWriterLockTest { static final NonBlockingWriterLock lock_ = new NonBlockingWriterLock(); static long SLEEP_MSECS = 500; Vector lockResult = new Vector(); int NO_MORE_OP = 0; int INVOKE_READ = 1; int INVOKE_WRITE = 2; int INVOKE_UPGRADE = 3; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { logX("\n"); log("Setting up test case ..."); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { log("Tearing down test case ..."); } private static void log(String str) { System.out.println(Thread.currentThread() + ": " + java.util.Calendar.getInstance().getTime() + " : " + str); } // For debugging purpose private static void logX(String str) { log(str); // try { // logToFile(str); // } catch (IOException ioe) { // } } // // Debugging intrnal function // private static void logToFile(String str) throws IOException // { // Writer out = new FileWriter("./ReadCommittedLog.txt", true/*append*/); // out.write(str); // out.close(); // } /***************************************************************/ /* Utility functions to creat threads for RL, WL and UL */ /***************************************************************/ /** * Creates a new thread and acquires a read lock with a timeout * value specified by the caller. Optionally, the caller can request * a second read or write lock after the first read lock request. * The locking result is stored in a vector with the following * format: *

*
'case number'-'thread name'-[RL|WL|UL]-[0|1] *
*

where: *

*
'case number' is the passed in test case # by the caller. *
'thread name' is the passed in thread name by the caller. *
RL - indicating was doing read lock request. *
WL - indicating was doing write lock request. *
UL - indicating was doing upgrade lock request. *
0 - indicating the locking request failed. *
1 - indicating the locking request succeeded. *
*

* After all threads in each test case terminate, the test case * should make the following call to verify the test result: *

*
asssertTrue(checkLockingResult(expected-result-string); *
*

* 'expected-result-string' is the locking result string * described above. For example, "8-t1-RL-0" means that thread * t1 in test case #8 doing a Read Lock request expects the * operation to fail. If the expected result string can't be * found then the test case is considered FAILED (ie, either * the read lock request was successful or did not complete). *

* Each test case should also call cleanLockingResult() to reset * result vector for the next test cases. * * @param caseNum Arbitrary string for the test case number. * @param name Arbitrary string for the calling thread name. * @param msecs Milliseconds that the thread should sleep after * acquiring the read lock. * @param errMsg Error msg to log in case of error. * @param secondOP Set to NO_MORE_OP if a 2nd lock request is not required. * Set to INVOKE_READ, INVOKE_READ or INVOKE_UPGRADE * respectively if the 2nd lock is a read, write or * upgrade request respectively. */ protected Thread readThread(final String caseNum, final String name, final long msecs, final long sleepSecs, final String errMsg, final int secondOP) { return new Thread(name) { public void run() { Lock rlock = lock_.readLock(); try { if (!rlock.tryLock(msecs, TimeUnit.MILLISECONDS)) { logX(caseNum + "-" + name + " requesting read lock failed!\n"); String str = caseNum + "-" + name + "-RL-0"; postLockingResult(str); return; } // OK, read lock obtained, sleep and release it. logX(caseNum + "-" + name + " requesting read lock succeeded!\n"); String str = caseNum + "-" + name + "-RL-1"; postLockingResult(str); TestingUtil.sleepThread(sleepSecs); if (secondOP == INVOKE_READ) acquireReadLock(caseNum, name, msecs, errMsg); else if (secondOP == INVOKE_WRITE) acquireWriteLock(caseNum, name, msecs, errMsg); else if (secondOP == INVOKE_UPGRADE) acquireUpgradeLock(caseNum, name, msecs, errMsg); rlock.unlock(); logX(caseNum + "-" + name + " releasing read lock.\n"); } catch (Exception ex) { } } }; } /** * Creates a new thread and acquires a write lock with a timeout * value specified by the caller. Similar to {@link #readThread readThread()} * except it's used for write locks. * * @see #readThread readThread() */ protected Thread writeThread(final String caseNum, final String name, final long msecs, final long sleepSecs, final String errMsg, final int secondOP) { return new Thread(name) { public void run() { try { Lock wlock = lock_.writeLock(); if (!wlock.tryLock(msecs, TimeUnit.MILLISECONDS)) { logX(caseNum + "-" + name + " requesting write lock failed!\n"); String str = caseNum + "-" + name + "-WL-0"; postLockingResult(str); return; } // OK, write lock obtained, sleep and release it. logX(caseNum + "-" + name + " requesting write lock succeeded!\n"); String str = caseNum + "-" + name + "-WL-1"; postLockingResult(str); TestingUtil.sleepThread(sleepSecs); if (secondOP == INVOKE_READ) acquireReadLock(caseNum, name, msecs, errMsg); else if (secondOP == INVOKE_WRITE) acquireWriteLock(caseNum, name, msecs, errMsg); else if (secondOP == INVOKE_UPGRADE) acquireUpgradeLock(caseNum, name, msecs, errMsg); wlock.unlock(); logX(caseNum + "-" + name + " releasing write lock.\n"); } catch (Exception ex) { } } }; } /** * Creates a new thread, acquires a read lock, sleeps for a while * and then tries to upgrade the read lock to a write one. Similar * to {@link #readThread readThread()} except it's used for upgrading * locks. * * @see #readThread readThread() */ protected Thread upgradeThread(final String caseNum, final String name, final long msecs, final String errMsg) { return new Thread(name) { public void run() { try { Lock rlock = lock_.readLock(); Lock wlock = null; if (!rlock.tryLock(msecs, TimeUnit.MILLISECONDS)) { logX(caseNum + "-" + name + " requesting read lock failed!\n"); String str = caseNum + "-" + name + "-RL-0"; postLockingResult(str); return; } // OK, read lock obtained, sleep and upgrade it later. logX(caseNum + "-" + name + " requesting read lock succeeded (upgrade later)!\n"); TestingUtil.sleepThread(SLEEP_MSECS / 2); String str = caseNum + "-" + name + "-UL-"; if ((wlock = lock_.upgradeLockAttempt(msecs)) == null) { logX(caseNum + "-" + name + " requesting upgrade lock failed!\n"); str += "0"; } else { logX(caseNum + "-" + name + " requesting upgrade lock succeeded!\n"); str += "1"; } postLockingResult(str); // Sleep again and then release the lock. TestingUtil.sleepThread(SLEEP_MSECS); if (wlock != null) { wlock.unlock(); logX(caseNum + "-" + name + " releasing upgrade lock.\n"); } rlock.unlock(); } catch (Exception ex) { } } }; } /***************************************************************/ /* Utility functions to acquire RL and WL (no thread) */ /***************************************************************/ /** * This routine tries to acquire a read lock with a timeout value * passed in by the caller. Like {@link #readThread readThread()} * it then stores the locking result in the result vector depending * on the outcome of the request. */ protected void acquireReadLock(final String caseNum, final String name, final long msecs, final String errMsg) { try { Lock rlock = lock_.readLock(); if (!rlock.tryLock(msecs, TimeUnit.MILLISECONDS)) { logX(caseNum + "-" + name + " requesting read lock failed!\n"); String str = caseNum + "-" + name + "-RL-0"; postLockingResult(str); return; } // OK, read lock obtained, sleep and release it. logX(caseNum + "-" + name + " requesting read lock succeeded!\n"); String str = caseNum + "-" + name + "-RL-1"; postLockingResult(str); TestingUtil.sleepThread(SLEEP_MSECS); rlock.unlock(); logX(caseNum + "-" + name + " releasing read lock.\n"); } catch (Exception ex) { } } /** * Same as {@link #acquireReadLock acquireReadLock()} except * it's for write lock request. */ protected void acquireWriteLock(final String caseNum, final String name, final long msecs, final String errMsg) { try { Lock wlock = lock_.writeLock(); if (!wlock.tryLock(msecs, TimeUnit.MILLISECONDS)) { logX(caseNum + "-" + name + " requesting write lock failed!\n"); String str = caseNum + "-" + name + "-WL-0"; postLockingResult(str); return; } // OK, write lock obtained, sleep and release it. logX(caseNum + "-" + name + " requesting write lock succeeded!\n"); String str = caseNum + "-" + name + "-WL-1"; postLockingResult(str); TestingUtil.sleepThread(SLEEP_MSECS); wlock.unlock(); logX(caseNum + "-" + name + " releasing write lock.\n"); } catch (Exception ex) { } } /** * Same as {@link #acquireReadLock acquireReadLock()} except * it's for upgrade lock request. */ protected void acquireUpgradeLock(final String caseNum, final String name, final long msecs, final String errMsg) { try { Lock ulock = null; if ((ulock = lock_.upgradeLockAttempt(msecs)) == null) { logX(caseNum + "-" + name + " requesting upgrade lock failed!\n"); String str = caseNum + "-" + name + "-UL-0"; postLockingResult(str); return; } // OK, write lock obtained, sleep and release it. logX(caseNum + "-" + name + " requesting upgrade lock succeeded!\n"); String str = caseNum + "-" + name + "-UL-1"; postLockingResult(str); TestingUtil.sleepThread(SLEEP_MSECS); ulock.unlock(); logX(caseNum + "-" + name + " releasing upgrade lock.\n"); } catch (Exception ex) { } } /***************************************************************/ /* Synchronized methods handling locking result vector */ /***************************************************************/ /** * Clean/remove all locking results in the vector. */ protected synchronized void cleanLockingResult() { lockResult.removeAllElements(); } /** * Post a locking result to the vector for later verification. */ protected synchronized void postLockingResult(Object obj) { logX(" Added *" + obj + "* to the result vector\n"); // Make sure we only have one in the vector //if (!checkLockingResult((String)obj)) lockResult.addElement(obj); } /** * Check if a given expected locking result is in the vector. */ protected synchronized boolean checkLockingResult(String expected) { boolean rc = false; for (int i = 0; i < lockResult.size(); i++) { Object ele = lockResult.elementAt(i); String str = (String) ele; if (expected.equals(str)) { rc = true; break; } } if (rc) logX(" Searching for *" + expected + "* SUCCEEDED.\n"); else logX(" Searching for *" + expected + "* FAILED.\n"); return rc; } /***************************************************************/ /* T e s t C a s e s */ /***************************************************************/ /** * Case #10 - T1 acquires RL, T2 acquires RL followed by WL. */ public void testWriteWithMultipleReaders() throws Exception { String caseNum = "10"; Thread t1 = readThread(caseNum, "t1", 0, SLEEP_MSECS * 2, "1st read lock attempt failed", NO_MORE_OP); Thread t2 = readThread(caseNum, "t2", 0, SLEEP_MSECS, "2nd read lock attempt failed", INVOKE_WRITE); t1.start(); t2.start(); t1.join(3000); t2.join(3000); assertTrue(checkLockingResult(caseNum + "-t1-RL-1") && checkLockingResult(caseNum + "-t2-RL-1") && checkLockingResult(caseNum + "-t2-WL-1")); cleanLockingResult(); // possilbe deadlock check if (t1.isAlive() || t2.isAlive()) fail("Possible deadlock resulted in testRead."); } /** * Case #11 - T1 acquires RL followed by WL, T2 acquires RL. */ public void testUpgradeWithMultipleReadersOn1() throws Exception { String caseNum = "11"; Thread t1 = readThread(caseNum, "t1", 0, SLEEP_MSECS, "1st read lock attempt failed", INVOKE_WRITE); Thread t2 = readThread(caseNum, "t2", 0, SLEEP_MSECS * 2, "2nd read lock attempt failed", NO_MORE_OP); t1.start(); t2.start(); t1.join(3000); t2.join(3000); assertTrue(checkLockingResult(caseNum + "-t1-RL-1") && checkLockingResult(caseNum + "-t2-RL-1") && checkLockingResult(caseNum + "-t1-WL-1")); cleanLockingResult(); // possilbe deadlock check if (t1.isAlive() || t2.isAlive()) fail("Possible deadlock resulted in testRead."); } /** * Case #2 - T1 acquires RL followed by UL. */ public void testUpgradeReadLock() throws Exception { String caseNum = "2"; Thread t1 = readThread(caseNum, "t1", 0, SLEEP_MSECS, "1st read lock attempt failed", INVOKE_UPGRADE); t1.start(); t1.join(3000); assertTrue(checkLockingResult(caseNum + "-t1-RL-1") && checkLockingResult(caseNum + "-t1-UL-1")); cleanLockingResult(); } /** * Case #3 - T1 acquires RL followed by WL. */ public void testReadThenWrite() throws Exception { String caseNum = "3"; acquireReadLock(caseNum, "t1", 0, "1st read lock attempt failed"); acquireWriteLock(caseNum, "t1.1", 0, "2nd write lock attempt failed"); assertTrue(checkLockingResult(caseNum + "-t1-RL-1") && checkLockingResult(caseNum + "-t1.1-WL-1")); cleanLockingResult(); } /** * Case #5 - T1 acquires WL followed by RL. */ public void testWriteThenRead() throws Exception { String caseNum = "5"; acquireWriteLock(caseNum, "t1", 0, "1st write lock attempt failed"); acquireReadLock(caseNum, "t1.1", 0, "2nd read lock attempt failed"); assertTrue(checkLockingResult(caseNum + "-t1-WL-1") && checkLockingResult(caseNum + "-t1.1-RL-1")); cleanLockingResult(); } /** * Case #6 - T1 acquires RL, T2 acquires RL. */ public void testMultipleReadlock() throws Exception { String caseNum = "6"; Thread t1 = readThread(caseNum, "t1", 0, SLEEP_MSECS, "1st read lock attempt failed", NO_MORE_OP); Thread t2 = readThread(caseNum, "t2", 0, SLEEP_MSECS, "2nd read lock attempt failed", NO_MORE_OP); t1.start(); t2.start(); t1.join(3000); t2.join(3000); assertTrue(checkLockingResult(caseNum + "-t1-RL-1") && checkLockingResult(caseNum + "-t2-RL-1")); cleanLockingResult(); // possilbe deadlock check if (t1.isAlive() || t2.isAlive()) fail("Possible deadlock resulted in testRead."); } /** * Case #8 - T1 acquires RL, T2 acquires WL. */ public void testWriteWithExistingReader() throws Exception { String caseNum = "8"; Thread t1 = readThread(caseNum, "t1", 0, SLEEP_MSECS, "1st write lock attempt failed", NO_MORE_OP); Thread t2 = writeThread(caseNum, "t2", 0, SLEEP_MSECS, "2nd read lock attempt failed", NO_MORE_OP); t1.start(); t2.start(); t1.join(3000); t2.join(3000); assertTrue(checkLockingResult(caseNum + "-t1-RL-1") && checkLockingResult(caseNum + "-t2-WL-1")); cleanLockingResult(); // possilbe deadlock check if (t1.isAlive() || t2.isAlive()) fail("Possible deadlock resulted in testRead."); } /** * Case #13 - T1 acquires RL, T2 acquires WL. */ public void testReadWithExistingWriter() throws Exception { String caseNum = "13"; Thread t1 = writeThread(caseNum, "t1", 0, SLEEP_MSECS, "1st write lock attempt failed", NO_MORE_OP); Thread t2 = readThread(caseNum, "t2", 0, SLEEP_MSECS, "2nd read lock attempt failed", NO_MORE_OP); t1.start(); t2.start(); t1.join(3000); t2.join(3000); assert checkLockingResult(caseNum + "-t1-WL-1"); assert checkLockingResult(caseNum + "-t2-RL-0"); // assertTrue(checkLockingResult(caseNum + "-t1-WL-1") && // checkLockingResult(caseNum + "-t2-RL-0")); cleanLockingResult(); // possilbe deadlock check if (t1.isAlive() || t2.isAlive()) fail("Possible deadlock resulted in testRead."); } /** * Case #14 - T1 acquires WL, T2 acquires WL. */ public void testMultipleWritelocks() throws Exception { String caseNum = "14"; Thread t1 = writeThread(caseNum, "t1", 0, SLEEP_MSECS, "1st write lock attempt failed", NO_MORE_OP); Thread t2 = writeThread(caseNum, "t2", 0, SLEEP_MSECS, "2nd write lock attempt failed", NO_MORE_OP); t1.start(); t2.start(); t1.join(3000); t2.join(3000); // assertTrue(checkLockingResult(caseNum + "-t1-WL-1") && // checkLockingResult(caseNum + "-t2-WL-0")); assert checkLockingResult(caseNum + "-t1-WL-1"); assert checkLockingResult(caseNum + "-t2-WL-0"); cleanLockingResult(); // possilbe deadlock check if (t1.isAlive() || t2.isAlive()) fail("Possible deadlock resulted in testRead."); } /** * Case #7 - T1 acquires RL, T2 acquires UL. */ public void testUpgradeWithExistingReader() throws Exception { String caseNum = "7"; Thread t1 = readThread(caseNum, "t1", 0, SLEEP_MSECS, "1st read lock attempt failed", NO_MORE_OP); Thread t2 = upgradeThread(caseNum, "t2", 0, "2nd upgrade lock attempt failed"); t1.start(); t2.start(); t1.join(3000); t2.join(3000); assertTrue(checkLockingResult(caseNum + "-t1-RL-1") && checkLockingResult(caseNum + "-t2-UL-1")); cleanLockingResult(); // possilbe deadlock check if (t1.isAlive() || t2.isAlive()) fail("Possible deadlock resulted in testRead."); } /** * Case #9 - T1 acquires RL, T2 acquires RL followed by UL. */ public void testUpgradeWithMultipleReaders() throws Exception { String caseNum = "9"; Thread t1 = readThread(caseNum, "t1", 0, SLEEP_MSECS * 2, "1st read lock attempt failed", NO_MORE_OP); Thread t2 = readThread(caseNum, "t2", 0, SLEEP_MSECS, "2nd read lock attempt failed", INVOKE_UPGRADE); t1.start(); t2.start(); t1.join(3000); t2.join(3000); assertTrue(checkLockingResult(caseNum + "-t1-RL-1") && checkLockingResult(caseNum + "-t2-RL-1") && checkLockingResult(caseNum + "-t2-UL-1")); cleanLockingResult(); // possilbe deadlock check if (t1.isAlive() || t2.isAlive()) fail("Possible deadlock resulted in testRead."); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/multiplexer/0000755000175000017500000000000011376173744025023 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/multiplexer/BadMuxConfigTest.java0000644000175000017500000000603211005362317031016 0ustar twernertwernerpackage org.jboss.cache.multiplexer; import org.jboss.cache.Cache; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.config.Configuration; import org.jgroups.JChannel; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * Tests that JBC starts correctly even if the multiplexer * configuration is incorrect. * * @author Brian Stansberry * @version $Revision: 5723 $ */ @Test(groups = {"functional", "jgroups"}, enabled = true) public class BadMuxConfigTest { private MultiplexerTestHelper muxHelper; private Cache cache; private boolean cacheStarted; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { muxHelper = new MultiplexerTestHelper(); Configuration config = new Configuration(); config.setCacheMode(Configuration.CacheMode.REPL_SYNC); config.setClusterConfig(JChannel.DEFAULT_PROTOCOL_STACK); cache = new DefaultCacheFactory().createCache(config, false); cacheStarted = false; } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { try { if (cacheStarted && cache != null) { cache.stop(); cache.destroy(); } } finally { if (muxHelper != null) { muxHelper.tearDown(); muxHelper = null; } } } public void testValidMuxConfig() throws Exception { // TODO: 2.2.0: this test hangs on forever. Uncomment it and make it work... // muxHelper.configureCacheForMux(cache); // // checkStart(false, true); } public void testMissingMuxChannelFactory() throws Exception { muxHelper.configureCacheForMux(cache); cache.getConfiguration().getRuntimeConfig().setMuxChannelFactory(null); checkStart(false, false); } public void testInvalidStackName() throws Exception { muxHelper.configureCacheForMux(cache); cache.getConfiguration().setMultiplexerStack("bogus"); checkStart(true, false); } public void testMissingStackName() throws Exception { muxHelper.configureCacheForMux(cache); cache.getConfiguration().setMultiplexerStack(null); checkStart(true, false); } private void checkStart(boolean expectFail, boolean expectMux) { try { cache.start(); cacheStarted = true; if (expectFail) { fail("Start did not fail as expected"); } if (expectMux) { assertTrue("Cache is using mux", cache.getConfiguration().isUsingMultiplexer()); } else { assertFalse("Cache is not using mux ", cache.getConfiguration().isUsingMultiplexer()); } } catch (Exception e) { if (!expectFail) { fail("Caught exception starting cache " + e.getLocalizedMessage()); } } } } ././@LongLink0000000000000000000000000000015100000000000011562 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/multiplexer/BuddyAssignmentStateTransferTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/multiplexer/BuddyAssignmentStateTransferTest.0000644000175000017500000000445410702415431033461 0ustar twernertwerner/* * JBoss, Home of Professional Open Source. * Copyright 2006, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.cache.multiplexer; import org.jboss.cache.Cache; import static org.testng.AssertJUnit.assertTrue; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * Repeats the superclass tests, but with the multiplexer enabled. * * @author Brian Stansberry * @version $Revision: 4556 $ */ @Test(enabled = true) public class BuddyAssignmentStateTransferTest extends org.jboss.cache.buddyreplication.BuddyAssignmentStateTransferTest { private MultiplexerTestHelper muxHelper; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { muxHelper = new MultiplexerTestHelper(); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { try { super.tearDown(); } finally { if (muxHelper != null) muxHelper.tearDown(); muxHelper = null; } } @Override protected void configureMultiplexer(Cache cache) throws Exception { muxHelper.configureCacheForMux(cache); } @Override protected void validateMultiplexer(Cache cache) { assertTrue("Cache is using multiplexer", cache.getConfiguration().isUsingMultiplexer()); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/multiplexer/BuddyGroupAssignmentTest.java0000644000175000017500000000443210731261227032632 0ustar twernertwerner/* * JBoss, Home of Professional Open Source. * Copyright 2006, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.cache.multiplexer; import org.jboss.cache.Cache; import static org.testng.AssertJUnit.assertTrue; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * Repeats the superclass tests, but with the multiplexer enabled. * * @author Brian Stansberry * @version $Revision: 4867 $ */ @Test(enabled = true) public class BuddyGroupAssignmentTest extends org.jboss.cache.buddyreplication.BuddyGroupAssignmentTest { private MultiplexerTestHelper muxHelper; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { muxHelper = new MultiplexerTestHelper(); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { try { super.tearDown(); } finally { if (muxHelper != null) muxHelper.tearDown(); muxHelper = null; } } @Override protected void configureMultiplexer(Cache cache) throws Exception { muxHelper.configureCacheForMux(cache); } @Override protected void validateMultiplexer(Cache cache) { assertTrue("Cache is using multiplexer", cache.getConfiguration().isUsingMultiplexer()); } } ././@LongLink0000000000000000000000000000014700000000000011567 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/multiplexer/ChannelInjectionPreferenceTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/multiplexer/ChannelInjectionPreferenceTest.ja0000644000175000017500000000540410732232543033400 0ustar twernertwernerpackage org.jboss.cache.multiplexer; import org.jboss.cache.Cache; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.RuntimeConfig; import org.jgroups.Channel; import org.jgroups.JChannel; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * Tests that JBC prefers an injected Channel to creating one via * a configured JChannelFactory and stack name. * * @author Brian Stansberry * @version $Revision: 4880 $ */ @Test(groups = {"functional", "jgroups"}, enabled = true) public class ChannelInjectionPreferenceTest { private MultiplexerTestHelper muxHelper; private Cache cache; private boolean cacheStarted; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { muxHelper = new MultiplexerTestHelper(); Configuration config = new Configuration(); config.setCacheMode(Configuration.CacheMode.REPL_SYNC); config.setClusterConfig(JChannel.DEFAULT_PROTOCOL_STACK); cache = new DefaultCacheFactory().createCache(config, false); cacheStarted = false; } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { try { if (cacheStarted && cache != null) { cache.stop(); cache.destroy(); } } finally { if (muxHelper != null) { muxHelper.tearDown(); muxHelper = null; } } } public void testChannelInjectionPreference() throws Exception { muxHelper.configureCacheForMux(cache); Channel channel = new JChannel(JChannel.DEFAULT_PROTOCOL_STACK); RuntimeConfig rtcfg = cache.getConfiguration().getRuntimeConfig(); rtcfg.setChannel(channel); // Start shouldn't fail and we shouldn't be using mulitplexer checkStart(false, false); assertEquals("Injected channel used", channel, rtcfg.getChannel()); } private void checkStart(boolean expectFail, boolean expectMux) { try { cache.start(); cacheStarted = true; if (expectFail) { fail("Start did not fail as expected"); } if (expectMux) { assertTrue("Cache is using mux", cache.getConfiguration().isUsingMultiplexer()); } else { assertFalse("Cache is not using mux ", cache.getConfiguration().isUsingMultiplexer()); } } catch (Exception e) { if (!expectFail) { fail("Caught exception starting cache " + e.getLocalizedMessage()); } } } } ././@LongLink0000000000000000000000000000014500000000000011565 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/multiplexer/BuddyReplicationFailoverTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/multiplexer/BuddyReplicationFailoverTest.java0000644000175000017500000000451010702415431033437 0ustar twernertwerner/* * JBoss, Home of Professional Open Source. * Copyright 2006, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.cache.multiplexer; import org.jboss.cache.Cache; import static org.testng.AssertJUnit.assertTrue; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * Repeats the superclass tests, but with the multiplexer enabled. * * @author Brian Stansberry * @version $Revision: 4556 $ */ @Test(groups={"functional", "jgroups"}, enabled = true) public class BuddyReplicationFailoverTest extends org.jboss.cache.buddyreplication.BuddyReplicationFailoverTest { private MultiplexerTestHelper muxHelper; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { muxHelper = new MultiplexerTestHelper(); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { try { super.tearDown(); } finally { if (muxHelper != null) muxHelper.tearDown(); muxHelper = null; } } @Override protected void configureMultiplexer(Cache cache) throws Exception { muxHelper.configureCacheForMux(cache); } @Override protected void validateMultiplexer(Cache cache) { assertTrue("Cache is using multiplexer", cache.getConfiguration().isUsingMultiplexer()); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/multiplexer/SyncReplTxTest.java0000644000175000017500000000451410701404500030557 0ustar twernertwerner/* * JBoss, Home of Professional Open Source. * Copyright 2006, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.cache.multiplexer; import org.jboss.cache.Cache; import org.testng.AssertJUnit; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * Repeats the superclass tests, but with the multiplexer enabled. * * @author Brian Stansberry * @version $Revision: 4552 $ */ @Test(groups = {"functional", "jgroups"}, enabled = true) public class SyncReplTxTest extends org.jboss.cache.replicated.SyncReplTxTest { private MultiplexerTestHelper muxHelper; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { muxHelper = new MultiplexerTestHelper(); super.setUp(); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { try { super.tearDown(); } finally { if (muxHelper != null) { muxHelper.tearDown(); muxHelper = null; } } } @Override protected void configureMultiplexer(Cache cache) throws Exception { muxHelper.configureCacheForMux(cache); } @Override protected void validateMultiplexer(Cache cache) { AssertJUnit.assertTrue("Cache is using multiplexer", cache.getConfiguration().isUsingMultiplexer()); } } ././@LongLink0000000000000000000000000000015600000000000011567 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/multiplexer/BuddyBackupActivationInactivationTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/multiplexer/BuddyBackupActivationInactivation0000644000175000017500000000452010702415431033517 0ustar twernertwerner/* * JBoss, Home of Professional Open Source. * Copyright 2006, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.cache.multiplexer; import org.jboss.cache.Cache; import static org.testng.AssertJUnit.assertTrue; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * Repeats the superclass tests, but with the multiplexer enabled. * * @author Brian Stansberry * @version $Revision: 4556 $ */ @Test(enabled = true) public class BuddyBackupActivationInactivationTest extends org.jboss.cache.buddyreplication.BuddyBackupActivationInactivationTest { private MultiplexerTestHelper muxHelper; @BeforeMethod(alwaysRun = true) protected void setUp() throws Exception { muxHelper = new MultiplexerTestHelper(); super.setUp(); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { try { super.tearDown(); } finally { if (muxHelper != null) muxHelper.tearDown(); muxHelper = null; } } @Override protected void configureMultiplexer(Cache cache) throws Exception { muxHelper.configureCacheForMux(cache); } @Override protected void validateMultiplexer(Cache cache) { assertTrue("Cache is using multiplexer", cache.getConfiguration().isUsingMultiplexer()); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/multiplexer/AsyncReplTest.java0000644000175000017500000000471410702415431030414 0ustar twernertwerner/* * JBoss, Home of Professional Open Source. * Copyright 2006, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.cache.multiplexer; import org.jboss.cache.Cache; import org.testng.AssertJUnit; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * Repeats the superclass tests, but with the multiplexer enabled. * * @author Brian Stansberry * @version $Revision: 4556 $ */ @Test(groups = {"functional", "jgroups"}, enabled = true) public class AsyncReplTest extends org.jboss.cache.replicated.AsyncReplTest { private MultiplexerTestHelper muxHelper; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { muxHelper = new MultiplexerTestHelper(); super.setUp(); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { try { super.tearDown(); } finally { if (muxHelper != null) { muxHelper.tearDown(); muxHelper = null; } } } @Test(enabled = true) public void testPutShouldNotReplicateToDifferentCluster() { super.testPutShouldNotReplicateToDifferentCluster(); } protected void configureMultiplexer(Cache cache) throws Exception { muxHelper.configureCacheForMux(cache); } protected void validateMultiplexer(Cache cache) { AssertJUnit.assertTrue("Cache is using multiplexer", cache.getConfiguration().isUsingMultiplexer()); } }jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/multiplexer/StateTransferTest.java0000644000175000017500000000456510701404500031277 0ustar twernertwerner/* * JBoss, Home of Professional Open Source. * Copyright 2006, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.cache.multiplexer; import org.jboss.cache.Cache; import org.jboss.cache.statetransfer.StateTransfer200Test; import org.testng.AssertJUnit; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * Repeats the superclass tests, but with the multiplexer enabled. * * @author Brian Stansberry * @version $Revision: 4552 $ */ @Test(groups = {"functional", "jgroups"}, enabled = true) public class StateTransferTest extends StateTransfer200Test { private MultiplexerTestHelper muxHelper; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { muxHelper = new MultiplexerTestHelper(); super.setUp(); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { try { super.tearDown(); } finally { if (muxHelper != null) { muxHelper.tearDown(); muxHelper = null; } } } @Override protected void configureMultiplexer(Cache cache) throws Exception { muxHelper.configureCacheForMux(cache); } @Override protected void validateMultiplexer(Cache cache) { AssertJUnit.assertTrue("Cache is using multiplexer", cache.getConfiguration().isUsingMultiplexer()); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/multiplexer/MultiplexerTestHelper.java0000644000175000017500000001470510727545014032177 0ustar twernertwerner/* * JBoss, Home of Professional Open Source. * Copyright 2006, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.cache.multiplexer; import org.jboss.cache.Cache; import org.jgroups.ChannelFactory; import org.jgroups.JChannel; import org.jgroups.JChannelFactory; import org.w3c.dom.Document; import org.w3c.dom.Element; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import java.util.Collections; import java.util.HashSet; import java.util.Set; import java.util.StringTokenizer; /** * Utility class that can associate a cache with a multiplexer-enabled * JGroups ChannelFactory. * * @author Brian Stansberry * @version $Revision: 4836 $ */ public class MultiplexerTestHelper { public static final String MUX_STACK = "jbc-test"; private final Set factories = Collections.synchronizedSet(new HashSet()); private final Set caches = Collections.synchronizedSet(new HashSet()); /** * Configures the given cache to get its JChannel from a * multiplexer-enabled JChannelFactory. The JChannelFactory will * produce MuxChannels configured with the same protocol stack as * whatever the provided cache is configured with. * * @param cache the cache * @throws Exception */ public void configureCacheForMux(Cache cache) throws Exception { synchronized (caches) { ChannelFactory factory = createMuxChannelFactory(cache); cache.getConfiguration().getRuntimeConfig().setMuxChannelFactory(factory); cache.getConfiguration().setMultiplexerStack(MUX_STACK); } } /** * Creates a JChannelFactory. The JChannelFactory will * produce MuxChannels configured with the same protocol stack as * whatever the provided cache is configured with. * * @param cache the cache from which the protocol stack config should * be obtained * @return the channel factory. * @throws Exception */ public ChannelFactory createMuxChannelFactory(Cache cache) throws Exception { return createMuxChannelFactory(getChannelProperties(cache)); } private String getChannelProperties(Cache cache) { String props = cache.getConfiguration().getClusterConfig(); return (props == null ? JChannel.DEFAULT_PROTOCOL_STACK : props); } /** * Creates a JChannelFactory. The JChannelFactory will * produce MuxChannels configured according to the given * protocol stack(s). * * @param muxConfig Element that looks like the root element * of a multiplexer stacks.xml file. * @return the channel factory. * @throws Exception */ public ChannelFactory createMuxChannelFactory(String muxConfig) throws Exception { synchronized (factories) { JChannelFactory factory = new JChannelFactory(); factory.setDomain("jbc.mux.test"); factory.setExposeChannels(false); factory.setMultiplexerConfig(getClusterConfigElement(muxConfig)); factories.add(factory); return factory; } } /** * Converts an old-style JGroups protocol stack config string to an Element * that looks like the root element of a multiplexer stacks.xml file. * * @param clusterConfig * @return * @throws Exception */ public static Element getClusterConfigElement(String clusterConfig) throws Exception { clusterConfig = clusterConfig.trim(); DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder(); Document doc = db.newDocument(); Element top = doc.createElement("protocol_stacks"); doc.appendChild(top); Element stack = doc.createElement("stack"); stack.setAttribute("name", MUX_STACK); top.appendChild(stack); Element config = doc.createElement("config"); StringTokenizer outer = new StringTokenizer(clusterConfig, ":"); while (outer.hasMoreTokens()) { String protocol = outer.nextToken(); String protName = protocol; String attribs = null; int nameEnd = protocol.indexOf('('); if (nameEnd > 0) { protName = protocol.substring(0, nameEnd); attribs = protocol.substring(nameEnd + 1, protocol.length() - 1); } Element element = doc.createElement(protName); if (attribs != null && attribs.length() > 0) { StringTokenizer inner = new StringTokenizer(attribs, ";"); while (inner.hasMoreTokens()) { String attrib = inner.nextToken(); int eq = attrib.indexOf('='); String name = attrib.substring(0, eq); String value = attrib.substring(eq + 1); element.setAttribute(name, value); } } config.appendChild(element); } stack.appendChild(config); return top; } /** * Performs cleanup work. Once this method is invoked, this * object should no longer be used. */ public void tearDown() { factories.clear(); caches.clear(); } /** * Tests creation of a channel factory. * * @param args */ public static void main(String[] args) { MultiplexerTestHelper helper = new MultiplexerTestHelper(); try { helper.createMuxChannelFactory(JChannel.DEFAULT_PROTOCOL_STACK); } catch (Exception e) { e.printStackTrace(); } finally { helper.tearDown(); } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/DataContainerTest.java0000644000175000017500000002713611024246144026662 0ustar twernertwernerpackage org.jboss.cache; import org.jboss.cache.config.Configuration; import org.jboss.cache.marshall.NodeData; import org.jboss.cache.mock.MockNodesFixture; import org.jboss.cache.optimistic.DataVersion; import org.jboss.cache.optimistic.DefaultDataVersion; import org.jboss.cache.buddyreplication.BuddyFqnTransformer; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.ArrayList; import java.util.List; /** * Tests functionality from DataContainer. * * @author Mircea.Markus@jboss.com * @since 2.2 */ @Test(groups = "unit") public class DataContainerTest { private DataContainerImpl container; private MockNodesFixture nodes; //end of node structure. @BeforeMethod public void setUp() { nodes = new MockNodesFixture(); container = new DataContainerImpl(); container.setRoot(nodes.root); container.setBuddyFqnTransformer(new BuddyFqnTransformer()); } /** * tests {@link DataContainerImpl#peek(Fqn, boolean, boolean)} method */ public void testPeekNodesSimple() { assert nodes.root == container.peek(Fqn.ROOT, true, true); assert nodes.adfgNode == container.peek(nodes.adfg, false, false); assert nodes.adfgNode == container.peek(nodes.adfg, false, true); assert nodes.adfgNode == container.peek(nodes.adfg, true, true); } /** * tests {@link DataContainerImpl#peek(Fqn, boolean, boolean)} for invalid nodes. */ public void testPeekInvalidNodes() { nodes.adfgNode.setValid(false, false); assert null == container.peek(nodes.adfg, true, false); assert nodes.adfgNode == container.peek(nodes.adfg, true, true); } /** * tests {@link DataContainerImpl#peek(Fqn, boolean, boolean)} method for deleted nodes. */ public void testPeekDeletedNodes() { nodes.adfgNode.markAsDeleted(true); assert null == container.peek(nodes.adfg, false, false); assert nodes.adfgNode == container.peek(nodes.adfg, true, false); } /** * tests {@link DataContainerImpl#peekVersioned(Fqn, org.jboss.cache.optimistic.DataVersion, boolean)} method. */ public void testPeekVersioned() { assert nodes.adfgNode == container.peekVersioned(nodes.adfg, null, true) : "if data version is null this returns same value as peek(boolean, boolean)"; //test pessimistic loking Configuration config = new Configuration(); config.setNodeLockingOptimistic(false); DataVersion dataVersion = new DefaultDataVersion(2); container.setDependencies(config, null, null); assert nodes.adfgNode == container.peekVersioned(nodes.adfg, dataVersion, true) : "if NOT opt locking same value as peek(boolean, boolean) expected"; //test optimistic locking with same version config.setNodeLockingOptimistic(true); DataVersion adfgDataVersion = new DefaultDataVersion(2); nodes.adfgNode.setVersion(adfgDataVersion); assert nodes.adfgNode == container.peekVersioned(nodes.adfg, adfgDataVersion, true) : "same version, expcting node to be returned"; //test optimistic locking with a an older try { container.peekVersioned(nodes.adfg, new DefaultDataVersion(1), true); assert false : "exception expected as version changed."; } catch (CacheException e) { //expected } } /** * tests {@link DataContainerImpl#peekStrict(org.jboss.cache.transaction.GlobalTransaction, Fqn, boolean)}. */ public void testPeekStrict() { assert nodes.adfgNode == container.peekStrict(null, nodes.adfg, true) : "if data version is null this returns same value as peek(boolean, boolean)"; try { container.peekStrict(null, nodes.notExistent, true); assert false : "excpetion expected as node does not exist"; } catch (Exception e) { //expected } } /** * tests {@link DataContainerImpl#exists(Fqn)} */ public void testsExists() { assert container.exists(nodes.ab) : "ab exists"; nodes.abNode.markAsDeleted(true); assert !container.exists(nodes.ab) : "ab marked as deleted"; assert container.exists(nodes.ad); nodes.adNode.setValid(false, false); assert !container.exists(nodes.ade) : "its parent was marked as invalid"; } /** * tests {@link DataContainerImpl#hasChildren(Fqn)} */ public void testHasChildren() { assert container.hasChildren(nodes.ad) : " ade is a child of ad"; assert !container.hasChildren(nodes.notExistent) : " this one does not exist"; assert !container.hasChildren(nodes.adfg) : "this one exists but does not have children"; nodes.adNode.setValid(false, false); assert !container.hasChildren(nodes.ad) : "ad exists and has children but is invalid"; } /** * test {@link DataContainerImpl#buildNodeData(java.util.List, NodeSPI)} */ public void testBuildNodeData() { nodes.abNode.put("ab", "ab"); nodes.abcNode.put("abc", "abc"); List result = new ArrayList(); container.buildNodeData(result, nodes.abNode); assert result.size() == 2; assert result.contains(new NodeData(nodes.ab, nodes.abNode.getData())); assert result.contains(new NodeData(nodes.abc, nodes.abcNode.getData())); } /** * tests {@link DataContainerImpl#getNodesForEviction(Fqn, boolean)} in a nonrecursive scenario. */ public void testGetNodesForEvictionNonrecursive() { //check for root first List result = container.getNodesForEviction(Fqn.ROOT, false); assert result.size() == 1 : "for root the direct children are considered for eviction"; assert result.contains(nodes.a); //check normal result = container.getNodesForEviction(nodes.ad, false); assert result.size() == 1 : "one child expected"; assert result.contains(nodes.ad); //check resident scenario nodes.adNode.setResident(true); result = container.getNodesForEviction(nodes.ad, false); assert result.size() == 0 : "no children expected"; } /** * tests {@link DataContainerImpl#getNodesForEviction(Fqn, boolean)} in a recursive scenario. */ public void testGetNodesForEvictionRecursive() { //check for root first List result = container.getNodesForEviction(Fqn.ROOT, true); assert result.size() == 8 : "all children are considered for eviction"; //check normal result = container.getNodesForEviction(nodes.ad, true); assert result.size() == 5 : "five childrens expected"; assert result.contains(nodes.ad); assert result.contains(nodes.ade); assert result.contains(nodes.adf); assert result.contains(nodes.adfh); assert result.contains(nodes.adfg); //check resident scenario nodes.adNode.setResident(true); result = container.getNodesForEviction(nodes.ad, true); assert result.size() == 4 : "only children expected"; assert result.contains(nodes.ade); assert result.contains(nodes.adf); assert result.contains(nodes.adfh); assert result.contains(nodes.adfg); } /** * tests {@link DataContainerImpl#getNodesForEviction(Fqn, boolean)} in a recursive scenario. */ public void testGetNodesForEvictionRecursiveNullNodes() { container.removeFromDataStructure(nodes.ad, true); //check for root first List result = container.getNodesForEviction(Fqn.ROOT, true); assert result.size() == 3 : "all children are considered for eviction"; //check normal // this node does not exist!! Should NOT throw a NPE. result = container.getNodesForEviction(nodes.ad, true); assert result.isEmpty() : "Should be empty"; } /** * tests {@link DataContainerImpl#getNumberOfNodes()} */ public void testGetNumberOfNodes() { assert container.getNumberOfNodes() == 8 : "eoght nodes expected"; } /** * tests {@link DataContainerImpl#removeFromDataStructure(Fqn, boolean)} having skipMarkerCheck set to false. */ public void removeFromDataStructureNoSkip1() { //check inexisten node assert !container.removeFromDataStructure(nodes.notExistent, false); //check root - all the subnodes should be deleted and marked as invalid, but the root itself nodes.root.markAsDeleted(true); assert container.removeFromDataStructure(Fqn.ROOT, false); assert !nodes.aNode.isValid(); assert !nodes.abNode.isValid(); assert !nodes.abcNode.isValid(); assert !nodes.adNode.isValid(); assert !nodes.adeNode.isValid(); assert !nodes.adfNode.isValid(); assert !nodes.adfgNode.isValid(); assert !nodes.adfhNode.isValid(); assert nodes.root.isValid(); } /** * tests {@link DataContainerImpl#removeFromDataStructure(Fqn, boolean)} having skipMarkerCheck set to false. */ public void removeFromDataStructureNoSkip2() { //check root - all the subnodes should be deleted and marked as invalid, but the root itself nodes.root.markAsDeleted(false); assert !container.removeFromDataStructure(Fqn.ROOT, false); //check a normal node nodes.adNode.markAsDeleted(true); assert container.removeFromDataStructure(nodes.ad, false); assert !nodes.adeNode.isValid(); assert !nodes.adfNode.isValid(); assert !nodes.adfhNode.isValid(); assert !nodes.adfhNode.isValid(); } /** * tests {@link DataContainerImpl#removeFromDataStructure(Fqn, boolean)} having skipMarkerCheck set to true. */ public void removeFromDataStructureWithSkip() { //check inexisten node assert !container.removeFromDataStructure(nodes.notExistent, false); //check root - all the subnodes should be deleted and marked as invalid, but the root itself assert container.removeFromDataStructure(Fqn.ROOT, true); assert !nodes.aNode.isValid(); assert !nodes.abNode.isValid(); assert !nodes.abcNode.isValid(); assert !nodes.adNode.isValid(); assert !nodes.adeNode.isValid(); assert !nodes.adfNode.isValid(); assert !nodes.adfgNode.isValid(); assert !nodes.adfhNode.isValid(); assert nodes.root.isValid(); } /** * tests {@link DataContainerImpl#evict(Fqn)} */ public void testEvict() { //tests eviction of leaf nodes assert container.evict(nodes.abc); assert !nodes.abcNode.isValid(); assert !nodes.abNode.hasChild("c"); //test eviction of intermediate nodes nodes.adNode.put("key", "value"); assert !container.evict(nodes.ad); assert nodes.adNode.isValid(); assert nodes.adNode.getData().isEmpty(); assert nodes.aNode.hasChild("d"); assert nodes.adNode.hasChild("e"); } /** * test {@link DataContainerImpl#createNodes(Fqn)} */ public void testCreateNodes() { Object[] objects = container.createNodes(Fqn.fromString("/a/x/y/z")); List result = (List) objects[0]; assert result.size() == 3; assert ((NodeSPI) result.get(0)).getFqn().equals(Fqn.fromString("/a/x")); assert ((NodeSPI) result.get(1)).getFqn().equals(Fqn.fromString("/a/x/y")); assert ((NodeSPI) result.get(2)).getFqn().equals(Fqn.fromString("/a/x/y/z")); NodeSPI target = (NodeSPI) objects[1]; assert target != null; assert target.getFqn().toString().equals("/a/x/y/z"); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/CacheFactoryTest.java0000644000175000017500000001336011017455042026474 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.XmlConfigurationParser; import org.jboss.cache.lock.IsolationLevel; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.transaction.DummyTransactionManagerLookup; import static org.testng.AssertJUnit.assertEquals; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.io.InputStream; /** * @author Manik Surtani (manik@jboss.org) */ @Test(groups = {"functional"}) public class CacheFactoryTest { Configuration expected; String configFile = "META-INF/conf-test/replSync-service.xml"; private CacheSPI cache; @BeforeMethod(alwaysRun = true) public void setUp() { XmlConfigurationParser parser = new XmlConfigurationParser(); expected = parser.parseFile(configFile); } @AfterMethod(alwaysRun = true) public void tearDown() { if (cache != null) { cache.stop(); } } public void testFromConfigFileStarted() { cache = (CacheSPI) new DefaultCacheFactory().createCache(configFile); // can't test for this anymore since the RuntimeConfig is attached to the running cache //assertEquals(expected, cache.getConfiguration()); assert cache.getCacheStatus() == CacheStatus.STARTED : "Should have started"; doSimpleConfTests(cache.getConfiguration()); } public void testFromConfigFileUnstarted() { cache = (CacheSPI) new DefaultCacheFactory().createCache(configFile, false); // can't test for this anymore since the RuntimeConfig is attached to the running cache // assertEquals(expected, cache.getConfiguration()); assert cache.getCacheStatus() != CacheStatus.STARTED : "Should not have started"; doSimpleConfTests(cache.getConfiguration()); } public void testFromConfigObjStarted() { cache = (CacheSPI) new DefaultCacheFactory().createCache(expected); assert cache.getCacheStatus() == CacheStatus.STARTED : "Should have started"; doSimpleConfTests(cache.getConfiguration()); } public void testFromConfigObjUnstarted() { cache = (CacheSPI) new DefaultCacheFactory().createCache(expected, false); assert cache.getCacheStatus() != CacheStatus.STARTED : "Should not have started"; doSimpleConfTests(cache.getConfiguration()); } private void doSimpleConfTests(Configuration tc) { assertEquals(Configuration.CacheMode.REPL_SYNC, tc.getCacheMode()); assertEquals(10000, tc.getLockAcquisitionTimeout()); assertEquals(IsolationLevel.REPEATABLE_READ, tc.getIsolationLevel()); assertEquals(true, tc.isUseRegionBasedMarshalling()); // test some of the XML content. // assertEquals("UDP(ip_mcast=true;ip_ttl=64;loopback=false;mcast_addr=228.1.2.3;mcast_port=48866;mcast_recv_buf_size=80000;mcast_send_buf_size=150000;ucast_recv_buf_size=80000;ucast_send_buf_size=150000):PING(down_thread=false;num_initial_members=3;timeout=2000;up_thread=false):MERGE2(max_interval=20000;min_interval=10000):FD_SOCK:VERIFY_SUSPECT(down_thread=false;timeout=1500;up_thread=false):pbcast.NAKACK(down_thread=false;gc_lag=50;max_xmit_size=8192;retransmit_timeout=600,1200,2400,4800;up_thread=false):UNICAST(down_thread=false;min_threshold=10;timeout=600,1200,2400;window_size=100):pbcast.STABLE(desired_avg_gossip=20000;down_thread=false;up_thread=false):FRAG(down_thread=false;frag_size=8192;up_thread=false):pbcast.GMS(join_retry_timeout=2000;join_timeout=5000;print_local_addr=true;shun=true):pbcast.STATE_TRANSFER(down_thread=true;up_thread=true)", tc.getClusterConfig()); } public void testLifecycle() throws Exception { cache = (CacheSPI) new DefaultCacheFactory().createCache(expected, false); assert cache.getCacheStatus() != CacheStatus.STARTED : "Should not have started"; cache.start(); assert cache.getCacheStatus() == CacheStatus.STARTED : "Should have started"; cache.stop(); assert cache.getCacheStatus() != CacheStatus.STARTED : "Should not have started"; } public void testCreationFromStreamStarted() throws Exception { InputStream is = getClass().getClassLoader().getResourceAsStream(configFile); CacheFactory cf = new DefaultCacheFactory(); cache = (CacheSPI) cf.createCache(is); assert cache.getCacheStatus() == CacheStatus.STARTED : "Should have started"; doSimpleConfTests(cache.getConfiguration()); } public void testCreationFromStream() throws Exception { InputStream is = getClass().getClassLoader().getResourceAsStream(configFile); CacheFactory cf = new DefaultCacheFactory(); cache = (CacheSPI) cf.createCache(is, false); assert cache.getCacheStatus() != CacheStatus.STARTED : "Should not have started"; doSimpleConfTests(cache.getConfiguration()); } public void testComponentsInjected() throws Exception { CacheFactory cf = new DefaultCacheFactory(); Configuration c = new Configuration(); c.setCacheMode(Configuration.CacheMode.REPL_SYNC); c.setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName()); cache = (CacheSPI) cf.createCache(c); assert TestingUtil.extractField(cache, "regionManager") != null; assert TestingUtil.extractField(cache, "notifier") != null; assert TestingUtil.extractField(cache, "marshaller") != null; assert TestingUtil.extractField(cache, "transactionManager") != null; assert TestingUtil.extractField(cache, "transactionTable") != null; assert TestingUtil.extractField(cache, "stateTransferManager") != null; } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/TreeCacheFunctionalTest.java0000755000175000017500000000624311010537324030011 0ustar twernertwernerpackage org.jboss.cache; import org.jboss.cache.config.Configuration; import org.jboss.cache.lock.IsolationLevel; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertNotNull; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.HashMap; /** * Simple functional tests for CacheSPI * * @author Bela Ban * @version $Id: TreeCacheFunctionalTest.java 5803 2008-05-08 08:50:28Z manik.surtani@jboss.com $ */ @Test(groups = "functional") public class TreeCacheFunctionalTest { CacheSPI cache = null; final Fqn FQN = Fqn.fromString("/myNode"); @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { cache = (CacheSPI) new DefaultCacheFactory().createCache(false); cache.getConfiguration().setCacheMode(Configuration.CacheMode.LOCAL); cache.getConfiguration().setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); cache.getConfiguration().setIsolationLevel(IsolationLevel.REPEATABLE_READ); cache.create(); cache.start(); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { if (cache != null) { cache.stop(); cache.destroy(); cache = null; } } public void testPut() throws CacheException { cache.put("/a/b/c", "age", 38); assertEquals(cache.get("/a/b/c", "age"), 38); assertNotNull(cache.getNode("/a/b/c")); assertEquals(0, cache.getNumberOfLocksHeld()); // assertEquals(0, cache.getLockTable().size()); } public void testPutNullKey() throws CacheException { Object key = null; cache.put("/a/b/c", key, "val"); } public void testPutNullValue() throws CacheException { Object val = null; cache.put("/a/b/c", "key", val); } public void testPutNullKeyAndValues() throws CacheException { Object key = null, val = null; cache.put("/a/b/c", key, val); } public void testPutMapsWithNullValues() throws CacheException { HashMap map = new HashMap(); map.put("key", null); map.put(null, "val"); map.put("a", "b"); map.put(null, null); cache.put("/a/b/c", map); } public void testPutKeys() throws CacheException { cache.put("/a/b/c", "age", 38); cache.put("/a/b/c", "name", "Bela"); assertEquals(cache.get("/a/b/c", "age"), 38); assertNotNull(cache.getNode("/a/b/c")); assertEquals(cache.getNode("/a/b/c").getKeys().size(), 2); assertEquals(cache.exists("/a/b/c"), true); assertEquals(0, cache.getNumberOfLocksHeld()); // assertEquals(0, cache.getLockTable().size()); } public void testRemove() throws CacheException { cache.put("/a/b/c", null); cache.put("/a/b/c/1", null); cache.put("/a/b/c/2", null); cache.put("/a/b/c/3", null); cache.put("/a/b/c/3/a/b/c", null); cache.removeNode("/a/b/c"); assertEquals(0, cache.getNumberOfLocksHeld()); // assertEquals(0, cache.getLockTable().size()); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/invocation/0000755000175000017500000000000011376173744024622 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/invocation/InterceptorChainTest.java0000644000175000017500000001510311015516661031553 0ustar twernertwernerpackage org.jboss.cache.invocation; import org.jboss.cache.interceptors.CallInterceptor; import org.jboss.cache.interceptors.InterceptorChain; import org.jboss.cache.interceptors.InvalidationInterceptor; import org.jboss.cache.interceptors.InvocationContextInterceptor; import org.jboss.cache.interceptors.PessimisticLockInterceptor; import org.jboss.cache.interceptors.TxInterceptor; import org.jboss.cache.interceptors.base.CommandInterceptor; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.ArrayList; import java.util.List; /** * Tests functionality defined by InterceptorChain. * * @author Mircea.Markus@jboss.com * @since 2.2 */ @Test(groups = {"unit"}) public class InterceptorChainTest { private CommandInterceptor icInterceptor; private CommandInterceptor invalidationInterceptor; private CommandInterceptor txInterceptor; private CommandInterceptor pessimisticInterceptor; private CommandInterceptor callInterceptor; private InterceptorChain chain; @BeforeMethod public void setUp() { icInterceptor = create(InvocationContextInterceptor.class); invalidationInterceptor = create(InvalidationInterceptor.class); txInterceptor = create(TxInterceptor.class); pessimisticInterceptor = create(PessimisticLockInterceptor.class); callInterceptor = create(CallInterceptor.class); chain = new InterceptorChain(icInterceptor); } public void testGetIntercpetorsAsList() throws Throwable { invalidationInterceptor.setNext(txInterceptor); txInterceptor.setNext(pessimisticInterceptor); pessimisticInterceptor.setNext(callInterceptor); InterceptorChain chain = new InterceptorChain(invalidationInterceptor); List expectedList = new ArrayList(); expectedList.add(invalidationInterceptor); expectedList.add(txInterceptor); expectedList.add(pessimisticInterceptor); expectedList.add(callInterceptor); assert chain.asList().equals(expectedList); } public void testAddAtPosition() throws Throwable { chain.addInterceptor(invalidationInterceptor, 1); assert invalidationInterceptor.equals(icInterceptor.getNext()); chain.addInterceptor(pessimisticInterceptor, 1); assert pessimisticInterceptor.equals(icInterceptor.getNext()); assert invalidationInterceptor.equals(pessimisticInterceptor.getNext()); assert invalidationInterceptor.getNext() == null; chain.addInterceptor(callInterceptor, 3); assert invalidationInterceptor.getNext().equals(callInterceptor); } public void testAddAtPositionIncremented() { chain.addInterceptor(txInterceptor, 1); chain.addInterceptor(invalidationInterceptor, 2); chain.addInterceptor(pessimisticInterceptor, 3); chain.addInterceptor(callInterceptor, 4); assert icInterceptor.getNext().equals(txInterceptor); assert txInterceptor.getNext().equals(invalidationInterceptor); assert invalidationInterceptor.getNext().equals(pessimisticInterceptor); assert pessimisticInterceptor.getNext().equals(callInterceptor); } public void testRemoveAtPostion() throws Throwable { chain.addInterceptor(txInterceptor, 1); chain.addInterceptor(invalidationInterceptor, 2); chain.addInterceptor(pessimisticInterceptor, 3); chain.addInterceptor(callInterceptor, 4); chain.removeInterceptor(4); assert chain.size() == 4; assert pessimisticInterceptor.getNext() == null; chain.removeInterceptor(0); assert chain.size() == 3; chain.getFirstInChain().equals(txInterceptor); chain.removeInterceptor(1); assert chain.size() == 2; assert txInterceptor.getNext().equals(pessimisticInterceptor); } public void testGetSize() { assert chain.size() == 1; chain.addInterceptor(txInterceptor, 1); assert chain.size() == 2; chain.addInterceptor(invalidationInterceptor, 2); assert chain.size() == 3; chain.addInterceptor(pessimisticInterceptor, 3); assert chain.size() == 4; chain.addInterceptor(callInterceptor, 4); assert chain.size() == 5; } public void testAppendInterceptor() { chain.appendIntereceptor(txInterceptor); assert chain.size() == 2; assert icInterceptor.getNext().equals(txInterceptor); chain.appendIntereceptor(invalidationInterceptor); assert chain.size() == 3; assert txInterceptor.getNext().equals(invalidationInterceptor); } public void testGetInterceptorsWhichExtend() { InvocationContextInterceptor ic2 = (InvocationContextInterceptor) create(InvocationContextInterceptor.class); chain.appendIntereceptor(ic2); List result = chain.getInterceptorsWhichExtend(InvocationContextInterceptor.class); assert result.contains(icInterceptor); assert result.contains(ic2); assert result.size() == 2; result = chain.getInterceptorsWhichExtend(CommandInterceptor.class); assert result.size() == chain.asList().size(); } public void removeInterceptorWithtType() { chain.addInterceptor(txInterceptor, 1); chain.addInterceptor(invalidationInterceptor, 2); chain.addInterceptor(pessimisticInterceptor, 3); chain.addInterceptor(callInterceptor, 4); chain.removeInterceptor(InvalidationInterceptor.class); assert chain.size() == 4; assert txInterceptor.getNext().equals(pessimisticInterceptor); chain.removeInterceptor(InvocationContextInterceptor.class); assert chain.size() == 3; assert chain.getFirstInChain().equals(txInterceptor); chain.removeInterceptor(CallInterceptor.class); assert chain.size() == 2; assert pessimisticInterceptor.getNext() == null; } public void addInterceptorWithType() { assert chain.addInterceptor(invalidationInterceptor, icInterceptor.getClass()); assert icInterceptor.getNext().equals(invalidationInterceptor); chain.addInterceptor(txInterceptor, icInterceptor.getClass()); assert icInterceptor.getNext().equals(txInterceptor); assert txInterceptor.getNext().equals(invalidationInterceptor); } private CommandInterceptor create(Class toInstantiate) { try { return toInstantiate.newInstance(); } catch (Throwable th) { throw new RuntimeException(th); } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/optimistic/0000755000175000017500000000000011376173740024631 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/optimistic/TxInterceptorTest.java0000755000175000017500000006440111017455042031144 0ustar twernertwerner/* * Created on 17-Feb-2005 * * * */ package org.jboss.cache.optimistic; import org.jboss.cache.CacheSPI; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.commands.ReversibleCommand; import org.jboss.cache.commands.tx.CommitCommand; import org.jboss.cache.commands.tx.OptimisticPrepareCommand; import org.jboss.cache.commands.tx.RollbackCommand; import org.jboss.cache.loader.SamplePojo; import org.jboss.cache.transaction.GlobalTransaction; import org.jboss.cache.transaction.OptimisticTransactionEntry; import org.jboss.cache.transaction.TransactionSetup; import org.jboss.cache.transaction.TransactionTable; import org.jgroups.Address; import static org.testng.AssertJUnit.*; import org.testng.annotations.Test; import javax.transaction.Transaction; import javax.transaction.TransactionManager; import java.util.List; import java.util.Map; @Test(groups = {"functional", "transaction"}) public class TxInterceptorTest extends AbstractOptimisticTestCase { @Override protected CacheSPI createCacheUnstarted(boolean optimistic) throws Exception { CacheSPI cache = super.createCacheUnstarted(optimistic); cache.getConfiguration().setTransactionManagerLookupClass(TransactionSetup.getManagerLookup()); return cache; } public void testNoTransaction() throws Exception { CacheSPI cache = createCache(); MockInterceptor dummy = new MockInterceptor(); setAlteredInterceptorChain(dummy, cache); TransactionManager mgr = cache.getConfiguration().getRuntimeConfig().getTransactionManager(); assertNull(mgr.getTransaction()); assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache.getTransactionTable().getNumLocalTransactions()); SamplePojo pojo = new SamplePojo(21, "test"); cache.put("/one/two", "key1", pojo); assertNull(mgr.getTransaction()); assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache.getTransactionTable().getNumLocalTransactions()); //make sure all calls were done in right order List calls = dummy.getAllCalledIds(); assertEquals(OptimisticPrepareCommand.METHOD_ID, calls.get(0)); assertEquals(CommitCommand.METHOD_ID, calls.get(1)); //flesh this out a bit more } public void testLocalTransactionExists() throws Exception { CacheSPI cache = createCache(); MockInterceptor dummy = new MockInterceptor(); setAlteredInterceptorChain(dummy, cache); TransactionManager mgr = cache.getConfiguration().getRuntimeConfig().getTransactionManager(); //start local transaction mgr.begin(); Transaction tx = mgr.getTransaction(); SamplePojo pojo = new SamplePojo(21, "test"); assertNotNull(mgr.getTransaction()); TransactionTable txTable = cache.getTransactionTable(); assertNull(txTable.get(tx)); cache.put("/one/two", "key1", pojo); assertNotNull(mgr.getTransaction()); mgr.commit(); assertNull(mgr.getTransaction()); List calls = dummy.getAllCalledIds(); assertEquals(OptimisticPrepareCommand.METHOD_ID, calls.get(0)); assertEquals(CommitCommand.METHOD_ID, calls.get(1)); assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache.getTransactionTable().getNumLocalTransactions()); cache.stop(); } public void testRollbackTransaction() throws Exception { CacheSPI cache = createCache(); MockInterceptor dummy = new MockInterceptor(); setAlteredInterceptorChain(dummy, cache); TransactionManager mgr = cache.getConfiguration().getRuntimeConfig().getTransactionManager(); //start local transaction mgr.begin(); SamplePojo pojo = new SamplePojo(21, "test"); cache.put("/one/two", "key1", pojo); assertNotNull(mgr.getTransaction()); mgr.rollback(); assertNull(mgr.getTransaction()); List calls = dummy.getAllCalledIds(); assertEquals(1, calls.size()); assertEquals(RollbackCommand.METHOD_ID, calls.get(0)); assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache.getTransactionTable().getNumLocalTransactions()); cache.stop(); } public void testEmptyLocalTransaction() throws Exception { CacheSPI cache = createCache(); MockInterceptor dummy = new MockInterceptor(); setAlteredInterceptorChain(dummy, cache); TransactionManager mgr = cache.getConfiguration().getRuntimeConfig().getTransactionManager(); //start local transaction mgr.begin(); assertNotNull(mgr.getTransaction()); mgr.commit(); assertNull(mgr.getTransaction()); List calls = dummy.getAllCalled(); assertEquals(0, calls.size()); assertNull(mgr.getTransaction()); assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache.getTransactionTable().getNumLocalTransactions()); cache.stop(); } public void testEmptyRollbackLocalTransaction() throws Exception { CacheSPI cache = createCache(); MockInterceptor dummy = new MockInterceptor(); setAlteredInterceptorChain(dummy, cache); TransactionManager mgr = cache.getConfiguration().getRuntimeConfig().getTransactionManager(); //start local transaction mgr.begin(); assertNotNull(mgr.getTransaction()); mgr.rollback(); assertNull(mgr.getTransaction()); List calls = dummy.getAllCalled(); assertEquals(0, calls.size()); assertNull(mgr.getTransaction()); assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache.getTransactionTable().getNumLocalTransactions()); cache.stop(); } public void testLocalRollbackAftercommitTransaction() throws Exception { CacheSPI cache = createCache(); MockInterceptor dummy = new MockInterceptor(); setAlteredInterceptorChain(dummy, cache); TransactionManager mgr = cache.getConfiguration().getRuntimeConfig().getTransactionManager(); //start local transaction mgr.begin(); SamplePojo pojo = new SamplePojo(21, "test"); cache.put("/one/two", "key1", pojo); assertNotNull(mgr.getTransaction()); mgr.commit(); assertNull(mgr.getTransaction()); List calls = dummy.getAllCalledIds(); assertEquals(OptimisticPrepareCommand.METHOD_ID, calls.get(0)); assertEquals(CommitCommand.METHOD_ID, calls.get(1)); boolean failed = false; try { mgr.rollback(); fail(); } catch (Exception e) { failed = true; assertTrue(true); } assertTrue(failed); assertNull(mgr.getTransaction()); assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache.getTransactionTable().getNumLocalTransactions()); cache.stop(); } public void testgtxTransactionExists() throws Exception { CacheSPI cache = createCache(); MockInterceptor dummy = new MockInterceptor(); setAlteredInterceptorChain(dummy, cache); TransactionManager mgr = cache.getConfiguration().getRuntimeConfig().getTransactionManager(); //start local transaction mgr.begin(); Transaction tx = mgr.getTransaction(); //this sets cache.getCurrentTransaction(tx, true); SamplePojo pojo = new SamplePojo(21, "test"); cache.put("/one/two", "key1", pojo); assertNotNull(mgr.getTransaction()); mgr.commit(); assertNull(mgr.getTransaction()); List calls = dummy.getAllCalledIds(); assertEquals(OptimisticPrepareCommand.METHOD_ID, calls.get(0)); assertEquals(CommitCommand.METHOD_ID, calls.get(1)); assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache.getTransactionTable().getNumLocalTransactions()); cache.stop(); } public void testRemotePrepareTransaction() throws Exception { CacheSPI cache = createCache(); MockInterceptor dummy = new MockInterceptor(); setAlteredInterceptorChain(dummy, cache); TransactionManager mgr = cache.getConfiguration().getRuntimeConfig().getTransactionManager(); //start local transaction mgr.begin(); Transaction tx = mgr.getTransaction(); SamplePojo pojo = new SamplePojo(21, "test"); cache.put("/one/two", "key1", pojo); GlobalTransaction gtx = cache.getCurrentTransaction(tx, true); TransactionTable table = cache.getTransactionTable(); OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table.get(gtx); assertNotNull(mgr.getTransaction()); ReversibleCommand command = entry.getModifications().get(0); mgr.commit(); //test local calls List calls = dummy.getAllCalledIds(); assertEquals(OptimisticPrepareCommand.METHOD_ID, calls.get(0)); assertEquals(CommitCommand.METHOD_ID, calls.get(1)); assertNull(mgr.getTransaction()); assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache.getTransactionTable().getNumLocalTransactions()); GlobalTransaction remoteGtx = new GlobalTransaction(); remoteGtx.setAddress(new DummyAddress()); //hack the method call to make it have the remote globalTransaction command.setGlobalTransaction(remoteGtx); //call our remote method OptimisticPrepareCommand prepareCommand = new OptimisticPrepareCommand(remoteGtx, injectDataVersion(entry.getModifications()), null, (Address) remoteGtx.getAddress(), Boolean.FALSE); try { TestingUtil.replicateCommand(cache, prepareCommand); } catch (Throwable t) { fail(); } //our thread should still be null assertNull(mgr.getTransaction()); //there should be a registration for the remote globalTransaction assertNotNull(table.get(remoteGtx)); assertNotNull(table.getLocalTransaction(remoteGtx)); //assert that the method has been passed up the stack calls = dummy.getAllCalledIds(); assertEquals(OptimisticPrepareCommand.METHOD_ID, calls.get(2)); //assert we have the tx in th table assertEquals(1, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(1, cache.getTransactionTable().getNumLocalTransactions()); cache.stop(); } public void testRemotePrepareSuspendTransaction() throws Exception { CacheSPI cache = createCache(); MockInterceptor dummy = new MockInterceptor(); setAlteredInterceptorChain(dummy, cache); TransactionManager mgr = cache.getConfiguration().getRuntimeConfig().getTransactionManager(); //start local transaction mgr.begin(); Transaction tx = mgr.getTransaction(); SamplePojo pojo = new SamplePojo(21, "test"); cache.put("/one/two", "key1", pojo); GlobalTransaction gtx = cache.getCurrentTransaction(tx, true); TransactionTable table = cache.getTransactionTable(); OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table.get(gtx); assertEquals(tx, mgr.getTransaction()); //now send the remote prepare GlobalTransaction remoteGtx = new GlobalTransaction(); remoteGtx.setAddress(new DummyAddress()); //hack the method call to make it have the remote globalTransaction ReversibleCommand command = entry.getModifications().get(0); command.setGlobalTransaction(remoteGtx); //call our remote method OptimisticPrepareCommand prepareCommand = new OptimisticPrepareCommand(remoteGtx, injectDataVersion(entry.getModifications()), null, (Address) remoteGtx.getAddress(), Boolean.FALSE); try { TestingUtil.replicateCommand(cache, prepareCommand); } catch (Throwable t) { t.printStackTrace(); fail(); } //we should have the same transaction back again assertEquals(tx, mgr.getTransaction()); // there should be a registration for the remote globalTransaction assertNotNull(table.get(remoteGtx)); assertNotNull(table.getLocalTransaction(remoteGtx)); List calls = dummy.getAllCalledIds(); assertEquals(OptimisticPrepareCommand.METHOD_ID, calls.get(0)); //assert we have two current transactions assertEquals(2, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(2, cache.getTransactionTable().getNumLocalTransactions()); //commit the local tx mgr.commit(); //check local calls calls = dummy.getAllCalledIds(); assertEquals(OptimisticPrepareCommand.METHOD_ID, calls.get(1)); assertEquals(CommitCommand.METHOD_ID, calls.get(2)); //assert we have only 1 transaction left assertEquals(1, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(1, cache.getTransactionTable().getNumLocalTransactions()); assertNotNull(table.get(remoteGtx)); assertNotNull(table.getLocalTransaction(remoteGtx)); assertNull(table.get(gtx)); assertNull(table.getLocalTransaction(gtx)); //assert we are no longer associated assertEquals(null, mgr.getTransaction()); cache.stop(); } public void testRemoteCommitSuspendTransaction() throws Exception { CacheSPI cache = createCache(); MockInterceptor dummy = new MockInterceptor(); setAlteredInterceptorChain(dummy, cache); TransactionManager mgr = cache.getConfiguration().getRuntimeConfig().getTransactionManager(); //start local transaction mgr.begin(); Transaction tx = mgr.getTransaction(); //this sets cache.getCurrentTransaction(tx, true); SamplePojo pojo = new SamplePojo(21, "test"); cache.put("/one/two", "key1", pojo); GlobalTransaction gtx = cache.getCurrentTransaction(tx, true); TransactionTable table = cache.getTransactionTable(); OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table.get(gtx); assertEquals(tx, mgr.getTransaction()); //now send the remote prepare GlobalTransaction remoteGtx = new GlobalTransaction(); remoteGtx.setAddress(new DummyAddress()); //hack the method call to make it have the remote globalTransaction ReversibleCommand command = entry.getModifications().get(0); command.setGlobalTransaction(remoteGtx); //call our remote method OptimisticPrepareCommand prepareCommand = new OptimisticPrepareCommand(remoteGtx, injectDataVersion(entry.getModifications()), (Map) null, (Address) remoteGtx.getAddress(), Boolean.FALSE); try { TestingUtil.replicateCommand(cache, prepareCommand); } catch (Throwable t) { fail(); } assertEquals(2, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(2, cache.getTransactionTable().getNumLocalTransactions()); // call our remote method CommitCommand commitMethod = new CommitCommand(remoteGtx); try { TestingUtil.replicateCommand(cache, commitMethod); } catch (Throwable t) { t.printStackTrace(); fail(); } //we should have the same transaction back again assertEquals(tx, mgr.getTransaction()); // there should be a registration for the remote globalTransaction assertNull(table.get(remoteGtx)); assertNull(table.getLocalTransaction(remoteGtx)); List calls = dummy.getAllCalledIds(); assertEquals(OptimisticPrepareCommand.METHOD_ID, calls.get(0)); assertEquals(CommitCommand.METHOD_ID, calls.get(1)); assertEquals(1, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(1, cache.getTransactionTable().getNumLocalTransactions()); //commit the local tx mgr.commit(); calls = dummy.getAllCalledIds(); assertEquals(OptimisticPrepareCommand.METHOD_ID, calls.get(2)); assertEquals(CommitCommand.METHOD_ID, calls.get(3)); assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache.getTransactionTable().getNumLocalTransactions()); assertEquals(null, mgr.getTransaction()); cache.stop(); } public void testRemoteRollbackSuspendTransaction() throws Exception { CacheSPI cache = createCache(); MockInterceptor dummy = new MockInterceptor(); setAlteredInterceptorChain(dummy, cache); TransactionManager mgr = cache.getConfiguration().getRuntimeConfig().getTransactionManager(); //start local transaction mgr.begin(); Transaction tx = mgr.getTransaction(); //this sets cache.getCurrentTransaction(tx, true); SamplePojo pojo = new SamplePojo(21, "test"); cache.put("/one/two", "key1", pojo); GlobalTransaction gtx = cache.getCurrentTransaction(tx, true); TransactionTable table = cache.getTransactionTable(); OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table.get(gtx); assertEquals(tx, mgr.getTransaction()); //now send the remote prepare GlobalTransaction remoteGtx = new GlobalTransaction(); remoteGtx.setAddress(new DummyAddress()); //hack the method call to make it have the remote globalTransaction ReversibleCommand command = entry.getModifications().get(0); command.setGlobalTransaction(remoteGtx); //call our remote method OptimisticPrepareCommand prepareCommand = new OptimisticPrepareCommand(remoteGtx, injectDataVersion(entry.getModifications()), (Map) null, (Address) remoteGtx.getAddress(), Boolean.FALSE); try { TestingUtil.replicateCommand(cache, prepareCommand); } catch (Throwable t) { fail(); } assertEquals(2, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(2, cache.getTransactionTable().getNumLocalTransactions()); // call our remote method RollbackCommand rollbackCommand = new RollbackCommand(remoteGtx); try { TestingUtil.replicateCommand(cache, rollbackCommand); } catch (Throwable t) { fail(); } //we should have the same transaction back again assertEquals(tx, mgr.getTransaction()); // there should be a registration for the remote globalTransaction assertNull(table.get(remoteGtx)); assertNull(table.getLocalTransaction(remoteGtx)); List calls = dummy.getAllCalledIds(); assertEquals(OptimisticPrepareCommand.METHOD_ID, calls.get(0)); assertEquals(RollbackCommand.METHOD_ID, calls.get(1)); assertEquals(1, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(1, cache.getTransactionTable().getNumLocalTransactions()); //commit the local tx mgr.commit(); calls = dummy.getAllCalledIds(); assertEquals(OptimisticPrepareCommand.METHOD_ID, calls.get(2)); assertEquals(CommitCommand.METHOD_ID, calls.get(3)); assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache.getTransactionTable().getNumLocalTransactions()); assertEquals(null, mgr.getTransaction()); cache.stop(); } public void testRemoteCommitTransaction() throws Exception { CacheSPI cache = createCache(); MockInterceptor dummy = new MockInterceptor(); setAlteredInterceptorChain(dummy, cache); TransactionManager mgr = cache.getConfiguration().getRuntimeConfig().getTransactionManager(); //start local transaction mgr.begin(); Transaction tx = mgr.getTransaction(); //this sets cache.getCurrentTransaction(tx, true); SamplePojo pojo = new SamplePojo(21, "test"); cache.put("/one/two", "key1", pojo); GlobalTransaction gtx = cache.getCurrentTransaction(tx, true); TransactionTable table = cache.getTransactionTable(); OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table.get(gtx); assertNotNull(mgr.getTransaction()); ReversibleCommand command = entry.getModifications().get(0); mgr.commit(); //test local calls List calls = dummy.getAllCalledIds(); assertEquals(OptimisticPrepareCommand.METHOD_ID, calls.get(0)); assertEquals(CommitCommand.METHOD_ID, calls.get(1)); assertNull(mgr.getTransaction()); assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache.getTransactionTable().getNumLocalTransactions()); GlobalTransaction remoteGtx = new GlobalTransaction(); remoteGtx.setAddress(new DummyAddress()); // hack the method call to make it have the remote globalTransaction command.setGlobalTransaction(remoteGtx); //call our remote method OptimisticPrepareCommand prepareCommand = new OptimisticPrepareCommand(remoteGtx, injectDataVersion(entry.getModifications()), (Map) null, (Address) remoteGtx.getAddress(), Boolean.FALSE); try { TestingUtil.replicateCommand(cache, prepareCommand); } catch (Throwable t) { fail(); } //our thread should be null assertNull(mgr.getTransaction()); assertEquals(1, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(1, cache.getTransactionTable().getNumLocalTransactions()); // there should be a registration for the remote globalTransaction assertNotNull(table.get(remoteGtx)); assertNotNull(table.getLocalTransaction(remoteGtx)); //this is not populated until replication interceptor is used // assertEquals(1, table.get(remoteGtx).getModifications().size()); calls = dummy.getAllCalledIds(); assertEquals(OptimisticPrepareCommand.METHOD_ID, calls.get(2)); assertNull(mgr.getTransaction()); // call our remote method CommitCommand commitCommand = new CommitCommand(remoteGtx); try { TestingUtil.replicateCommand(cache, commitCommand); } catch (Throwable t) { fail(); } assertNull(table.get(remoteGtx)); assertNull(table.getLocalTransaction(remoteGtx)); assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache.getTransactionTable().getNumLocalTransactions()); assertNull(mgr.getTransaction()); cache.stop(); } public void testRemoteRollbackTransaction() throws Exception { CacheSPI cache = createCache(); MockInterceptor dummy = new MockInterceptor(); setAlteredInterceptorChain(dummy, cache); TransactionManager mgr = cache.getConfiguration().getRuntimeConfig().getTransactionManager(); //start local transaction mgr.begin(); Transaction tx = mgr.getTransaction(); //this sets cache.getCurrentTransaction(tx, true); SamplePojo pojo = new SamplePojo(21, "test"); cache.put("/one/two", "key1", pojo); GlobalTransaction gtx = cache.getCurrentTransaction(tx, true); TransactionTable table = cache.getTransactionTable(); OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table.get(gtx); assertNotNull(mgr.getTransaction()); ReversibleCommand command = entry.getModifications().get(0); mgr.commit(); //test local calls List calls = dummy.getAllCalledIds(); assertEquals(OptimisticPrepareCommand.METHOD_ID, calls.get(0)); assertEquals(CommitCommand.METHOD_ID, calls.get(1)); GlobalTransaction remoteGtx = new GlobalTransaction(); remoteGtx.setAddress(new DummyAddress()); command.setGlobalTransaction(remoteGtx); //call our remote method OptimisticPrepareCommand prepareCommand = new OptimisticPrepareCommand(remoteGtx, injectDataVersion(entry.getModifications()), (Map) null, (Address) remoteGtx.getAddress(), Boolean.FALSE); try { TestingUtil.replicateCommand(cache, prepareCommand); } catch (Throwable t) { fail(); } //our thread should be null assertNull(mgr.getTransaction()); // there should be a registration for the remote globalTransaction assertNotNull(table.get(remoteGtx)); assertNotNull(table.getLocalTransaction(remoteGtx)); //this is not populated until replication interceptor is used // assertEquals(1, table.get(remoteGtx).getModifications().size()); calls = dummy.getAllCalledIds(); assertEquals(OptimisticPrepareCommand.METHOD_ID, calls.get(2)); // call our remote method RollbackCommand rollbackCommand = new RollbackCommand(remoteGtx); try { TestingUtil.replicateCommand(cache, rollbackCommand); } catch (Throwable t) { fail(); } calls = dummy.getAllCalledIds(); assertEquals(OptimisticPrepareCommand.METHOD_ID, calls.get(2)); assertEquals(RollbackCommand.METHOD_ID, calls.get(3)); assertNull(table.get(remoteGtx)); assertNull(table.getLocalTransaction(remoteGtx)); assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache.getTransactionTable().getNumLocalTransactions()); cache.stop(); } public void testSequentialTransactionExists() throws Exception { CacheSPI cache = createCache(); MockInterceptor dummy = new MockInterceptor(); setAlteredInterceptorChain(dummy, cache); TransactionManager mgr = cache.getConfiguration().getRuntimeConfig().getTransactionManager(); mgr.begin(); Transaction tx = mgr.getTransaction(); SamplePojo pojo = new SamplePojo(21, "test"); cache.put("/one/two", "key1", pojo); assertNotNull(mgr.getTransaction()); mgr.commit(); mgr.begin(); Transaction tx1 = mgr.getTransaction(); assertNotNull(tx1); assertNotSame(tx, tx1); cache.put("/one/two", "key1", pojo); mgr.commit(); List calls = dummy.getAllCalledIds(); assertEquals(OptimisticPrepareCommand.METHOD_ID, calls.get(0)); assertEquals(CommitCommand.METHOD_ID, calls.get(1)); assertEquals(OptimisticPrepareCommand.METHOD_ID, calls.get(2)); assertEquals(CommitCommand.METHOD_ID, calls.get(3)); cache.stop(); } } ././@LongLink0000000000000000000000000000014500000000000011565 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/optimistic/OptimisticWithCacheLoaderTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/optimistic/OptimisticWithCacheLoaderTest.java0000644000175000017500000002212110765362374033371 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.optimistic; import org.jboss.cache.CacheSPI; import org.jboss.cache.config.Option; import org.jboss.cache.loader.CacheLoader; import static org.testng.AssertJUnit.*; import org.testng.annotations.Test; import javax.transaction.Transaction; import javax.transaction.TransactionManager; /** * Tests optimistic locking with cache loaders * * @author Manik Surtani (manik@jboss.org) */ @Test(groups = {"functional", "transaction"}) public class OptimisticWithCacheLoaderTest extends AbstractOptimisticTestCase { public void testLoaderIndependently() throws Exception { CacheSPI cache = createCacheWithLoader(); CacheLoader loader = cache.getCacheLoaderManager().getCacheLoader(); // test the cache loader independently first ... loader.remove(fqn); assert loader.get(fqn) == null; loader.put(fqn, key, value); assertEquals(value, loader.get(fqn).get(key)); // clean up loader.remove(fqn); assertNull(loader.get(fqn)); } public void testCacheLoadOnTree() throws Exception { CacheLoader loader = null; try { CacheSPI cache = createCacheWithLoader(); loader = cache.getCacheLoaderManager().getCacheLoader(); TransactionManager mgr = cache.getTransactionManager(); Transaction tx; // make sure the fqn is not in cache assertNull(cache.getNode(fqn)); // put something in the loader and make sure all tx's can see it loader.put(fqn, key, value); // start the 1st tx mgr.begin(); tx = mgr.getTransaction(); assertEquals(value, cache.get(fqn, key)); mgr.suspend(); // start a new tx mgr.begin(); assertEquals(value, cache.get(fqn, key)); mgr.commit(); mgr.resume(tx); assertEquals(value, cache.get(fqn, key)); mgr.commit(); } finally { // cleanup if (loader != null) loader.remove(fqn); } } public void testCacheStoring() throws Exception { Transaction tx; CacheSPI cache = createCacheWithLoader(); CacheLoader loader = cache.getCacheLoaderManager().getCacheLoader(); // test the cache ... TransactionManager mgr = cache.getTransactionManager(); assertNull(mgr.getTransaction()); mgr.begin(); cache.put(fqn, key, value); mgr.commit(); assertEquals(value, cache.get(fqn, key)); //now lets see if the state has been persisted in the cache loader assertEquals(value, loader.get(fqn).get(key)); mgr.begin(); cache.removeNode(fqn); mgr.commit(); assertNull(cache.get(fqn, key)); //now lets see if the state has been persisted in the cache loader assertNull(loader.get(fqn)); mgr.begin(); cache.put(fqn, key, value); tx = mgr.getTransaction(); mgr.suspend(); // lets see what we've got halfway within a tx assertNull(cache.get(fqn, key)); assertNull(loader.get(fqn)); mgr.resume(tx); mgr.commit(); // and after committing... assertEquals(value, cache.get(fqn, key)); assertEquals(value, loader.get(fqn).get(key)); // clean up loader loader.remove(fqn); } public void testCacheStoringImplicitTx() throws Exception { CacheSPI cache = createCacheWithLoader(); CacheLoader loader = cache.getCacheLoaderManager().getCacheLoader(); // test the cache ... TransactionManager mgr = cache.getTransactionManager(); assertNull(mgr.getTransaction()); cache.put(fqn, key, value); assertEquals(value, cache.get(fqn, key)); //now lets see if the state has been persisted in the cache loader assertEquals(value, loader.get(fqn).get(key)); cache.removeNode(fqn); assertNull(cache.get(fqn, key)); //now lets see if the state has been persisted in the cache loader assertNull(loader.get(fqn)); cache.put(fqn, key, value); assertEquals(value, cache.get(fqn, key)); assertEquals(value, loader.get(fqn).get(key)); // clean up loader loader.remove(fqn); } public void testCacheStoringImplicitTxOptionOverride() throws Exception { CacheSPI cache = createCacheWithLoader(); CacheLoader loader = cache.getCacheLoaderManager().getCacheLoader(); Option option = new Option(); option.setCacheModeLocal(true); cache.getInvocationContext().setOptionOverrides(option); cache.put(fqn, key, value); assertEquals(value, cache.get(fqn, key)); //now lets see if the state has been persisted in the cache loader assertNotNull(loader.get(fqn)); assertNotNull(value, loader.get(fqn).get(key)); cache.removeNode(fqn); } public void testCacheLoading() throws Exception { CacheSPI cache = createCacheWithLoader(); CacheLoader loader = cache.getCacheLoaderManager().getCacheLoader(); assertNull(cache.get(fqn, key)); // put something in the loader loader.put(fqn, key, value); assertEquals(value, loader.get(fqn).get(key)); // test that this can now be accessed by the cache assertEquals(value, cache.get(fqn, key)); // clean up loader loader.remove(fqn); // what's in the cache now? Should not ne null... assertNotNull(cache.get(fqn, key)); } public void testCacheLoadingWithReplication() throws Exception { CacheSPI cache1 = createReplicatedCacheWithLoader(false); CacheLoader loader1 = cache1.getCacheLoaderManager().getCacheLoader(); CacheSPI cache2 = createReplicatedCacheWithLoader(false); CacheLoader loader2 = cache2.getCacheLoaderManager().getCacheLoader(); // test the cache ... TransactionManager mgr = cache1.getTransactionManager(); assertNull(mgr.getTransaction()); mgr.begin(); // add something in cache1 cache1.put(fqn, key, value); assertEquals(value, cache1.get(fqn, key)); // test that loader1, loader2, cache2 doesnt have entry assertNull(loader1.get(fqn)); assertNull(loader2.get(fqn)); assertNull(cache2.get(fqn, key)); // commit mgr.commit(); // test that loader1, loader2, cache2 has entry assertEquals(value, cache1.get(fqn, key)); assertEquals(value, loader1.get(fqn).get(key)); assertEquals(value, loader2.get(fqn).get(key)); assertEquals(value, cache2.get(fqn, key)); // cache2 removes entry cache2.getTransactionManager().begin(); cache2.removeNode(fqn); assertNull(cache2.get(fqn, key)); // test that loader1, loader2 and cache2 have the entry assertEquals(value, cache1.get(fqn, key)); assertEquals(value, loader1.get(fqn).get(key)); assertEquals(value, loader2.get(fqn).get(key)); // commit cache2.getTransactionManager().commit(); // test that the entry has been removed everywhere. assertNull(cache1.getNode(fqn)); assertNull(loader1.get(fqn)); assertNull(loader2.get(fqn)); assertNull(cache2.getNode(fqn)); } public void testSharedCacheLoadingWithReplication() throws Exception { CacheSPI cache1 = createReplicatedCacheWithLoader(true); CacheLoader loader1 = cache1.getCacheLoaderManager().getCacheLoader(); CacheSPI cache2 = createReplicatedCacheWithLoader(true); CacheLoader loader2 = cache2.getCacheLoaderManager().getCacheLoader(); // test the cache ... TransactionManager mgr = cache1.getTransactionManager(); assertNull(mgr.getTransaction()); mgr.begin(); // add something in cache1 cache1.put(fqn, key, value); assertEquals(value, cache1.get(fqn, key)); // test that loader1, loader2, cache2 doesnt have entry System.out.println("*** " + loader1.get(fqn)); assertNull(loader1.get(fqn)); assertNull(loader2.get(fqn)); assertNull(cache2.get(fqn, key)); // commit mgr.commit(); // test that loader1, loader2, cache2 has entry assertEquals(value, cache1.get(fqn, key)); assertEquals(value, loader1.get(fqn).get(key)); assertEquals(value, loader2.get(fqn).get(key)); assertEquals(value, cache2.get(fqn, key)); // cache2 removes entry cache2.getTransactionManager().begin(); cache2.removeNode(fqn); assertNull(cache2.get(fqn, key)); // test that loader1, loader2 and cache2 have the entry assertEquals(value, cache1.get(fqn, key)); assertEquals(value, loader1.get(fqn).get(key)); assertEquals(value, loader2.get(fqn).get(key)); // commit cache2.getTransactionManager().commit(); // test that the entry has been removed everywhere. assertNull(cache1.getNode(fqn)); assertNull(loader1.get(fqn)); assertNull(loader2.get(fqn)); assertNull(cache2.getNode(fqn)); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/optimistic/NodeInterceptorPutEraseTest.java0000755000175000017500000001233011017455042033101 0ustar twernertwernerpackage org.jboss.cache.optimistic; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.interceptors.OptimisticCreateIfNotExistsInterceptor; import org.jboss.cache.interceptors.OptimisticNodeInterceptor; import org.jboss.cache.interceptors.base.CommandInterceptor; import org.jboss.cache.loader.SamplePojo; import org.jboss.cache.transaction.GlobalTransaction; import org.jboss.cache.transaction.OptimisticTransactionEntry; import org.jboss.cache.transaction.TransactionTable; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.Transaction; import javax.transaction.TransactionManager; import java.util.HashMap; import java.util.Map; /** * @author xenephon */ @SuppressWarnings("unchecked") @Test(groups = "functional") public class NodeInterceptorPutEraseTest extends AbstractOptimisticTestCase { private CacheSPI cache; private TransactionManager mgr; private TestListener listener; private MockInterceptor dummy; @BeforeMethod public void setUp() throws Exception { listener = new TestListener(); cache = createCacheWithListener(listener); CommandInterceptor interceptor = TestingUtil.findInterceptor(cache, OptimisticCreateIfNotExistsInterceptor.class); CommandInterceptor nodeInterceptor = TestingUtil.findInterceptor(cache, OptimisticNodeInterceptor.class); dummy = new MockInterceptor(); interceptor.setNext(nodeInterceptor); nodeInterceptor.setNext(dummy); TestingUtil.replaceInterceptorChain(cache, interceptor); mgr = cache.getTransactionManager(); } @AfterMethod public void tearDown() { TestingUtil.killCaches(cache); } public void testTransactionPutKeyMethod() throws Exception { mgr.begin(); Transaction tx = mgr.getTransaction(); setupTransactions(cache, tx); SamplePojo pojo = new SamplePojo(21, "test"); Map temp = new HashMap(); temp.put("key1", pojo); cache.put("/one/two", temp); assertEquals(null, dummy.getCalledCommand()); TransactionTable table = cache.getTransactionTable(); GlobalTransaction gtx = table.get(tx); OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table.get(gtx); TransactionWorkspace workspace = entry.getTransactionWorkSpace(); mgr.commit(); //assert what should be the results of our call assertEquals(3, workspace.getNodes().size()); assertNotNull(workspace.getNode(Fqn.fromString("/one/two"))); assertEquals(pojo, workspace.getNode(Fqn.fromString("/one/two")).get("key1")); assertTrue(entry.getLocks().isEmpty()); assertEquals(1, entry.getModifications().size()); assertTrue(!cache.exists("/one/two")); assertEquals(null, dummy.getCalledCommand()); } public void testTransactionKeyValOverwriteMethod() throws Exception { mgr.begin(); Transaction tx = mgr.getTransaction(); setupTransactions(cache, tx); SamplePojo pojo = new SamplePojo(21, "test"); cache.put("/one/two", "key1", pojo); //overwrite the map we just put in SamplePojo pojo2 = new SamplePojo(21, "test"); cache.put("/one/two", "key1", pojo2); assertEquals(null, dummy.getCalledCommand()); TransactionTable table = cache.getTransactionTable(); GlobalTransaction gtx = table.get(tx); OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table.get(gtx); TransactionWorkspace workspace = entry.getTransactionWorkSpace(); mgr.commit(); //assert what should be the results of our call assertEquals(3, workspace.getNodes().size()); assertNotNull(workspace.getNode(Fqn.fromString("/one/two"))); assertEquals(pojo2, workspace.getNode(Fqn.fromString("/one/two")).get("key1")); assertTrue(entry.getLocks().isEmpty()); assertEquals(2, entry.getModifications().size()); assertTrue(!cache.exists("/one/two")); assertEquals(null, dummy.getCalledCommand()); } public void testTransactionKeyValOverwriteNullMethod() throws Exception { mgr.begin(); Transaction tx = mgr.getTransaction(); setupTransactions(cache, tx); SamplePojo pojo = new SamplePojo(21, "test"); cache.put("/one/two", "key1", pojo); cache.put("/one/two", "key1", null); assertEquals(null, dummy.getCalledCommand()); TransactionTable table = cache.getTransactionTable(); GlobalTransaction gtx = table.get(tx); OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table.get(gtx); TransactionWorkspace workspace = entry.getTransactionWorkSpace(); mgr.commit(); //assert what should be the results of our call assertEquals(3, workspace.getNodes().size()); assertNotNull(workspace.getNode(Fqn.fromString("/one/two"))); assertEquals(null, workspace.getNode(Fqn.fromString("/one/two")).get("key1")); assertTrue(entry.getLocks().isEmpty()); assertEquals(2, entry.getModifications().size()); assertTrue(!cache.exists("/one/two")); assertEquals(null, dummy.getCalledCommand()); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/optimistic/HasChildTest.java0000644000175000017500000000360010735006726030007 0ustar twernertwernerpackage org.jboss.cache.optimistic; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import static org.testng.AssertJUnit.assertFalse; import static org.testng.AssertJUnit.assertTrue; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.Transaction; import javax.transaction.TransactionManager; /** * Tests the hasChild() API * * @author Manik Surtani * @since 2.0.0 */ @Test(groups = "functional") public class HasChildTest extends AbstractOptimisticTestCase { private CacheSPI cache; private TransactionManager txMgr; private Fqn f = Fqn.fromString("/a"); @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { cache = createCache(); txMgr = cache.getTransactionManager(); } @AfterMethod(alwaysRun = true) public void tearDown() { super.tearDown(); destroyCache(cache); } public void testExists() throws Exception { cache.put(f, "k", "v"); assertTrue(cache.getRoot().hasChild(f)); cache.removeNode(f); assertFalse(cache.getRoot().hasChild(f)); txMgr.begin(); cache.put(f, "k", "v"); assertTrue(cache.getRoot().hasChild(f)); Transaction t = txMgr.suspend(); assertFalse(cache.getRoot().hasChild(f)); txMgr.resume(t); assertTrue(cache.getRoot().hasChild(f)); txMgr.commit(); assertTrue(cache.getRoot().hasChild(f)); txMgr.begin(); assertTrue(cache.getRoot().hasChild(f)); cache.removeNode(f); assertFalse(cache.getRoot().hasChild(f)); t = txMgr.suspend(); assertTrue(cache.getRoot().hasChild(f)); txMgr.resume(t); assertFalse(cache.getRoot().hasChild(f)); txMgr.commit(); assertFalse(cache.getRoot().hasChild(f)); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/optimistic/CacheTest.java0000644000175000017500000003304411021017606027324 0ustar twernertwerner/* * Created on 17-Feb-2005 * */ package org.jboss.cache.optimistic; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.NodeSPI; import org.jboss.cache.VersionedNode; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.commands.ReversibleCommand; import org.jboss.cache.commands.tx.CommitCommand; import org.jboss.cache.commands.tx.OptimisticPrepareCommand; import org.jboss.cache.commands.tx.RollbackCommand; import org.jboss.cache.config.Configuration; import org.jboss.cache.invocation.NodeInvocationDelegate; import org.jboss.cache.transaction.GlobalTransaction; import org.jboss.cache.transaction.OptimisticTransactionEntry; import org.jboss.cache.transaction.TransactionTable; import org.jgroups.Address; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.RollbackException; import javax.transaction.Transaction; import javax.transaction.TransactionManager; import java.util.List; import java.util.Map; import java.util.concurrent.CountDownLatch; @Test(groups = "functional") public class CacheTest extends AbstractOptimisticTestCase { Log log = LogFactory.getLog(CacheTest.class); private CacheSPI c; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { c = createCache(); } @AfterMethod(alwaysRun = true) public void tearDown() { super.tearDown(); if (c != null) destroyCache(c); c = null; } public void testRoot() { NodeSPI node = c.getRoot(); assert ((NodeInvocationDelegate) node).getDelegationTarget() instanceof VersionedNode; } public void testExplicitTxFailure() throws Exception { // explicit. TransactionManager mgr = c.getTransactionManager(); try { mgr.begin(); c.put("/a", "k", "v"); Transaction t = mgr.suspend(); c.put("/a", "k2", "v2"); mgr.resume(t); mgr.commit(); assertTrue("Expecting a rollback exception!", false); } catch (RollbackException re) { assertTrue("Expecting a rollback exception!", true); } } public void testImplicitTxFailure() throws Exception { // implicit (much harder to orchestrate... int numThreads = 100; ExceptionThread thread[] = new ExceptionThread[numThreads]; final CountDownLatch latch = new CountDownLatch(1); for (int i = 0; i < numThreads; i++) { thread[i] = new ExceptionThread() { public void run() { try { latch.await(); for (int i=0;i<5; i++) { c.put("/a", "k", "v"); } } catch (Exception e) { setException(e); } } }; } for (int i = 0; i < numThreads; i++) thread[i].start(); latch.countDown(); for (int i = 0; i < numThreads; i++) thread[i].join(); // test exceptions. Expecting at least one exception Exception e; int exceptionCount = 0; for (int i = 0; i < numThreads; i++) { if ((e = thread[i].getException()) != null) { assertFalse("Should never see a RollbackException - instead, expecting the CAUSE of the rollback.", e instanceof RollbackException); exceptionCount++; } } assertTrue("Expecting at least ONE concurrent write exception!!", exceptionCount > 0); } public void testLocalTransaction() throws Exception { MockInterceptor dummy = new MockInterceptor(); setAlteredInterceptorChain(dummy, c); TransactionManager mgr = c.getConfiguration().getRuntimeConfig().getTransactionManager(); assertNull(mgr.getTransaction()); mgr.begin(); assertEquals(0, c.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, c.getTransactionTable().getNumLocalTransactions()); c.put("/one/two", "key1", "value"); mgr.commit(); assertNull(mgr.getTransaction()); assertEquals(0, c.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, c.getTransactionTable().getNumLocalTransactions()); //make sure all calls were done in right order List calls = dummy.getAllCalled(); assertEquals(OptimisticPrepareCommand.class, calls.get(0)); assertEquals(CommitCommand.class, calls.get(1)); } public void testRollbackTransaction() throws Exception { destroyCache(c); c = createCacheWithListener(); MockInterceptor dummy = new MockInterceptor(); setAlteredInterceptorChain(dummy, c); TransactionManager mgr = c.getConfiguration().getRuntimeConfig().getTransactionManager(); assertNull(mgr.getTransaction()); assertEquals(0, c.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, c.getTransactionTable().getNumLocalTransactions()); mgr.begin(); c.put("/one/two", "key1", "value"); mgr.rollback(); assertNull(mgr.getTransaction()); assertEquals(0, c.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, c.getTransactionTable().getNumLocalTransactions()); //make sure all calls were done in right order List calls = dummy.getAllCalled(); assertEquals(1, calls.size()); assertEquals(RollbackCommand.class, calls.get(0)); } public void testRemotePrepareTransaction() throws Throwable { destroyCache(c); c = createCacheWithListener(); MockInterceptor dummy = new MockInterceptor(); setAlteredInterceptorChain(dummy, c); TransactionManager mgr = c.getConfiguration().getRuntimeConfig().getTransactionManager(); //start local transaction mgr.begin(); Transaction tx = mgr.getTransaction(); //this sets c.getCurrentTransaction(tx, true); c.put("/one/two", "key1", "value"); GlobalTransaction gtx = c.getCurrentTransaction(tx, true); TransactionTable table = c.getTransactionTable(); OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table.get(gtx); assertNotNull(mgr.getTransaction()); ReversibleCommand command = entry.getModifications().get(0); mgr.commit(); GlobalTransaction remoteGtx = new GlobalTransaction(); remoteGtx.setAddress(new DummyAddress()); //hack the method call to make it have the remote globalTransaction command.setGlobalTransaction(remoteGtx); //call our remote method List cacheCommands = injectDataVersion(entry.getModifications()); OptimisticPrepareCommand prepareCommand = new OptimisticPrepareCommand(remoteGtx, cacheCommands, (Map) null, (Address) remoteGtx.getAddress(), false); TestingUtil.replicateCommand(c, prepareCommand); //our thread should be null assertNull(mgr.getTransaction()); // there should be a registration for the remote globalTransaction assertNotNull(table.get(remoteGtx)); assertNotNull(table.getLocalTransaction(remoteGtx)); //assert that this is populated // assertEquals(1, table.get(remoteGtx).getModifications().size()); //assert that the remote prepare has populated the local workspace assertEquals(3, entry.getTransactionWorkSpace().getNodes().size()); List calls = dummy.getAllCalled(); assertEquals(OptimisticPrepareCommand.class, calls.get(2)); assertEquals(1, c.getTransactionTable().getNumGlobalTransactions()); assertEquals(1, c.getTransactionTable().getNumLocalTransactions()); } public void testRemoteCacheBroadcast() throws Exception { destroyCache(c); CacheSPI cache = createReplicatedCache(Configuration.CacheMode.REPL_SYNC); CacheSPI cache2 = createReplicatedCache(Configuration.CacheMode.REPL_SYNC); assertEquals(2, cache.getMembers().size()); assertEquals(2, cache2.getMembers().size()); TransactionManager mgr = cache.getConfiguration().getRuntimeConfig().getTransactionManager(); //start local transaction mgr.begin(); //this sets cache.put("/one/two", "key1", "value"); //GlobalTransaction globalTransaction = cache.getCurrentTransaction(tx); assertNotNull(mgr.getTransaction()); mgr.commit(); assertNull(mgr.getTransaction()); //assert that the local cache is in the right state assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache.getTransactionTable().getNumLocalTransactions()); assertTrue(cache.exists(Fqn.fromString("/one/two"))); assertTrue(cache.exists(Fqn.fromString("/one"))); assertEquals("value", cache.get(Fqn.fromString("/one/two"), "key1")); assertEquals(0, cache2.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache2.getTransactionTable().getNumLocalTransactions()); assertTrue(cache2.exists(Fqn.fromString("/one/two"))); assertTrue(cache2.exists(Fqn.fromString("/one"))); assertEquals("value", cache2.get(Fqn.fromString("/one/two"), "key1")); destroyCache(cache); destroyCache(cache2); } public void testTwoWayRemoteCacheBroadcast() throws Exception { destroyCache(c); CacheSPI cache = createReplicatedCache(Configuration.CacheMode.REPL_SYNC); CacheSPI cache2 = createReplicatedCache(Configuration.CacheMode.REPL_SYNC); assertEquals(2, cache.getMembers().size()); assertEquals(2, cache2.getMembers().size()); TransactionManager mgr = cache.getConfiguration().getRuntimeConfig().getTransactionManager(); //start local transaction mgr.begin(); Transaction tx = mgr.getTransaction(); //this sets cache.getCurrentTransaction(tx, true); cache.put("/one/two", "key1", "value"); assertNotNull(mgr.getTransaction()); mgr.commit(); assertNull(mgr.getTransaction()); //assert that the local cache is in the right state assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache.getTransactionTable().getNumLocalTransactions()); assertTrue(cache.exists(Fqn.fromString("/one/two"))); assertTrue(cache.exists(Fqn.fromString("/one"))); assertEquals("value", cache.get(Fqn.fromString("/one/two"), "key1")); assertEquals(0, cache2.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache2.getTransactionTable().getNumLocalTransactions()); assertTrue(cache2.exists(Fqn.fromString("/one/two"))); assertTrue(cache2.exists(Fqn.fromString("/one"))); assertEquals("value", cache2.get(Fqn.fromString("/one/two"), "key1")); destroyCache(cache); destroyCache(cache2); } public void testConcurrentNodeRemoval() throws Exception { c.put(fqn, "key", "value"); // now start a tx to change the value in fqn TransactionManager mgr = c.getTransactionManager(); mgr.begin(); c.put(fqn, "key2", "value2"); Transaction tx = mgr.suspend(); // now remove the original node... c.removeNode(fqn); mgr.resume(tx); // now try and commit this - this should fail. boolean ok = false; try { mgr.commit(); } catch (RollbackException rbe) { ok = true; } assertTrue("Concurrent mod should result in a rollback", ok); // now assert that the node has in fact been removed. assertTrue("The node should have been removed!", !c.exists(fqn)); } public void testConcurrentNodeModification() throws Exception { c.put(fqn, "key", "value"); // now start a tx to change the value in fqn TransactionManager mgr = c.getTransactionManager(); mgr.begin(); c.put(fqn, "key2", "value2"); Transaction tx = mgr.suspend(); // now change the original node... c.put(fqn, "key3", "value3"); mgr.resume(tx); // now try and commit this - this should fail. boolean ok = false; try { mgr.commit(); } catch (RollbackException rbe) { ok = true; } assertTrue("Concurrent mod should result in a rollback", ok); } public void testRemoveAndCreate() throws Exception { c = createCache(); c.put(fqn, "key", "value"); TransactionManager tm = c.getTransactionManager(); tm.begin(); c.put(fqn, "test", "test"); tm.commit(); assertEquals(1, c.getRoot().getChildrenNames().size()); tm.begin(); c.removeNode(fqn); c.put(fqn, "test", "test"); tm.commit(); assertEquals(1, c.getRoot().getChildrenNames().size()); } public void testRemoveChildAfterRemoveParent() throws Exception { c = createCache(); TransactionManager tm = c.getTransactionManager(); c.put(Fqn.fromString("/a/b"), "k", "v"); tm.begin(); c.removeNode(Fqn.fromString("/a")); c.removeNode(Fqn.fromString("/a/b")); tm.commit(); destroyCache(c); } public void testAddChildAfterRemoveParent() throws Exception { c = createCache(); TransactionManager tm = c.getTransactionManager(); c.put(Fqn.fromString("/a/b"), "k", "v"); tm.begin(); c.removeNode(Fqn.fromString("/a")); c.put(Fqn.fromString("/a/b"), "k", "v"); tm.commit(); destroyCache(c); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/optimistic/NodeInterceptorPutMapTest.java0000755000175000017500000001273711017455042032572 0ustar twernertwernerpackage org.jboss.cache.optimistic; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.interceptors.OptimisticCreateIfNotExistsInterceptor; import org.jboss.cache.interceptors.OptimisticNodeInterceptor; import org.jboss.cache.interceptors.base.CommandInterceptor; import org.jboss.cache.loader.SamplePojo; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.transaction.GlobalTransaction; import org.jboss.cache.transaction.OptimisticTransactionEntry; import org.jboss.cache.transaction.TransactionTable; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.Transaction; import javax.transaction.TransactionManager; import java.util.HashMap; import java.util.Map; /** * @author xenephon */ @SuppressWarnings("unchecked") @Test(groups = "functional") public class NodeInterceptorPutMapTest extends AbstractOptimisticTestCase { private CacheSPI cache; private TransactionManager mgr; private TestListener listener; private MockInterceptor dummy; @BeforeMethod public void setUp() throws Exception { listener = new TestListener(); cache = createCacheWithListener(listener); CommandInterceptor interceptor = TestingUtil.findInterceptor(cache, OptimisticCreateIfNotExistsInterceptor.class); CommandInterceptor nodeInterceptor = TestingUtil.findInterceptor(cache, OptimisticNodeInterceptor.class); dummy = new MockInterceptor(); interceptor.setNext(nodeInterceptor); nodeInterceptor.setNext(dummy); TestingUtil.replaceInterceptorChain(cache, interceptor); mgr = cache.getTransactionManager(); } @AfterMethod public void tearDown() { TestingUtil.killCaches(cache); } public void testTransactionPutDataMethod() throws Exception { mgr.begin(); Transaction tx = mgr.getTransaction(); setupTransactions(cache, tx); SamplePojo pojo = new SamplePojo(21, "test"); Map temp = new HashMap(); temp.put("key1", pojo); cache.put("/one/two", temp); assertEquals(null, dummy.getCalledCommand()); TransactionTable table = cache.getTransactionTable(); GlobalTransaction gtx = table.get(tx); OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table.get(gtx); TransactionWorkspace workspace = entry.getTransactionWorkSpace(); mgr.commit(); //assert what should be the results of our call assertEquals(3, workspace.getNodes().size()); assertNotNull(workspace.getNode(Fqn.fromString("/one/two"))); assertEquals(pojo, workspace.getNode(Fqn.fromString("/one/two")).get("key1")); assertEquals(1, workspace.getNode(Fqn.fromString("/one/two")).getMergedData().size()); assertTrue(entry.getLocks().isEmpty()); assertEquals(1, entry.getModifications().size()); assertTrue(!cache.exists("/one/two")); assertEquals(null, dummy.getCalledCommand()); } public void testTransactionPutLocalOverwriteDataMethod() throws Exception { mgr.begin(); Transaction tx = mgr.getTransaction(); setupTransactions(cache, tx); SamplePojo pojo = new SamplePojo(21, "test"); Map temp = new HashMap(); temp.put("key1", pojo); cache.put("/one/two", temp); //overwrite the map we just put in SamplePojo pojo2 = new SamplePojo(22, "test"); Map temp2 = new HashMap(); temp2.put("key1", pojo2); cache.put("/one/two", temp2); assertEquals(null, dummy.getCalledCommand()); TransactionTable table = cache.getTransactionTable(); GlobalTransaction gtx = table.get(tx); OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table.get(gtx); TransactionWorkspace workspace = entry.getTransactionWorkSpace(); mgr.commit(); //assert what should be the results of our call assertEquals(3, workspace.getNodes().size()); assertNotNull(workspace.getNode(Fqn.fromString("/one/two"))); assertEquals(pojo2, workspace.getNode(Fqn.fromString("/one/two")).get("key1")); assertTrue(entry.getLocks().isEmpty()); assertEquals(2, entry.getModifications().size()); assertTrue(!cache.exists("/one/two")); assertEquals(null, dummy.getCalledCommand()); } public void testTransactionPutLocalEmptyMethod() throws Exception { Fqn f = Fqn.fromString("/one/two"); mgr.begin(); Transaction tx = mgr.getTransaction(); setupTransactions(cache, tx); SamplePojo pojo = new SamplePojo(21, "test"); Map temp = new HashMap(); temp.put("key1", pojo); cache.put(f, temp); Map temp2 = new HashMap(); cache.getNode(f).replaceAll(temp2); assertEquals(null, dummy.getCalledCommand()); TransactionTable table = cache.getTransactionTable(); GlobalTransaction gtx = table.get(tx); OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table.get(gtx); TransactionWorkspace workspace = entry.getTransactionWorkSpace(); mgr.commit(); //assert what should be the results of our call assertEquals(3, workspace.getNodes().size()); assertNotNull(workspace.getNode(f)); assertEquals(null, workspace.getNode(f).get("key1")); assertTrue(entry.getLocks().isEmpty()); assertEquals(3, entry.getModifications().size()); assertTrue(!cache.exists(f)); assertEquals(null, dummy.getCalledCommand()); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/optimistic/ThreadedCacheAccessTest.java0000644000175000017500000000567510767534221032135 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.optimistic; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.CacheSPI; import org.testng.annotations.AfterMethod; import org.testng.annotations.Test; import javax.transaction.TransactionManager; /** * Tests multiple thread access on opt locked cache * * @author Manik Surtani (manik@jboss.org) */ @Test(groups = "functional") public class ThreadedCacheAccessTest extends AbstractOptimisticTestCase { private static final Log log = LogFactory.getLog(ThreadedCacheAccessTest.class); // 5 concurrent threads. private final int numThreads = 10; // how many times each thread loops private final int numLoopsPerThread = 1000; // write frequency. 1 in writeFrequency loops will do a put(). private final int writeFrequency = 5; private final String key = "key", value = "value"; private CacheSPI cache; private WorkerThread[] threads; @Override @AfterMethod(alwaysRun = true) public void tearDown() { super.tearDown(); destroyCache(cache); } public void testThreadedMostlyReads() throws Exception { cache = createCache(); // write some stuff into the cache. cache.put(fqn, key, value); threads = new WorkerThread[numThreads]; for (int i = 0; i < numThreads; i++) { threads[i] = new WorkerThread(); threads[i].start(); } for (WorkerThread t : threads) { t.join(); if (t.e != null) throw t.e; } } public class WorkerThread extends Thread { Exception e = null; public WorkerThread() { setDaemon(true); } @Override public void run() { log.debug(getName() + " starting up ... "); for (int j = 0; j < numLoopsPerThread; j++) { TransactionManager tm = cache.getTransactionManager(); boolean write = j % writeFrequency == 0; try { tm.begin(); // read something from the cache - it should be in it's own thread. cache.get(fqn, key); if (write) { cache.put(fqn, key, value + j); } tm.commit(); } catch (Exception e) { if (!write) // writes could fail from a perfectly acceptable data versioning exception { this.e = e; } try { if (tm.getTransaction() != null) tm.rollback(); } catch (Exception e2) { log.error("Rollback failed!", e2); } if (!write) break; } } } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/optimistic/DataVersionTransferTest.java0000644000175000017500000001664011017455042032255 0ustar twernertwernerpackage org.jboss.cache.optimistic; import org.jboss.cache.Cache; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.NodeSPI; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.config.Configuration; import org.jboss.cache.transaction.DummyTransactionManagerLookup; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.ArrayList; import java.util.List; /** * Tests whether data versions are transferred along with state * * @author Manik Surtani * @since 2.1.0 */ @Test(groups = {"functional"}) public class DataVersionTransferTest { private List> caches = new ArrayList>(2); @BeforeMethod public void setUp() { caches.add(new DefaultCacheFactory().createCache(false)); caches.get(0).getConfiguration().setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName()); caches.get(0).getConfiguration().setNodeLockingScheme(Configuration.NodeLockingScheme.OPTIMISTIC); caches.get(0).getConfiguration().setCacheMode(Configuration.CacheMode.REPL_SYNC); caches.get(0).start(); caches.add(new DefaultCacheFactory().createCache(false)); caches.get(1).getConfiguration().setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName()); caches.get(1).getConfiguration().setNodeLockingScheme(Configuration.NodeLockingScheme.OPTIMISTIC); caches.get(1).getConfiguration().setCacheMode(Configuration.CacheMode.REPL_SYNC); } @AfterMethod public void tearDown() { if (caches != null) { for (Cache cache : caches) { try { cache.getConfiguration().getRuntimeConfig().getTransactionManager().rollback(); } catch (Exception e) { // do nothing? } cache.stop(); } } } public void testStateTransferDefaultVersions() throws Exception { Fqn f = Fqn.fromString("/one/two/three"); caches.get(0).put(f, "k", "v"); caches.get(0).put(f, "k1", "v1"); caches.get(0).remove(f, "k1"); NodeSPI n = (NodeSPI) caches.get(0).getRoot().getChild(f); DataVersion dv = n.getVersion(); assert dv instanceof DefaultDataVersion : "Should be an instance of DefaultDataVersion"; assert ((DefaultDataVersion) dv).getRawVersion() == 3 : "Should have accurate data version"; // now start next cache instance caches.get(1).start(); TestingUtil.blockUntilViewsReceived(10000, caches.get(0), caches.get(1)); assert caches.get(1).get(f, "k").equals("v") : "Value should have transferred"; n = (NodeSPI) caches.get(1).getRoot().getChild(f); dv = n.getVersion(); assert dv instanceof DefaultDataVersion : "Should be an instance of DefaultDataVersion"; assert ((DefaultDataVersion) dv).getRawVersion() == 3 : "Version should have transferred"; // make sure leakage doesn't occur into data map assert n.getData().size() == 1; } public void testStateTransferCustomVersion() throws Exception { Fqn f = Fqn.fromString("/one/two/three"); caches.get(0).getInvocationContext().getOptionOverrides().setDataVersion(new CharVersion('A')); caches.get(0).put(f, "k", "v"); caches.get(0).getInvocationContext().getOptionOverrides().setDataVersion(new CharVersion('B')); caches.get(0).put(f, "k1", "v1"); caches.get(0).getInvocationContext().getOptionOverrides().setDataVersion(new CharVersion('C')); caches.get(0).remove(f, "k1"); NodeSPI n = (NodeSPI) caches.get(0).getRoot().getChild(f); DataVersion dv = n.getVersion(); assert dv instanceof CharVersion : "Should be an instance of CharVersion"; assert ((CharVersion) dv).version == 'C' : "Should have accurate data version"; // now start next cache instance caches.get(1).start(); TestingUtil.blockUntilViewsReceived(10000, caches.get(0), caches.get(1)); assert caches.get(1).get(f, "k").equals("v") : "Value should have transferred"; n = (NodeSPI) caches.get(1).getRoot().getChild(f); dv = n.getVersion(); assert dv instanceof CharVersion : "Should be an instance of CharVersion"; assert ((CharVersion) dv).version == 'C' : "Version should have transferred"; // make sure leakage doesn't occur into data map assert n.getData().size() == 1; } public void testStateTransferIntermediateNodeDefaultVersions() throws Exception { Fqn f = Fqn.fromString("/one/two/three"); Fqn intermediate = f.getParent(); caches.get(0).put(f, "k", "v"); caches.get(0).put(intermediate, "k", "v"); NodeSPI n = (NodeSPI) caches.get(0).getRoot().getChild(intermediate); DataVersion dv = n.getVersion(); assert dv instanceof DefaultDataVersion : "Should be an instance of DefaultDataVersion"; assert ((DefaultDataVersion) dv).getRawVersion() == 1 : "Should have accurate data version"; // now start next cache instance caches.get(1).start(); TestingUtil.blockUntilViewsReceived(10000, caches.get(0), caches.get(1)); assert caches.get(1).get(intermediate, "k").equals("v") : "Value should have transferred"; n = (NodeSPI) caches.get(0).getRoot().getChild(intermediate); dv = n.getVersion(); assert dv instanceof DefaultDataVersion : "Should be an instance of DefaultDataVersion"; assert ((DefaultDataVersion) dv).getRawVersion() == 1 : "Should have accurate data version"; // make sure leakage doesn't occur into data map assert n.getData().size() == 1; } public void testStateTransferIntermediateNodeCustomVersion() throws Exception { Fqn f = Fqn.fromString("/one/two/three"); Fqn intermediate = f.getParent(); caches.get(0).put(f, "k", "v"); caches.get(0).getInvocationContext().getOptionOverrides().setDataVersion(new CharVersion('X')); caches.get(0).put(intermediate, "k", "v"); NodeSPI n = (NodeSPI) caches.get(0).getRoot().getChild(intermediate); DataVersion dv = n.getVersion(); assert dv instanceof CharVersion : "Should be an instance of CharVersion"; assert ((CharVersion) dv).version == 'X' : "Should have accurate data version"; // now start next cache instance caches.get(1).start(); TestingUtil.blockUntilViewsReceived(10000, caches.get(0), caches.get(1)); assert caches.get(1).get(intermediate, "k").equals("v") : "Value should have transferred"; n = (NodeSPI) caches.get(0).getRoot().getChild(intermediate); dv = n.getVersion(); assert dv instanceof CharVersion : "Should be an instance of CharVersion"; assert ((CharVersion) dv).version == 'X' : "Should have accurate data version"; // make sure leakage doesn't occur into data map assert n.getData().size() == 1; } public static class CharVersion implements DataVersion { private char version = 'A'; public CharVersion(char version) { this.version = version; } public boolean newerThan(DataVersion other) { if (other instanceof CharVersion) { CharVersion otherVersion = (CharVersion) other; return version > otherVersion.version; } else { return true; } } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/optimistic/ComparatorTest.java0000755000175000017500000001047410777521744030461 0ustar twernertwernerpackage org.jboss.cache.optimistic; import org.jboss.cache.Fqn; import org.jboss.cache.FqnComparator; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertTrue; import org.testng.annotations.Test; import java.util.ArrayList; import java.util.List; /** * Tests {@link FqnComparator}. * * @author xenephon */ @Test(groups = {"functional"}) public class ComparatorTest { FqnComparator comp = new FqnComparator(); public void testSingleCompare() { Fqn fqn1 = Fqn.fromString("one"); Fqn fqn2 = Fqn.fromString("two"); assertTrue(comp.compare(fqn1, fqn2) < 0); assertTrue(comp.compare(fqn2, fqn1) > 0); assertTrue(comp.compare(fqn1, fqn1) == 0); assertTrue(comp.compare(fqn2, fqn2) == 0); } public void testNullCompare() { Fqn fqn1 = Fqn.fromList(new ArrayList()); Fqn fqn2 = Fqn.fromList(new ArrayList()); assertTrue(comp.compare(fqn1, fqn2) == 0); assertTrue(comp.compare(fqn2, fqn1) == 0); assertTrue(comp.compare(fqn1, fqn1) == 0); assertTrue(comp.compare(fqn2, fqn2) == 0); } public void testOneNullCompare() { Fqn fqn1 = Fqn.fromList(new ArrayList()); List temp = new ArrayList(); temp.add("one"); Fqn fqn2 = Fqn.fromList(temp); assertTrue(comp.compare(fqn1, fqn2) < 0); assertTrue(comp.compare(fqn2, fqn1) > 0); } public void testNotComparableCompare() { Fqn fqn1 = Fqn.fromList(new ArrayList()); List temp = new ArrayList(); temp.add("one"); Fqn fqn2 = Fqn.fromList(temp); assertTrue(comp.compare(fqn1, fqn2) < 0); assertTrue(comp.compare(fqn2, fqn1) > 0); } public void testMultiChildCompare() { Fqn fqn1 = Fqn.fromString("/one/two"); Fqn fqn2 = Fqn.fromString("/one/two/three"); assertTrue(comp.compare(fqn1, fqn2) < 0); assertTrue(comp.compare(fqn2, fqn1) > 0); assertTrue(comp.compare(fqn2, fqn2) == 0); assertTrue(comp.compare(fqn1, fqn1) == 0); } public void testMultiNotChildCompare() { Fqn fqn1 = Fqn.fromString("/one/two"); Fqn fqn2 = Fqn.fromString("/three/four"); assertTrue(comp.compare(fqn1, fqn2) < 0); assertTrue(comp.compare(fqn2, fqn1) > 0); assertTrue(comp.compare(fqn2, fqn2) == 0); assertTrue(comp.compare(fqn1, fqn1) == 0); } public void testPartialMultiNotChildCompare() { Fqn fqn1 = Fqn.fromString("/one/two"); Fqn fqn2 = Fqn.fromString("/three"); assertTrue(comp.compare(fqn1, fqn2) < 0); assertTrue(comp.compare(fqn2, fqn1) > 0); assertTrue(comp.compare(fqn2, fqn2) == 0); assertTrue(comp.compare(fqn1, fqn1) == 0); } public void testEqualsMultidCompare() { Fqn fqn1 = Fqn.fromString("/one/two"); Fqn fqn2 = Fqn.fromString("/one/two"); assertTrue(comp.compare(fqn1, fqn2) == 0); assertTrue(comp.compare(fqn2, fqn1) == 0); assertTrue(comp.compare(fqn2, fqn2) == 0); assertTrue(comp.compare(fqn1, fqn1) == 0); } public void testStringIntMultidCompare() { Fqn fqn1 = Fqn.fromString("/one/two"); List temp = new ArrayList(); temp.add(1234); Fqn fqn2 = Fqn.fromList(temp); assertTrue(comp.compare(fqn1, fqn2) > 0); assertTrue(comp.compare(fqn2, fqn1) < 0); assertTrue(comp.compare(fqn2, fqn2) == 0); assertTrue(comp.compare(fqn1, fqn1) == 0); } public void testOrdinaryObjectCompare() { Fqn fqn1 = Fqn.fromElements(new XYZ(), new ABC()); Fqn fqn2 = Fqn.fromElements("XYZ", "ABC"); Fqn fqn3 = Fqn.fromElements("XYZ", new ABC()); Fqn fqn4 = Fqn.fromElements("XYZ", new XYZ()); assertEquals(0, comp.compare(fqn1, fqn2)); assertEquals(0, comp.compare(fqn1, fqn3)); assertEquals(0, comp.compare(fqn2, fqn3)); assertEquals(true, comp.compare(fqn1, fqn4) < 0); assertEquals(true, comp.compare(fqn4, fqn1) > 0); } private static class XYZ { @Override public String toString() { return "XYZ"; } } private static class ABC { @Override public String toString() { return "ABC"; } } } ././@LongLink0000000000000000000000000000014600000000000011566 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/optimistic/NodeInterceptorTransactionTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/optimistic/NodeInterceptorTransactionTest.jav0000755000175000017500000000425011017455042033477 0ustar twernertwerner/* * Created on 17-Feb-2005 * * * */ package org.jboss.cache.optimistic; import org.jboss.cache.CacheSPI; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.interceptors.OptimisticCreateIfNotExistsInterceptor; import org.jboss.cache.interceptors.OptimisticNodeInterceptor; import org.jboss.cache.interceptors.base.CommandInterceptor; import static org.testng.AssertJUnit.*; import org.testng.annotations.Test; /** * @author xenephon */ @SuppressWarnings("unchecked") @Test(groups = "functional") public class NodeInterceptorTransactionTest extends AbstractOptimisticTestCase { public void testNoTransactionCRUDMethod() throws Exception { TestListener listener = new TestListener(); final CacheSPI cache = createCacheWithListener(listener); CommandInterceptor interceptor = new OptimisticCreateIfNotExistsInterceptor(); CommandInterceptor nodeInterceptor = new OptimisticNodeInterceptor(); MockInterceptor dummy = new MockInterceptor(); interceptor.setNext(nodeInterceptor); nodeInterceptor.setNext(dummy); TestingUtil.replaceInterceptorChain(cache, interceptor); try { cache.put("/one/two", "key1", new Object()); fail(); } catch (Throwable t) { assertTrue(true); } assertEquals(null, dummy.getCalledCommand()); cache.stop(); } public void testNoTransactionGetMethod() throws Exception { TestListener listener = new TestListener(); final CacheSPI cache = createCacheWithListener(listener); CommandInterceptor interceptor = new OptimisticCreateIfNotExistsInterceptor(); CommandInterceptor nodeInterceptor = new OptimisticNodeInterceptor(); MockInterceptor dummy = new MockInterceptor(); interceptor.setNext(nodeInterceptor); nodeInterceptor.setNext(dummy); TestingUtil.replaceInterceptorChain(cache, interceptor); boolean fail = false; try { assertEquals(null, cache.get("/one/two", "key1")); } catch (Exception e) { fail = true; } assertTrue(fail); assertEquals(null, dummy.getCalledCommand()); cache.stop(); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/optimistic/ChildMapLazyLoadingTest.java0000644000175000017500000001772210732232105032146 0ustar twernertwernerpackage org.jboss.cache.optimistic; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.Node; import org.jboss.cache.config.Configuration; import org.jboss.cache.transaction.DummyTransactionManagerLookup; import org.jboss.cache.transaction.GlobalTransaction; import org.jboss.cache.transaction.OptimisticTransactionEntry; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.Transaction; import java.util.HashMap; import java.util.Map; /** * Tests that children maps in workspace nodes are only loaded lazily, when a move() or getChildrenNames() * * @author Manik Surtani * @since 2.1.0 */ @Test(groups = {"functional"}) public class ChildMapLazyLoadingTest { private CacheSPI cache; private Fqn parent = Fqn.fromString("/parent"); private Fqn child = Fqn.fromString("/parent/child"); @BeforeMethod public void setUp() { cache = (CacheSPI) new DefaultCacheFactory().createCache(false); cache.getConfiguration().setNodeLockingScheme(Configuration.NodeLockingScheme.OPTIMISTIC); cache.getConfiguration().setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName()); cache.start(); cache.put(parent, "k", "v"); cache.put(child, "k", "v"); } @AfterMethod public void tearDown() { if (cache != null) { try { cache.getTransactionManager().rollback(); } catch (Exception e) { } cache.stop(); } } public void testLazyLoadingOnCacheGet() throws Exception { cache.getTransactionManager().begin(); assert cache.get(parent, "k").equals("v") : "Should retrieve value"; WorkspaceNode n = getWorkspaceNode(parent); assert !n.isChildrenLoaded() : "Should not have loaded children"; cache.getTransactionManager().commit(); } public void testLazyLoadingOnCachePut() throws Exception { cache.getTransactionManager().begin(); cache.put(parent, "k2", "v2"); assert cache.get(parent, "k2").equals("v2") : "Should retrieve value"; WorkspaceNode n = getWorkspaceNode(parent); assert !n.isChildrenLoaded() : "Should not have loaded children"; cache.getTransactionManager().commit(); } public void testLazyLoadingOnCachePutData() throws Exception { cache.getTransactionManager().begin(); Map data = new HashMap(); data.put("k2", "v2"); cache.put(parent, data); assert cache.get(parent, "k2").equals("v2") : "Should retrieve value"; WorkspaceNode n = getWorkspaceNode(parent); assert !n.isChildrenLoaded() : "Should not have loaded children"; cache.getTransactionManager().commit(); } public void testLazyLoadingOnCacheRemove() throws Exception { cache.getTransactionManager().begin(); cache.remove(parent, "k"); assert cache.get(parent, "k") == null : "Data should be removed"; WorkspaceNode n = getWorkspaceNode(parent); assert !n.isChildrenLoaded() : "Should not have loaded children"; cache.getTransactionManager().commit(); } public void testLazyLoadingOnCacheRemoveNode() throws Exception { cache.getTransactionManager().begin(); cache.removeNode(parent); assert !cache.getRoot().hasChild(parent) : "Node should be removed"; WorkspaceNode n = getWorkspaceNode(parent); assert !n.isChildrenLoaded() : "Should not have loaded children"; cache.getTransactionManager().commit(); } public void testLazyLoadingOnCacheMove() throws Exception { Fqn newparentToMoveTo = Fqn.fromString("/newparent"); Fqn newparent = Fqn.fromString("/newparent/parent"); Fqn newchild = Fqn.fromString("/newparent/parent/child"); cache.getTransactionManager().begin(); cache.move(parent, newparentToMoveTo); assert !cache.getRoot().hasChild(parent) : "Node should be removed"; assert !cache.getRoot().hasChild(child) : "Node should be removed"; assert cache.getRoot().hasChild(newparent) : "Node should have moved"; assert cache.getRoot().hasChild(newchild) : "Node should have moved"; assert cache.get(newparent, "k").equals("v") : "Data should have moved too"; WorkspaceNode n = getWorkspaceNode(parent); assert n.isChildrenLoaded() : "Should have loaded children"; cache.getTransactionManager().commit(); } public void testLazyLoadingOnNodeGet() throws Exception { cache.getTransactionManager().begin(); Node node = cache.getRoot().getChild(parent); assert node.getData().size() == 1 : "Node should have data"; WorkspaceNode n = getWorkspaceNode(parent); assert !n.isChildrenLoaded() : "Should not have loaded children"; cache.getTransactionManager().commit(); } public void testLazyLoadingOnNodeRemove() throws Exception { cache.getTransactionManager().begin(); Node node = cache.getRoot().getChild(parent); node.clearData(); assert node.getData().size() == 0 : "Node should have removed data"; WorkspaceNode n = getWorkspaceNode(parent); assert !n.isChildrenLoaded() : "Should not have loaded children"; cache.getTransactionManager().commit(); } public void testLazyLoadingOnNodePut() throws Exception { cache.getTransactionManager().begin(); Node node = cache.getRoot().getChild(parent); node.put("k2", "v2"); assert node.getData().size() == 2 : "Node should have added data"; WorkspaceNode n = getWorkspaceNode(parent); assert !n.isChildrenLoaded() : "Should not have loaded children"; cache.getTransactionManager().commit(); } public void testLazyLoadingOnNodeGetChildrenNames() throws Exception { cache.getTransactionManager().begin(); Node node = cache.getRoot().getChild(parent); assert node.getChildrenNames().size() == 1 : "Node should have 1 child"; WorkspaceNode n = getWorkspaceNode(parent); assert n.isChildrenLoaded() : "Should have loaded children"; cache.getTransactionManager().commit(); } public void testLazyLoadingOnNodeGetChildren() throws Exception { cache.getTransactionManager().begin(); Node node = cache.getRoot().getChild(parent); assert node.getChildren().size() == 1 : "Node should have 1 child"; WorkspaceNode n = getWorkspaceNode(parent); assert n.isChildrenLoaded() : "Should have loaded children"; cache.getTransactionManager().commit(); } public void testLazyLoadingOnNodeAddChild() throws Exception { Fqn newChild = Fqn.fromString("/newchild"); cache.getTransactionManager().begin(); Node node = cache.getRoot().getChild(parent); node.addChild(newChild); assert node.hasChild(newChild) : "Node should have added child"; WorkspaceNode n = getWorkspaceNode(parent); assert !n.isChildrenLoaded() : "Should not have loaded children"; cache.getTransactionManager().commit(); } public void testLazyLoadingOnNodeRemoveChild() throws Exception { cache.getTransactionManager().begin(); Node node = cache.getRoot().getChild(parent); node.removeChild(child.getLastElement()); assert !node.hasChild(child.getLastElement()) : "Node should have removed child"; WorkspaceNode n = getWorkspaceNode(parent); assert !n.isChildrenLoaded() : "Should not have loaded children"; cache.getTransactionManager().commit(); } private WorkspaceNode getWorkspaceNode(Fqn fqn) throws Exception { Transaction tx = cache.getTransactionManager().getTransaction(); GlobalTransaction gtx = cache.getTransactionTable().get(tx); OptimisticTransactionEntry te = (OptimisticTransactionEntry) cache.getTransactionTable().get(gtx); TransactionWorkspace tw = te.getTransactionWorkSpace(); return tw.getNode(fqn); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/optimistic/ConcurrentTransactionTest.java0000644000175000017500000002447511030754015032663 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.optimistic; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.NodeSPI; import org.jboss.cache.commands.VisitableCommand; import org.jboss.cache.commands.tx.OptimisticPrepareCommand; import org.jboss.cache.interceptors.OptimisticCreateIfNotExistsInterceptor; import org.jboss.cache.interceptors.TxInterceptor; import org.jboss.cache.interceptors.base.CommandInterceptor; import org.jboss.cache.util.TestingUtil; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.Transaction; import javax.transaction.TransactionManager; import java.util.LinkedList; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CountDownLatch; /** * @author Manik Surtani (manik@jboss.org) */ @Test(groups = "functional") public class ConcurrentTransactionTest extends AbstractOptimisticTestCase { private CacheSPI cache; private Fqn f = Fqn.fromString("/a/b"); private List exceptions = new CopyOnWriteArrayList(); @BeforeMethod(alwaysRun = true) public void setUp() { try { cache = createCacheUnstarted(); cache.getConfiguration().setUseRegionBasedMarshalling(true); cache.start(); } catch (Exception e) { e.printStackTrace(); } } @AfterMethod(alwaysRun = true) public void tearDown() { if (cache != null) { cache.stop(); cache = null; } } public void testConcurrentTransactions() throws Exception { TransactionManager tm = cache.getConfiguration().getRuntimeConfig().getTransactionManager(); Fqn abcd = Fqn.fromString("/a/b/c/d"); Fqn abce = Fqn.fromString("/a/b/c/e"); Fqn abcf = Fqn.fromString("/a/b/c/f"); Fqn abcg = Fqn.fromString("/a/b/c/g"); Fqn abxy = Fqn.fromString("/a/b/x/y"); cache.put(abcd, key, value); assertEquals(value, cache.get(abcd, key)); tm.begin(); Transaction tx = tm.getTransaction(); cache.put(abxy, key, value); tm.suspend(); // a number of random puts in unrelated sub nodes. cache.put(abcd, key, value + value); cache.put(abce, key, value); cache.put(abcf, key, value); cache.put(abcg, key, value); assertEquals(value + value, cache.get(abcd, key)); assertEquals(value, cache.get(abce, key)); assertEquals(value, cache.get(abcf, key)); assertEquals(value, cache.get(abcg, key)); tm.resume(tx); tm.commit(); assertEquals(value, cache.get(abxy, key)); NodeSPI n = cache.getRoot(); System.out.println(n.getVersion()); } public void testConcurrentCreationTestWithEmptyCache() throws Exception { doConcurrentCreationTest(false); } public void testConcurrentCreationTestWithEmptyCacheActivated() throws Exception { cache.put(Fqn.fromString("/parent"), null); cache.getRegion(Fqn.fromString("/parent"), true).activate(); assertNotNull(cache.peek(Fqn.fromString("/parent"), false)); doConcurrentCreationTest(false); } public void testConcurrentCreationTestWithPopulatedCache() throws Exception { doConcurrentCreationTest(true); } public void testConcurrentReadAndRemove() throws Exception { final List exceptions = new LinkedList(); final CountDownLatch readerLatch = new CountDownLatch(1); final CountDownLatch readerFinishedLatch = new CountDownLatch(1); final Fqn fqn = Fqn.fromString("/parent/child"); cache.put(fqn, "k", "v"); class Reader extends Thread { public void run() { try { cache.getTransactionManager().begin(); cache.get(fqn, "k"); // read readerFinishedLatch.countDown(); readerLatch.await(); // wait cache.getTransactionManager().commit(); } catch (Exception e) { e.printStackTrace(); exceptions.add(e); } } } Thread reader = new Reader(); reader.start(); readerFinishedLatch.await(); cache.removeNode(fqn.getParent()); assertNull(cache.peek(fqn.getParent(), false)); readerLatch.countDown(); reader.join(); assertTrue("Should not have caught any exceptions!!", exceptions.isEmpty()); } public void testConcurrentPutReadAndRemove() throws Exception { final List exceptions = new LinkedList(); final CountDownLatch readerLatch = new CountDownLatch(1); final CountDownLatch readerFinishedLatch = new CountDownLatch(1); final Fqn fqn = Fqn.fromString("/parent/child"); cache.put(fqn, "k", "v"); class Reader extends Thread { public void run() { try { cache.getTransactionManager().begin(); cache.put(Fqn.ROOT, "x", "y"); // a dummy put to ensure that validation occurs cache.get(fqn, "k"); // read readerFinishedLatch.countDown(); readerLatch.await(); // wait cache.getTransactionManager().commit(); } catch (Exception e) { e.printStackTrace(); exceptions.add(e); } } } Thread reader = new Reader(); reader.start(); readerFinishedLatch.await(); cache.removeNode(fqn.getParent()); assertNull(cache.peek(fqn.getParent(), false)); readerLatch.countDown(); reader.join(); assertTrue("Should not have caught any exceptions!!", exceptions.isEmpty()); } private void doConcurrentCreationTest(boolean prepopulateParent) throws Exception { if (prepopulateParent) cache.put(Fqn.fromString("/parent/dummy"), "k", "v"); final List exceptions = new LinkedList(); final CountDownLatch latch = new CountDownLatch(1); class ConcurrentCreator extends Thread { private String name; public ConcurrentCreator(String name) { this.name = name; } public void run() { try { cache.getTransactionManager().begin(); cache.put(Fqn.fromString("/parent/child" + name), "key", "value"); latch.await(); cache.getTransactionManager().commit(); } catch (Exception e) { e.printStackTrace(); exceptions.add(e); } } } Thread one = new ConcurrentCreator("one"); Thread two = new ConcurrentCreator("two"); one.start(); two.start(); latch.countDown(); one.join(); two.join(); assertTrue("Should not have caught any exceptions!!", exceptions.isEmpty()); } public void testConcurrentPut() throws Exception { final String slowThreadName = "SLOW"; final String fastThreadName = "FAST"; CommandInterceptor slowdownInterceptor = new CommandInterceptor() { @Override public Object handleDefault(InvocationContext ctx, VisitableCommand command) throws Throwable { if (Thread.currentThread().getName().equals(slowThreadName)) { Thread.sleep(1000); } return super.handleDefault(ctx, command); } }; TestingUtil.injectInterceptor(cache, slowdownInterceptor, OptimisticCreateIfNotExistsInterceptor.class); // now create 2 threads to do concurrent puts. Putter slow = new Putter(slowThreadName); Putter fast = new Putter(fastThreadName); // start the slow putter first slow.start(); TestingUtil.sleepThread(200); fast.start(); fast.join(); slow.join(); for (Exception e : exceptions) e.printStackTrace(); assertEquals(0, exceptions.size()); } public void testConcurrentRemove() throws Exception { final String slowThreadName = "SLOW"; final String fastThreadName = "FAST"; CommandInterceptor slowdownInterceptor = new CommandInterceptor() { @Override public Object handleDefault(InvocationContext ctx, VisitableCommand command) throws Throwable { if (Thread.currentThread().getName().equals(slowThreadName) && ctx.getMethodCall().getMethodId() == OptimisticPrepareCommand.METHOD_ID) { Thread.sleep(1000); } return invokeNextInterceptor(ctx, command); } }; TestingUtil.injectInterceptor(cache, slowdownInterceptor, TxInterceptor.class); // now create 2 threads to do concurrent puts. Remover slow = new Remover(slowThreadName); Remover fast = new Remover(fastThreadName); cache.put(f, "hello", "world"); // start the slow putter first slow.start(); TestingUtil.sleepThread(200); fast.start(); fast.join(); slow.join(); for (Exception e : exceptions) e.printStackTrace(); assertEquals(0, exceptions.size()); } public class Putter extends Thread { public Putter(String name) { super(name); } public void run() { try { cache.getTransactionManager().begin(); cache.put(Fqn.fromRelativeElements(f, getName()), "a", "b"); cache.getTransactionManager().commit(); } catch (Exception e) { exceptions.add(e); } } } public class Remover extends Thread { public Remover(String name) { super(name); } public void run() { try { cache.getTransactionManager().begin(); cache.removeNode(f); cache.getTransactionManager().commit(); } catch (Exception e) { exceptions.add(e); } } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/optimistic/DataVersionPersistenceTest.java0000644000175000017500000001163711012542031032744 0ustar twernertwernerpackage org.jboss.cache.optimistic; import org.jboss.cache.Cache; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.NodeSPI; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.config.Configuration; import org.jboss.cache.loader.CacheLoader; import org.jboss.cache.loader.DummySharedInMemoryCacheLoader; import org.jboss.cache.transaction.DummyTransactionManagerLookup; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.io.IOException; /** * Tests whether data versions are transferred along with state * * @author Manik Surtani * @since 2.1.0 */ @Test(groups = {"functional"}) public class DataVersionPersistenceTest { private Cache cache; private CacheLoader loader; @BeforeMethod public void setUp() throws IOException { cache = new DefaultCacheFactory().createCache(false); cache.getConfiguration().setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName()); cache.getConfiguration().setNodeLockingScheme(Configuration.NodeLockingScheme.OPTIMISTIC); CacheLoaderConfig clc = new CacheLoaderConfig(); CacheLoaderConfig.IndividualCacheLoaderConfig iclc = new CacheLoaderConfig.IndividualCacheLoaderConfig(); iclc.setProperties("debug=true"); iclc.setClassName(DummySharedInMemoryCacheLoader.class.getName()); clc.addIndividualCacheLoaderConfig(iclc); cache.getConfiguration().setCacheLoaderConfig(clc); cache.start(); loader = ((CacheSPI) cache).getCacheLoaderManager().getCacheLoader(); } @AfterMethod public void tearDown() { try { cache.getConfiguration().getRuntimeConfig().getTransactionManager().rollback(); } catch (Exception e) { // do nothing? } ((DummySharedInMemoryCacheLoader) loader).wipe(); cache.stop(); } public void testStateTransferDefaultVersions() throws Exception { Fqn f = Fqn.fromString("/one/two/three"); cache.put(f, "k", "v"); cache.put(f, "k1", "v1"); cache.remove(f, "k1"); assert loader.get(f).containsKey("_JBOSS_INTERNAL_OPTIMISTIC_DATA_VERSION"); assert ((DefaultDataVersion) loader.get(f).get("_JBOSS_INTERNAL_OPTIMISTIC_DATA_VERSION")).getRawVersion() == 3; NodeSPI n = (NodeSPI) cache.getRoot().getChild(f); DataVersion dv = n.getVersion(); assert dv instanceof DefaultDataVersion : "Should be an instance of DefaultDataVersion"; assert ((DefaultDataVersion) dv).getRawVersion() == 3 : "Should have accurate data version"; // now restart cache instance cache.stop(); cache.start(); assert cache.get(f, "k").equals("v") : "Value should have peristed"; n = (NodeSPI) cache.getRoot().getChild(f); dv = n.getVersion(); assert dv instanceof DefaultDataVersion : "Should be an instance of DefaultDataVersion"; assert ((DefaultDataVersion) dv).getRawVersion() == 3 : "Version should have peristed"; // make sure leakage doesn't occur into data map assert n.getData().size() == 1; } public void testStateTransferCustomVersion() throws Exception { Fqn f = Fqn.fromString("/one/two/three"); cache.getInvocationContext().getOptionOverrides().setDataVersion(new CharVersion('A')); cache.put(f, "k", "v"); cache.getInvocationContext().getOptionOverrides().setDataVersion(new CharVersion('B')); cache.put(f, "k1", "v1"); cache.getInvocationContext().getOptionOverrides().setDataVersion(new CharVersion('C')); cache.remove(f, "k1"); NodeSPI n = (NodeSPI) cache.getRoot().getChild(f); DataVersion dv = n.getVersion(); assert dv instanceof CharVersion : "Should be an instance of CharVersion"; assert ((CharVersion) dv).version == 'C' : "Should have accurate data version"; // now restart cache instance cache.stop(); cache.start(); assert cache.get(f, "k").equals("v") : "Value should have peristed"; n = (NodeSPI) cache.getRoot().getChild(f); dv = n.getVersion(); assert dv instanceof CharVersion : "Should be an instance of CharVersion"; assert ((CharVersion) dv).version == 'C' : "Version should have peristed"; // make sure leakage doesn't occur into data map assert n.getData().size() == 1; } public static class CharVersion implements DataVersion { private char version = 'A'; public CharVersion(char version) { this.version = version; } public boolean newerThan(DataVersion other) { if (other instanceof CharVersion) { CharVersion otherVersion = (CharVersion) other; return version > otherVersion.version; } else { return true; } } } } ././@LongLink0000000000000000000000000000014500000000000011565 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/optimistic/NodeInterceptorRemoveDataTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/optimistic/NodeInterceptorRemoveDataTest.java0000755000175000017500000001506111017455042033404 0ustar twernertwernerpackage org.jboss.cache.optimistic; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.interceptors.OptimisticCreateIfNotExistsInterceptor; import org.jboss.cache.interceptors.OptimisticNodeInterceptor; import org.jboss.cache.interceptors.base.CommandInterceptor; import org.jboss.cache.loader.SamplePojo; import org.jboss.cache.transaction.GlobalTransaction; import org.jboss.cache.transaction.OptimisticTransactionEntry; import org.jboss.cache.transaction.TransactionTable; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.Transaction; import javax.transaction.TransactionManager; import java.util.HashMap; import java.util.Map; /** * @author xenephon */ @SuppressWarnings("unchecked") @Test(groups = "functional") public class NodeInterceptorRemoveDataTest extends AbstractOptimisticTestCase { private CacheSPI cache; private TransactionManager mgr; private TestListener listener; private MockInterceptor dummy; @BeforeMethod public void setUp() throws Exception { listener = new TestListener(); cache = createCacheWithListener(listener); CommandInterceptor interceptor = TestingUtil.findInterceptor(cache, OptimisticCreateIfNotExistsInterceptor.class); CommandInterceptor nodeInterceptor = TestingUtil.findInterceptor(cache, OptimisticNodeInterceptor.class); dummy = new MockInterceptor(); interceptor.setNext(nodeInterceptor); nodeInterceptor.setNext(dummy); TestingUtil.replaceInterceptorChain(cache, interceptor); mgr = cache.getTransactionManager(); } @AfterMethod public void tearDown() { TestingUtil.killCaches(cache); } public void testTransactionRemoveNoNodeDataMethod() throws Exception { mgr.begin(); Transaction tx = mgr.getTransaction(); setupTransactions(cache, tx); assert null == cache.getNode("/one/two"); assertEquals(null, dummy.getCalledCommand()); TransactionTable table = cache.getTransactionTable(); GlobalTransaction gtx = table.get(tx); OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table.get(gtx); TransactionWorkspace workspace = entry.getTransactionWorkSpace(); mgr.commit(); //assert what should be the results of our call assertEquals(0, workspace.getNodes().size()); assertNull(workspace.getNode(Fqn.fromString("/one/two"))); assertTrue(entry.getLocks().isEmpty()); assertEquals(0, entry.getModifications().size()); assertTrue(!cache.exists("/one/two")); assertEquals(null, dummy.getCalledCommand()); } public void testTransactionRemoveEmptyMethod() throws Exception { mgr.begin(); Transaction tx = mgr.getTransaction(); setupTransactions(cache, tx); Map temp = new HashMap(); cache.put("/one/two", temp); cache.getNode("/one/two").clearData(); assertEquals(null, dummy.getCalledCommand()); TransactionTable table = cache.getTransactionTable(); GlobalTransaction gtx = table.get(tx); OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table.get(gtx); TransactionWorkspace workspace = entry.getTransactionWorkSpace(); mgr.commit(); //assert what should be the results of our call assertEquals(3, workspace.getNodes().size()); assertNotNull(workspace.getNode(Fqn.fromString("/one/two"))); assertNull(workspace.getNode(Fqn.fromString("/one/two")).get("key1")); assertEquals(0, workspace.getNode(Fqn.fromString("/one/two")).getMergedData().size()); assertTrue(entry.getLocks().isEmpty()); assertEquals(2, entry.getModifications().size()); assertTrue(!cache.exists("/one/two")); assertEquals(null, dummy.getCalledCommand()); } public void testTransactionRemoveDataMethod() throws Exception { mgr.begin(); Transaction tx = mgr.getTransaction(); setupTransactions(cache, tx); SamplePojo pojo = new SamplePojo(21, "test"); Map temp = new HashMap(); temp.put("key1", pojo); cache.put("/one/two", temp); assertEquals(null, dummy.getCalledCommand()); TransactionTable table = cache.getTransactionTable(); GlobalTransaction gtx = table.get(tx); OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table.get(gtx); TransactionWorkspace workspace = entry.getTransactionWorkSpace(); assertEquals(1, workspace.getNode(Fqn.fromString("/one/two")).getMergedData().size()); cache.getNode("/one/two").clearData(); mgr.commit(); //assert what should be the results of our call assertEquals(3, workspace.getNodes().size()); assertNotNull(workspace.getNode(Fqn.fromString("/one/two"))); assertNull(workspace.getNode(Fqn.fromString("/one/two")).get("key1")); assertEquals(0, workspace.getNode(Fqn.fromString("/one/two")).getMergedData().size()); assertTrue(entry.getLocks().isEmpty()); assertEquals(2, entry.getModifications().size()); assertTrue(!cache.exists("/one/two")); assertEquals(null, dummy.getCalledCommand()); } public void testTransactionRemoveOtherNodeDataMethod() throws Exception { mgr.begin(); Transaction tx = mgr.getTransaction(); setupTransactions(cache, tx); SamplePojo pojo = new SamplePojo(21, "test"); Map temp = new HashMap(); temp.put("key1", pojo); cache.put("/one/two", temp); assertEquals(null, dummy.getCalledCommand()); TransactionTable table = cache.getTransactionTable(); GlobalTransaction gtx = table.get(tx); OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table.get(gtx); TransactionWorkspace workspace = entry.getTransactionWorkSpace(); assertEquals(1, workspace.getNode(Fqn.fromString("/one/two")).getMergedData().size()); cache.getNode("/one").clearData(); mgr.commit(); //assert what should be the results of our call assertEquals(3, workspace.getNodes().size()); assertNotNull(workspace.getNode(Fqn.fromString("/one/two"))); assertNotNull(workspace.getNode(Fqn.fromString("/one/two")).get("key1")); assertEquals(1, workspace.getNode(Fqn.fromString("/one/two")).getMergedData().size()); assertTrue(entry.getLocks().isEmpty()); assertEquals(2, entry.getModifications().size()); assertTrue(!cache.exists("/one/two")); assertEquals(null, dummy.getCalledCommand()); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/optimistic/ParentVersionTest.java0000644000175000017500000001517010731233724031130 0ustar twernertwernerpackage org.jboss.cache.optimistic; import org.jboss.cache.Cache; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.Node; import org.jboss.cache.NodeSPI; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.TransactionManager; /** * @author Manik Surtani * @since 2.0.0 */ @Test(groups = {"functional"}) public class ParentVersionTest extends AbstractOptimisticTestCase { private Cache cache; private TransactionManager tm; protected boolean lockParentForChildInsertRemove = false; // the default private Fqn parent = Fqn.fromString("/parent"); private Fqn child1 = Fqn.fromString("/parent/child1"); private Fqn child2 = Fqn.fromString("/parent/child2"); private Fqn deepchild = Fqn.fromString("/parent/deep/child"); @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { if (lockParentForChildInsertRemove) { cache = createCacheUnstarted(); cache.getConfiguration().setLockParentForChildInsertRemove(true); cache.start(); } else cache = createCache(); tm = ((CacheSPI) cache).getTransactionManager(); } @AfterMethod(alwaysRun = true) public void tearDown() { destroyCache(cache); } private long getVersion(Node n) { return ((DefaultDataVersion) ((NodeSPI) n).getVersion()).getRawVersion(); } public void testSimpleAdd() { cache.put(parent, "k", "v"); long parentVersion = getVersion(cache.getRoot().getChild(parent)); cache.put(child1, "k", "v"); if (lockParentForChildInsertRemove) assertEquals(parentVersion + 1, getVersion(cache.getRoot().getChild(parent))); else assertEquals(parentVersion, getVersion(cache.getRoot().getChild(parent))); assertTrue(cache.getRoot().hasChild(parent)); assertTrue(cache.getRoot().hasChild(child1)); } public void testSimpleRemove() { cache.put(parent, "k", "v"); cache.put(child1, "k", "v"); assertTrue(cache.getRoot().hasChild(parent)); assertTrue(cache.getRoot().hasChild(child1)); long parentVersion = getVersion(cache.getRoot().getChild(parent)); cache.removeNode(child1); if (lockParentForChildInsertRemove) assertEquals(parentVersion + 1, getVersion(cache.getRoot().getChild(parent))); else assertEquals(parentVersion, getVersion(cache.getRoot().getChild(parent))); assertTrue(cache.getRoot().hasChild(parent)); assertFalse("Should have removed child1", cache.getRoot().hasChild(child1)); } public void testAddAndRemove() throws Exception { cache.put(parent, "k", "v"); cache.put(child1, "k", "v"); assertTrue(cache.getRoot().hasChild(parent)); assertTrue(cache.getRoot().hasChild(child1)); assertFalse(cache.getRoot().hasChild(child2)); long parentVersion = getVersion(cache.getRoot().getChild(parent)); tm.begin(); cache.put(child2, "k", "v"); cache.removeNode(child1); tm.commit(); if (lockParentForChildInsertRemove) assertEquals(parentVersion + 1, getVersion(cache.getRoot().getChild(parent))); else assertEquals(parentVersion, getVersion(cache.getRoot().getChild(parent))); assertTrue(cache.getRoot().hasChild(parent)); assertFalse("Should have removed child1", cache.getRoot().hasChild(child1)); assertTrue(cache.getRoot().hasChild(child2)); } public void testAddAndRemoveOverlap() throws Exception { cache.put(parent, "k", "v"); cache.put(child1, "k", "v"); assertTrue(cache.getRoot().hasChild(parent)); assertTrue(cache.getRoot().hasChild(child1)); assertFalse(cache.getRoot().hasChild(child2)); long parentVersion = getVersion(cache.getRoot().getChild(parent)); tm.begin(); cache.put(child2, "k", "v"); cache.removeNode(child1); cache.removeNode(child2); cache.removeNode(child1); cache.put(child1, "k", "v"); cache.removeNode(child1); cache.removeNode(child2); cache.put(child2, "k", "v"); tm.commit(); if (lockParentForChildInsertRemove) assertEquals(parentVersion + 1, getVersion(cache.getRoot().getChild(parent))); else assertEquals(parentVersion, getVersion(cache.getRoot().getChild(parent))); assertTrue(cache.getRoot().hasChild(parent)); assertFalse("Should have removed child1", cache.getRoot().hasChild(child1)); assertTrue(cache.getRoot().hasChild(child2)); } public void testRemoveAndAdd() throws Exception { cache.put(parent, "k", "v"); cache.put(child1, "k", "v"); assertTrue(cache.getRoot().hasChild(parent)); assertTrue(cache.getRoot().hasChild(child1)); assertFalse(cache.getRoot().hasChild(child2)); long parentVersion = getVersion(cache.getRoot().getChild(parent)); tm.begin(); cache.removeNode(child1); cache.put(child2, "k", "v"); tm.commit(); if (lockParentForChildInsertRemove) assertEquals(parentVersion + 1, getVersion(cache.getRoot().getChild(parent))); else assertEquals(parentVersion, getVersion(cache.getRoot().getChild(parent))); assertTrue(cache.getRoot().hasChild(parent)); assertFalse("Should have removed child1", cache.getRoot().hasChild(child1)); assertTrue(cache.getRoot().hasChild(child2)); } public void testDeepRemove() { cache.put(parent, "k", "v"); cache.put(deepchild, "k", "v"); assertTrue(cache.getRoot().hasChild(parent)); assertTrue(cache.getRoot().hasChild(deepchild)); long parentVersion = getVersion(cache.getRoot().getChild(parent)); cache.removeNode(deepchild); assertEquals(parentVersion, getVersion(cache.getRoot().getChild(parent))); assertTrue(cache.getRoot().hasChild(parent)); assertFalse("Should have removed deepchild", cache.getRoot().hasChild(deepchild)); } public void testDeepAdd() { cache.put(parent, "k", "v"); assertTrue(cache.getRoot().hasChild(parent)); assertFalse(cache.getRoot().hasChild(deepchild)); long parentVersion = getVersion(cache.getRoot().getChild(parent)); cache.put(deepchild, "k", "v"); if (lockParentForChildInsertRemove) assertEquals(parentVersion + 1, getVersion(cache.getRoot().getChild(parent))); else assertEquals(parentVersion, getVersion(cache.getRoot().getChild(parent))); assertTrue(cache.getRoot().hasChild(parent)); assertTrue(cache.getRoot().hasChild(deepchild)); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/optimistic/NodeInterceptorKeyValTest.java0000755000175000017500000002145211017455042032551 0ustar twernertwernerpackage org.jboss.cache.optimistic; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.interceptors.OptimisticCreateIfNotExistsInterceptor; import org.jboss.cache.interceptors.OptimisticNodeInterceptor; import org.jboss.cache.interceptors.base.CommandInterceptor; import org.jboss.cache.loader.SamplePojo; import org.jboss.cache.transaction.GlobalTransaction; import org.jboss.cache.transaction.OptimisticTransactionEntry; import org.jboss.cache.transaction.TransactionTable; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.Transaction; import javax.transaction.TransactionManager; /** * @author xenephon */ @Test(groups = "functional") public class NodeInterceptorKeyValTest extends AbstractOptimisticTestCase { private CacheSPI cache; private TestListener listener; private MockInterceptor dummy; private TransactionManager mgr; @BeforeMethod public void setUp() throws Exception { listener = new TestListener(); cache = createCacheWithListener(listener); CommandInterceptor interceptor = TestingUtil.findInterceptor(cache, OptimisticCreateIfNotExistsInterceptor.class); CommandInterceptor nodeInterceptor = TestingUtil.findInterceptor(cache, OptimisticNodeInterceptor.class); dummy = new MockInterceptor(); interceptor.setNext(nodeInterceptor); nodeInterceptor.setNext(dummy); TestingUtil.replaceInterceptorChain(cache, interceptor); mgr = cache.getTransactionManager(); } @AfterMethod public void tearDown() { TestingUtil.killCaches(cache); } public void testTransactionPutKeyMethod() throws Exception { mgr.begin(); Transaction tx = mgr.getTransaction(); setupTransactions(cache, tx); SamplePojo pojo = new SamplePojo(21, "test"); cache.put("/one/two", "key1", pojo); assertEquals(null, dummy.getCalledCommand()); TransactionTable table = cache.getTransactionTable(); GlobalTransaction gtx = table.get(tx); OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table.get(gtx); @SuppressWarnings("unchecked") TransactionWorkspace workspace = entry.getTransactionWorkSpace(); mgr.commit(); //assert what should be the results of our call assertEquals(3, workspace.getNodes().size()); assertNotNull(workspace.getNode(Fqn.fromString("/one/two"))); assertEquals(pojo, workspace.getNode(Fqn.fromString("/one/two")).get("key1")); assertTrue(entry.getLocks().isEmpty()); assertEquals(1, entry.getModifications().size()); assertTrue(!cache.exists("/one/two")); assertEquals(null, dummy.getCalledCommand()); } public void testTransactionKeyValOverwriteMethod() throws Exception { mgr.begin(); Transaction tx = mgr.getTransaction(); setupTransactions(cache, tx); SamplePojo pojo = new SamplePojo(21, "test"); cache.put("/one/two", "key1", pojo); //overwrite the map we just put in SamplePojo pojo2 = new SamplePojo(21, "test"); cache.put("/one/two", "key1", pojo2); assertEquals(null, dummy.getCalledCommand()); TransactionTable table = cache.getTransactionTable(); GlobalTransaction gtx = table.get(tx); OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table.get(gtx); @SuppressWarnings("unchecked") TransactionWorkspace workspace = entry.getTransactionWorkSpace(); mgr.commit(); //assert what should be the results of our call assertEquals(3, workspace.getNodes().size()); assertNotNull(workspace.getNode(Fqn.fromString("/one/two"))); assertEquals(pojo2, workspace.getNode(Fqn.fromString("/one/two")).get("key1")); assertTrue(entry.getLocks().isEmpty()); assertEquals(2, entry.getModifications().size()); assertTrue(!cache.exists("/one/two")); assertEquals(null, dummy.getCalledCommand()); } public void testTransactionKeyValOverwriteNullMethod() throws Exception { mgr.begin(); Transaction tx = mgr.getTransaction(); setupTransactions(cache, tx); SamplePojo pojo = new SamplePojo(21, "test"); cache.put("/one/two", "key1", pojo); cache.put("/one/two", "key1", null); assertEquals(null, dummy.getCalledCommand()); TransactionTable table = cache.getTransactionTable(); GlobalTransaction gtx = table.get(tx); OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table.get(gtx); @SuppressWarnings("unchecked") TransactionWorkspace workspace = entry.getTransactionWorkSpace(); mgr.commit(); //assert what should be the results of our call assertEquals(3, workspace.getNodes().size()); assertNotNull(workspace.getNode(Fqn.fromString("/one/two"))); assertEquals(null, workspace.getNode(Fqn.fromString("/one/two")).get("key1")); assertTrue(entry.getLocks().isEmpty()); assertEquals(2, entry.getModifications().size()); assertTrue(!cache.exists("/one/two")); assertEquals(null, dummy.getCalledCommand()); } public void testTransactionAdditionlaKeyValMethod() throws Exception { mgr.begin(); Transaction tx = mgr.getTransaction(); setupTransactions(cache, tx); SamplePojo pojo = new SamplePojo(21, "test"); cache.put("/one/two", "key1", pojo); SamplePojo pojo2 = new SamplePojo(21, "test"); cache.put("/one/two", "key2", pojo2); assertEquals(null, dummy.getCalledCommand()); TransactionTable table = cache.getTransactionTable(); GlobalTransaction gtx = table.get(tx); OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table.get(gtx); @SuppressWarnings("unchecked") TransactionWorkspace workspace = entry.getTransactionWorkSpace(); mgr.commit(); //assert what should be the results of our call assertEquals(3, workspace.getNodes().size()); assertNotNull(workspace.getNode(Fqn.fromString("/one/two"))); assertEquals(pojo, workspace.getNode(Fqn.fromString("/one/two")).get("key1")); assertEquals(pojo2, workspace.getNode(Fqn.fromString("/one/two")).get("key2")); assertTrue(entry.getLocks().isEmpty()); assertEquals(2, entry.getModifications().size()); assertTrue(!cache.exists("/one/two")); assertEquals(null, dummy.getCalledCommand()); } public void testTwoTransactionAdditionKeyValMethod() throws Exception { mgr.begin(); Transaction tx = mgr.getTransaction(); setupTransactions(cache, tx); SamplePojo pojo = new SamplePojo(21, "test"); cache.put("/one/two", "key1", pojo); //suspend current transaction mgr.suspend(); //start a new transaction mgr.begin(); Transaction tx2 = mgr.getTransaction(); setupTransactions(cache, tx2); SamplePojo pojo2 = new SamplePojo(21, "test"); cache.put("/one/two", "key2", pojo2); assertEquals(null, dummy.getCalledCommand()); TransactionTable table = cache.getTransactionTable(); GlobalTransaction gtx = table.get(tx); OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table.get(gtx); @SuppressWarnings("unchecked") TransactionWorkspace workspace = entry.getTransactionWorkSpace(); //resume the suspended transaction GlobalTransaction gtx2 = table.get(tx2); OptimisticTransactionEntry entry2 = (OptimisticTransactionEntry) table.get(gtx2); @SuppressWarnings("unchecked") TransactionWorkspace workspace2 = entry2.getTransactionWorkSpace(); //commit both tx mgr.commit(); mgr.resume(tx); mgr.commit(); //assert that our keys are in one space assertEquals(3, workspace.getNodes().size()); assertNotNull(workspace.getNode(Fqn.fromString("/one/two"))); assertEquals(pojo, workspace.getNode(Fqn.fromString("/one/two")).get("key1")); assertEquals(null, workspace.getNode(Fqn.fromString("/one/two")).get("key2")); assertTrue(entry.getLocks().isEmpty()); assertEquals(1, entry.getModifications().size()); //assert that our keys are in one space assertEquals(3, workspace2.getNodes().size()); assertNotNull(workspace2.getNode(Fqn.fromString("/one/two"))); assertEquals(null, workspace2.getNode(Fqn.fromString("/one/two")).get("key1")); assertEquals(pojo2, workspace2.getNode(Fqn.fromString("/one/two")).get("key2")); assertTrue(entry2.getLocks().isEmpty()); assertEquals(1, entry2.getModifications().size()); assertTrue(!cache.exists("/one/two")); assertEquals(null, dummy.getCalledCommand()); } } ././@LongLink0000000000000000000000000000014500000000000011565 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/optimistic/OptimisticWithPassivationTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/optimistic/OptimisticWithPassivationTest.java0000644000175000017500000000635510735006726033543 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.optimistic; import org.jboss.cache.CacheSPI; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.factories.XmlConfigurationParser; import org.jboss.cache.loader.CacheLoader; import org.jboss.cache.transaction.DummyTransactionManager; import org.jboss.cache.xml.XmlHelper; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertNull; import org.testng.annotations.Test; import org.w3c.dom.Element; /** * Tests optimistic locking with pasivation * * @author Manik Surtani (manik@jboss.org) */ @Test(groups = "functional") public class OptimisticWithPassivationTest extends AbstractOptimisticTestCase { protected CacheLoaderConfig getCacheLoaderConfig() throws Exception { String xml = " \n" + " \n" + " true\n" + " \n" + "\n" + " \n" + " org.jboss.cache.loader.DummyInMemoryCacheLoader\n" + " false\n" + " false\n" + " false\n" + " \n" + " \n" + " "; Element element = XmlHelper.stringToElement(xml); return XmlConfigurationParser.parseCacheLoaderConfig(element); } private CacheSPI createLocalCache() throws Exception { CacheSPI cache = createCacheUnstarted(false); cache.getConfiguration().setCacheLoaderConfig(getCacheLoaderConfig()); cache.create(); cache.start(); return cache; } public void testPassivationLocal() throws Exception { CacheSPI cache = createLocalCache(); CacheLoader loader = cache.getCacheLoaderManager().getCacheLoader(); // clean up cache.removeNode(fqn); loader.remove(fqn); assertNull(loader.get(fqn)); DummyTransactionManager mgr = DummyTransactionManager.getInstance(); // put something in the cache mgr.begin(); cache.put(fqn, key, value); mgr.commit(); // should be nothing in the loader assertEquals(value, cache.get(fqn, key)); assertNull(loader.get(fqn)); // evict from cache mgr.begin(); cache.evict(fqn); mgr.commit(); mgr.begin(); // should now be passivated in the loader // don't do a cache.get() first as this will activate this node from the loader again! // assertNull( cache.get( fqn ) ); assertEquals(value, loader.get(fqn).get(key)); // now do a cache.get()... assertEquals(value, cache.get(fqn, key)); // and the object should now be removed from the loader assertNull(loader.get(fqn)); mgr.commit(); // clean up mgr.begin(); cache.removeNode(fqn); loader.remove(fqn); mgr.commit(); } } ././@LongLink0000000000000000000000000000015300000000000011564 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/optimistic/NodeInterceptorGetChildrenNamesTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/optimistic/NodeInterceptorGetChildrenNamesTes0000644000175000017500000001376711017455042033435 0ustar twernertwernerpackage org.jboss.cache.optimistic; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.factories.ComponentRegistry; import org.jboss.cache.interceptors.OptimisticCreateIfNotExistsInterceptor; import org.jboss.cache.interceptors.OptimisticNodeInterceptor; import org.jboss.cache.interceptors.base.CommandInterceptor; import org.jboss.cache.loader.SamplePojo; import org.jboss.cache.transaction.GlobalTransaction; import org.jboss.cache.transaction.OptimisticTransactionEntry; import org.jboss.cache.transaction.TransactionTable; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.Transaction; import javax.transaction.TransactionManager; import java.util.Iterator; /** * @author xenephon */ @Test(groups = {"functional"}) public class NodeInterceptorGetChildrenNamesTest extends AbstractOptimisticTestCase { TestListener listener; CacheSPI cache; MockInterceptor dummy; TransactionManager mgr; @BeforeMethod public void setUp() throws Exception { listener = new TestListener(); cache = createCacheWithListener(listener); ComponentRegistry cr = TestingUtil.extractComponentRegistry(cache); CommandInterceptor interceptor = new OptimisticCreateIfNotExistsInterceptor(); cr.registerComponent(interceptor, OptimisticCreateIfNotExistsInterceptor.class); CommandInterceptor nodeInterceptor = new OptimisticNodeInterceptor(); cr.registerComponent(nodeInterceptor, OptimisticNodeInterceptor.class); dummy = new MockInterceptor(); cr.registerComponent(dummy, MockInterceptor.class); interceptor.setNext(nodeInterceptor); nodeInterceptor.setNext(dummy); TestingUtil.replaceInterceptorChain(cache, interceptor); mgr = cache.getConfiguration().getRuntimeConfig().getTransactionManager(); } @AfterMethod public void tearDown() { TestingUtil.killCaches(cache); } public void testTransactionGetNamesMethod() throws Exception { mgr.begin(); Transaction tx = mgr.getTransaction(); setupTransactions(cache, tx); SamplePojo pojo = new SamplePojo(21, "test"); cache.put("/one/two", "key1", pojo); assertEquals(null, dummy.getCalledCommand()); TransactionTable table = cache.getTransactionTable(); GlobalTransaction gtx = table.get(tx); OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table.get(gtx); @SuppressWarnings("unchecked") TransactionWorkspace workspace = entry.getTransactionWorkSpace(); //assert we can see this with a key value get in the transaction assertEquals(1, cache.getNode("/one").getChildrenNames().size()); mgr.commit(); //assert what should be the results of our call assertEquals(3, workspace.getNodes().size()); assertNotNull(workspace.getNode(Fqn.fromString("/one/two"))); assertEquals(pojo, workspace.getNode(Fqn.fromString("/one/two")).get("key1")); assertTrue(entry.getLocks().isEmpty()); assertEquals(1, entry.getModifications().size()); assertTrue(!cache.exists("/one/two")); assertEquals(null, dummy.getCalledCommand()); //assert that we cannot see the change if we have not put it into the cache // we need to do this as we do not have the tx interceptor in this stack mgr.begin(); Transaction tx2 = mgr.getTransaction(); setupTransactions(cache, tx2); assertEquals(0, cache.getRoot().getChildrenNames().size()); mgr.commit(); } public void testTransactionGetNoNamesMethod() throws Exception { mgr.begin(); Transaction tx = mgr.getTransaction(); setupTransactions(cache, tx); assertEquals(null, dummy.getCalledCommand()); TransactionTable table = cache.getTransactionTable(); GlobalTransaction gtx = table.get(tx); OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table.get(gtx); //assert we can see this with a key value get in the transaction assertEquals(0, cache.getRoot().getChildrenNames().size()); mgr.commit(); assertTrue(entry.getLocks().isEmpty()); assertEquals(0, entry.getModifications().size()); assertTrue(!cache.exists("/one/two")); assertEquals(null, dummy.getCalledCommand()); } public void testTransactionGetNamesIteratorMethod() throws Exception { mgr.begin(); Transaction tx = mgr.getTransaction(); setupTransactions(cache, tx); SamplePojo pojo = new SamplePojo(21, "test"); cache.put("/one/two", "key1", pojo); assertEquals(null, dummy.getCalledCommand()); TransactionTable table = cache.getTransactionTable(); GlobalTransaction gtx = table.get(tx); OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table.get(gtx); @SuppressWarnings("unchecked") TransactionWorkspace workspace = entry.getTransactionWorkSpace(); //assert we can see this assertEquals(1, cache.getNode("/one").getChildrenNames().size()); try { for (Iterator it = cache.getNode("/one").getChildrenNames().iterator(); it.hasNext();) { it.next(); it.remove(); } fail("Should not be allowed to modify elements in the set returned by getChildrenNames()"); } catch (UnsupportedOperationException uoe) { // the returned set should be unmodifiable and a remove on the iterator should fail. } //assert the removal has had no effect assertEquals(1, cache.getNode("/one").getChildrenNames().size()); assertNotNull(workspace.getNode(Fqn.fromString("/one/two"))); mgr.commit(); assertTrue(entry.getLocks().isEmpty()); assertEquals(1, entry.getModifications().size()); assertTrue(!cache.exists("/one/two")); assertEquals(null, dummy.getCalledCommand()); } } ././@LongLink0000000000000000000000000000015400000000000011565 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/optimistic/OptimisticReplicationInterceptorTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/optimistic/OptimisticReplicationInterceptorTe0000755000175000017500000005201611017455042033577 0ustar twernertwerner/* * Created on 17-Feb-2005 * * * */ package org.jboss.cache.optimistic; import org.jboss.cache.CacheSPI; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.commands.ReversibleCommand; import org.jboss.cache.commands.tx.CommitCommand; import org.jboss.cache.commands.tx.OptimisticPrepareCommand; import org.jboss.cache.commands.tx.RollbackCommand; import org.jboss.cache.config.Configuration; import org.jboss.cache.loader.SamplePojo; import org.jboss.cache.transaction.DummyTransactionManager; import org.jboss.cache.transaction.GlobalTransaction; import org.jboss.cache.transaction.OptimisticTransactionEntry; import org.jboss.cache.transaction.TransactionTable; import org.jgroups.Address; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.RollbackException; import javax.transaction.Transaction; import javax.transaction.TransactionManager; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.ObjectInput; import java.io.ObjectOutput; import java.util.ArrayList; import java.util.List; import java.util.Map; /** * @author xenephon */ @SuppressWarnings("unchecked") @Test(groups = "functional", enabled = false) // disabling since this needs to be rewritten to cope with the new OptimisticReplicationInterceptor. This really doesn't test very much right now. public class OptimisticReplicationInterceptorTest extends AbstractOptimisticTestCase { private CacheSPI cache; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { cache = createCache(); } @AfterMethod(alwaysRun = true) public void tearDown() { TestingUtil.killCaches(cache); } public void testLocalTransaction() throws Exception { MockInterceptor dummy = new MockInterceptor(); setAlteredInterceptorChain(dummy, cache); DummyTransactionManager mgr = DummyTransactionManager.getInstance(); assertNull(mgr.getTransaction()); mgr.begin(); assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache.getTransactionTable().getNumLocalTransactions()); SamplePojo pojo = new SamplePojo(21, "test"); cache.put("/one/two", "key1", pojo); mgr.commit(); assertNull(mgr.getTransaction()); assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache.getTransactionTable().getNumLocalTransactions()); //make sure all calls were done in right order List calls = dummy.getAllCalledIds(); assertEquals(OptimisticPrepareCommand.METHOD_ID, calls.get(0)); assertEquals(CommitCommand.METHOD_ID, calls.get(1)); } public void testRollbackTransaction() throws Exception { MockInterceptor dummy = new MockInterceptor(); setAlteredInterceptorChain(dummy, cache); DummyTransactionManager mgr = DummyTransactionManager.getInstance(); assertNull(mgr.getTransaction()); assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache.getTransactionTable().getNumLocalTransactions()); SamplePojo pojo = new SamplePojo(21, "test"); mgr.begin(); cache.put("/one/two", "key1", pojo); mgr.rollback(); assertNull(mgr.getTransaction()); assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache.getTransactionTable().getNumLocalTransactions()); //make sure all calls were done in right order List calls = dummy.getAllCalledIds(); assertEquals(1, calls.size()); assertEquals(RollbackCommand.METHOD_ID, calls.get(0)); } public void testRemotePrepareTransaction() throws Exception { MockInterceptor dummy = new MockInterceptor(); setAlteredInterceptorChain(dummy, cache); DummyTransactionManager mgr = DummyTransactionManager.getInstance(); //start local transaction mgr.begin(); Transaction tx = mgr.getTransaction(); //this sets cache.getCurrentTransaction(tx, true); SamplePojo pojo = new SamplePojo(21, "test"); cache.put("/one/two", "key1", pojo); GlobalTransaction gtx = cache.getCurrentTransaction(tx, true); TransactionTable table = cache.getTransactionTable(); OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table.get(gtx); assertNotNull(mgr.getTransaction()); mgr.commit(); GlobalTransaction remoteGtx = new GlobalTransaction(); remoteGtx.setAddress(new TestAddress()); //hack the method call to make it have the remote globalTransaction ReversibleCommand command = (ReversibleCommand) entry.getModifications().get(0); command.setGlobalTransaction(remoteGtx); //call our remote method OptimisticPrepareCommand optimisticPrepareCommand = new OptimisticPrepareCommand(remoteGtx, null, (Map) null, (Address) remoteGtx.getAddress(), false); try { TestingUtil.replicateCommand(cache, optimisticPrepareCommand); //getInvocationDelegate(cache)._replicate(prepareMethod); } catch (Throwable t) { fail(); } //our thread should be null assertNull(mgr.getTransaction()); // there should be a registration for the remote globalTransaction assertNotNull(table.get(remoteGtx)); assertNotNull(table.getLocalTransaction(remoteGtx)); //assert that this is populated assertEquals(1, table.get(remoteGtx).getModifications().size()); //assert that the remote prepare has populated the local workspace assertEquals(3, entry.getTransactionWorkSpace().getNodes().size()); assertEquals(1, entry.getModifications().size()); List calls = dummy.getAllCalledIds(); assertEquals(OptimisticPrepareCommand.METHOD_ID, calls.get(2)); assertEquals(1, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(1, cache.getTransactionTable().getNumLocalTransactions()); } public void testRemoteRollbackTransaction() throws Exception { MockInterceptor dummy = new MockInterceptor(); setAlteredInterceptorChain(dummy, cache); DummyTransactionManager mgr = DummyTransactionManager.getInstance(); //start local transaction mgr.begin(); Transaction tx = mgr.getTransaction(); //this sets cache.getCurrentTransaction(tx, true); SamplePojo pojo = new SamplePojo(21, "test"); cache.put("/one/two", "key1", pojo); GlobalTransaction gtx = cache.getCurrentTransaction(tx, true); TransactionTable table = cache.getTransactionTable(); OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table.get(gtx); assertNotNull(mgr.getTransaction()); mgr.commit(); GlobalTransaction remoteGtx = new GlobalTransaction(); remoteGtx.setAddress(new TestAddress()); //hack the method call to make it have the remote globalTransaction ReversibleCommand command = entry.getModifications().get(0); command.setGlobalTransaction(remoteGtx); //call our remote method OptimisticPrepareCommand prepareCommand = new OptimisticPrepareCommand(remoteGtx, null, null, (Address) remoteGtx.getAddress(), false); try { TestingUtil.replicateCommand(cache, prepareCommand); } catch (Throwable t) { fail(); } //our thread should be null assertNull(mgr.getTransaction()); // there should be a registration for the remote globalTransaction assertNotNull(table.get(remoteGtx)); assertNotNull(table.getLocalTransaction(remoteGtx)); //assert that this is populated assertEquals(1, table.get(remoteGtx).getModifications().size()); assertEquals(3, entry.getTransactionWorkSpace().getNodes().size()); assertEquals(1, entry.getModifications().size()); List calls = dummy.getAllCalledIds(); assertEquals(OptimisticPrepareCommand.METHOD_ID, calls.get(2)); assertEquals(1, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(1, cache.getTransactionTable().getNumLocalTransactions()); // call our remote method RollbackCommand cacheCommand = new RollbackCommand(null); try { TestingUtil.replicateCommand(cache, cacheCommand); } catch (Throwable t) { fail(); } //we should have the commit as well now assertNull(mgr.getTransaction()); assertEquals(RollbackCommand.METHOD_ID, calls.get(3)); assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache.getTransactionTable().getNumLocalTransactions()); } public void testRemoteCommitNoPrepareTransaction() throws Exception { MockInterceptor dummy = new MockInterceptor(); setAlteredInterceptorChain(dummy, cache); DummyTransactionManager mgr = DummyTransactionManager.getInstance(); //start local transaction mgr.begin(); Transaction tx = mgr.getTransaction(); //this sets cache.getCurrentTransaction(tx, true); SamplePojo pojo = new SamplePojo(21, "test"); cache.put("/one/two", "key1", pojo); GlobalTransaction gtx = cache.getCurrentTransaction(tx, true); TransactionTable table = cache.getTransactionTable(); OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table.get(gtx); assertNotNull(mgr.getTransaction()); mgr.commit(); GlobalTransaction remoteGtx = new GlobalTransaction(); remoteGtx.setAddress(new TestAddress()); //hack the method call to make it have the remote globalTransaction ReversibleCommand command = entry.getModifications().get(0); command.setGlobalTransaction(remoteGtx); List calls = dummy.getAllCalledIds(); assertEquals(2, calls.size()); assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache.getTransactionTable().getNumLocalTransactions()); // call our remote method CommitCommand cacheCommand = new CommitCommand(gtx); try { TestingUtil.replicateCommand(cache, cacheCommand); fail(); } catch (Throwable t) { assertTrue(t instanceof RuntimeException); //t.printStackTrace(); } //we should have the commit as well now assertNull(mgr.getTransaction()); assertEquals(2, calls.size()); assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache.getTransactionTable().getNumLocalTransactions()); } public void testRemoteRollbackNoPrepareTransaction() throws Throwable { MockInterceptor dummy = new MockInterceptor(); setAlteredInterceptorChain(dummy, cache); DummyTransactionManager mgr = DummyTransactionManager.getInstance(); //start local transaction mgr.begin(); Transaction tx = mgr.getTransaction(); //this sets cache.getCurrentTransaction(tx, true); SamplePojo pojo = new SamplePojo(21, "test"); cache.put("/one/two", "key1", pojo); GlobalTransaction gtx = cache.getCurrentTransaction(tx, true); TransactionTable table = cache.getTransactionTable(); OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table.get(gtx); assertNotNull(mgr.getTransaction()); mgr.commit(); GlobalTransaction remoteGtx = new GlobalTransaction(); remoteGtx.setAddress(new TestAddress()); //hack the method call to make it have the remote globalTransaction ReversibleCommand command = entry.getModifications().get(0); command.setGlobalTransaction(remoteGtx); List calls = dummy.getAllCalledIds(); assertEquals(2, calls.size()); assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache.getTransactionTable().getNumLocalTransactions()); // call our remote method RollbackCommand cacheCommand = new RollbackCommand(remoteGtx); TestingUtil.replicateCommand(cache, cacheCommand); assertTrue("Should be handled on the remote end without barfing, in the event of a rollback without a prepare", true); //we should have the commit as well now assertNull(mgr.getTransaction()); assertEquals(2, calls.size()); assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache.getTransactionTable().getNumLocalTransactions()); } public void testRemoteCommitTransaction() throws Exception { MockInterceptor dummy = new MockInterceptor(); setAlteredInterceptorChain(dummy, cache); DummyTransactionManager mgr = DummyTransactionManager.getInstance(); //start local transaction mgr.begin(); Transaction tx = mgr.getTransaction(); //this sets cache.getCurrentTransaction(tx, true); SamplePojo pojo = new SamplePojo(21, "test"); cache.put("/one/two", "key1", pojo); GlobalTransaction gtx = cache.getCurrentTransaction(tx, true); TransactionTable table = cache.getTransactionTable(); OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table.get(gtx); assertNotNull(mgr.getTransaction()); mgr.commit(); GlobalTransaction remoteGtx = new GlobalTransaction(); remoteGtx.setAddress(new TestAddress()); //hack the method call to make it have the remote globalTransaction ReversibleCommand command = entry.getModifications().get(0); command.setGlobalTransaction(remoteGtx); OptimisticPrepareCommand prepareCommand = new OptimisticPrepareCommand(remoteGtx, null, (Map) null, (Address) remoteGtx.getAddress(), false); try { TestingUtil.replicateCommand(cache, prepareCommand); } catch (Throwable t) { fail(); } //our thread should be null assertNull(mgr.getTransaction()); // there should be a registration for the remote globalTransaction assertNotNull(table.get(remoteGtx)); assertNotNull(table.getLocalTransaction(remoteGtx)); //assert that this is populated assertEquals(1, table.get(remoteGtx).getModifications().size()); //assert that the remote prepare has populated the local workspace assertEquals(3, entry.getTransactionWorkSpace().getNodes().size()); assertEquals(1, entry.getModifications().size()); List calls = dummy.getAllCalledIds(); assertEquals(OptimisticPrepareCommand.METHOD_ID, calls.get(2)); assertEquals(1, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(1, cache.getTransactionTable().getNumLocalTransactions()); // call our remote method CommitCommand commitCommand = new CommitCommand(remoteGtx); try { TestingUtil.replicateCommand(cache, commitCommand); } catch (Throwable t) { fail(); } //we should have the commit as well now assertNull(mgr.getTransaction()); assertEquals(CommitCommand.METHOD_ID, calls.get(3)); assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache.getTransactionTable().getNumLocalTransactions()); } public void testTwoWayRemoteCacheBroadcast() throws Exception { destroyCache(cache); cache = createReplicatedCache(Configuration.CacheMode.REPL_SYNC); MockInterceptor dummy = new MockInterceptor(); setAlteredInterceptorChain(dummy, cache); CacheSPI cache2 = createReplicatedCache(Configuration.CacheMode.REPL_SYNC); MockInterceptor dummy2 = new MockInterceptor(); setAlteredInterceptorChain(dummy2, cache2); TransactionManager mgr = cache.getTransactionManager(); //start local transaction mgr.begin(); Transaction tx = mgr.getTransaction(); //this sets cache.getCurrentTransaction(tx, true); SamplePojo pojo = new SamplePojo(21, "test"); cache.put("/one/two", "key1", pojo); assertNotNull(mgr.getTransaction()); mgr.commit(); assertNull(mgr.getTransaction()); //assert that the local cache is in the right state assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache.getTransactionTable().getNumLocalTransactions()); assertEquals(0, cache2.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache2.getTransactionTable().getNumLocalTransactions()); List calls = dummy.getAllCalledIds(); assertEquals(OptimisticPrepareCommand.METHOD_ID, calls.get(0)); assertEquals(CommitCommand.METHOD_ID, calls.get(1)); List calls2 = dummy2.getAllCalledIds(); assertEquals(OptimisticPrepareCommand.METHOD_ID, calls2.get(0)); assertEquals(CommitCommand.METHOD_ID, calls2.get(1)); destroyCache(cache2); } public void testFailurePrepareRemoteCacheBroadcast() throws Exception { destroyCache(cache); cache = createReplicatedCache(Configuration.CacheMode.REPL_SYNC); MockInterceptor dummy = new MockInterceptor(); setAlteredInterceptorChain(dummy, cache); CacheSPI cache2 = createReplicatedCache(Configuration.CacheMode.REPL_SYNC); MockFailureInterceptor dummy2 = new MockFailureInterceptor(); List failures = new ArrayList(); failures.add(OptimisticPrepareCommand.class); dummy2.setFailurelist(failures); setAlteredInterceptorChain(dummy2, cache2); DummyTransactionManager mgr = DummyTransactionManager.getInstance(); //start local transaction mgr.begin(); Transaction tx = mgr.getTransaction(); //this sets cache.getCurrentTransaction(tx, true); SamplePojo pojo = new SamplePojo(21, "test"); cache.put("/one/two", "key1", pojo); assertNotNull(mgr.getTransaction()); try { mgr.commit(); } catch (Exception e) { assertTrue(e instanceof RollbackException); } assertNull(mgr.getTransaction()); //assert that the local cache is in the right state assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache.getTransactionTable().getNumLocalTransactions()); assertEquals(0, cache2.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache2.getTransactionTable().getNumLocalTransactions()); List calls = dummy.getAllCalledIds(); assertEquals(OptimisticPrepareCommand.METHOD_ID, calls.get(0)); assertEquals(RollbackCommand.METHOD_ID, calls.get(1)); //we have no prepare - as it failed - but we have a commit List calls2 = dummy2.getAllCalledIds(); assertEquals(RollbackCommand.METHOD_ID, calls2.get(0)); destroyCache(cache2); } public void testFailurePrepareLocalCacheBroadcast() throws Exception { destroyCache(cache); cache = createReplicatedCache(Configuration.CacheMode.REPL_SYNC); MockFailureInterceptor dummy = new MockFailureInterceptor(); setAlteredInterceptorChain(dummy, cache); CacheSPI cache2 = createReplicatedCache(Configuration.CacheMode.REPL_SYNC); MockInterceptor dummy2 = new MockInterceptor(); setAlteredInterceptorChain(dummy, cache); List failures = new ArrayList(); failures.add(OptimisticPrepareCommand.class); dummy.setFailurelist(failures); DummyTransactionManager mgr = DummyTransactionManager.getInstance(); //start local transaction mgr.begin(); Transaction tx = mgr.getTransaction(); //this sets cache.getCurrentTransaction(tx, true); SamplePojo pojo = new SamplePojo(21, "test"); cache.put("/one/two", "key1", pojo); assertNotNull(mgr.getTransaction()); try { mgr.commit(); } catch (Exception e) { assertTrue(e instanceof RollbackException); } assertNull(mgr.getTransaction()); //assert that the local cache is in the right state assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache.getTransactionTable().getNumLocalTransactions()); assertEquals(0, cache2.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache2.getTransactionTable().getNumLocalTransactions()); List calls = dummy.getAllCalledIds(); assertEquals(RollbackCommand.METHOD_ID, calls.get(0)); //we have no prepare - as it failed - but we have a commit List calls2 = dummy2.getAllCalledIds(); assertEquals(0, calls2.size()); destroyCache(cache2); } static class TestAddress implements Address { private static final long serialVersionUID = -8525272532201600656L; public boolean isMulticastAddress() { return false; } public void readExternal(ObjectInput arg0) { } public int size() { return 0; } public void writeExternal(ObjectOutput arg0) { } public void writeTo(DataOutputStream arg0) { } public void readFrom(DataInputStream arg0) { } public int compareTo(Object arg0) { return 0; } } } ././@LongLink0000000000000000000000000000016200000000000011564 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/optimistic/OptimisticCreateIfNotExistsInterceptorTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/optimistic/OptimisticCreateIfNotExistsInterce0000755000175000017500000001244211017455042033472 0ustar twernertwerner/* * Created on 17-Feb-2005 * * * */ package org.jboss.cache.optimistic; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.commands.write.PutKeyValueCommand; import org.jboss.cache.interceptors.OptimisticCreateIfNotExistsInterceptor; import org.jboss.cache.interceptors.base.CommandInterceptor; import org.jboss.cache.loader.SamplePojo; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.transaction.DummyTransactionManager; import org.jboss.cache.transaction.GlobalTransaction; import org.jboss.cache.transaction.OptimisticTransactionEntry; import org.jboss.cache.transaction.TransactionTable; import static org.testng.AssertJUnit.*; import org.testng.annotations.Test; import javax.transaction.Transaction; import javax.transaction.TransactionManager; /** * @author xenephon */ @SuppressWarnings("unchecked") @Test(groups = "functional") public class OptimisticCreateIfNotExistsInterceptorTest extends AbstractOptimisticTestCase { protected TransactionManager txManager; protected Transaction tx; protected GlobalTransaction gtx; protected TransactionTable table; protected OptimisticTransactionEntry entry; protected TransactionWorkspace workspace; protected void setupTransactionsInInvocationCtx(CacheSPI cache) throws Exception { txManager = DummyTransactionManager.getInstance(); // start a tx txManager.begin(); // set class level vars table = cache.getTransactionTable(); // create a globalTransaction gtx = cache.getCurrentTransaction(); tx = txManager.getTransaction(); entry = (OptimisticTransactionEntry) table.get(gtx); workspace = entry.getTransactionWorkSpace(); setupTransactions(cache, tx); } public void testNodeCreation() throws Exception { TestListener listener = new TestListener(); final CacheSPI cache = createCacheWithListener(listener); CommandInterceptor interceptor = new OptimisticCreateIfNotExistsInterceptor(); MockInterceptor dummy = new MockInterceptor(); interceptor.setNext(dummy); TestingUtil.replaceInterceptorChain(cache, interceptor); setupTransactionsInInvocationCtx(cache); final SamplePojo pojo = new SamplePojo(21, "test"); cache.put("/one/two", "key1", pojo); assertEquals(3, workspace.getNodes().size()); assertNotNull(workspace.getNode(Fqn.fromString("/one/two"))); assertNotNull(workspace.getNode(Fqn.fromString("/one/"))); assertEquals(null, workspace.getNode(Fqn.fromString("/one/two")).get("key1")); assertTrue(entry.getLocks().isEmpty()); assertTrue(!cache.exists("/one/two")); assertEquals(PutKeyValueCommand.class, dummy.getCalledCommandClass()); txManager.commit(); cache.stop(); } public void testInvalidTransaction() throws Exception { TestListener listener = new TestListener(); final CacheSPI cache = createCacheWithListener(listener); CommandInterceptor interceptor = new OptimisticCreateIfNotExistsInterceptor(); MockInterceptor dummy = new MockInterceptor(); interceptor.setNext(dummy); TestingUtil.replaceInterceptorChain(cache, interceptor); setupTransactionsInInvocationCtx(cache); final SamplePojo pojo = new SamplePojo(21, "test"); cache.put("/one/two", "key1", pojo); assertEquals(3, workspace.getNodes().size()); assertNotNull(workspace.getNode(Fqn.fromString("/one/two"))); assertNotNull(workspace.getNode(Fqn.fromString("/one/"))); assertEquals(null, workspace.getNode(Fqn.fromString("/one/two")).get("key1")); assertTrue(entry.getLocks().isEmpty()); assertTrue(!cache.exists("/one/two")); assertEquals(PutKeyValueCommand.class, dummy.getCalledCommandClass()); txManager.commit(); // we should now remove stuff from the InvocationCtx cache.getInvocationContext().setGlobalTransaction(null); cache.getInvocationContext().setTransaction(null); cache.getInvocationContext().setTransactionEntry(null); try { cache.put("/one/two/three", "key1", pojo); assertTrue("Should never be reched", false); } catch (Throwable t) { assertTrue(true); } cache.stop(); } public void testMultiplePut() throws Exception { TestListener listener = new TestListener(); final CacheSPI cache = createCacheWithListener(listener); CommandInterceptor interceptor = new OptimisticCreateIfNotExistsInterceptor(); MockInterceptor dummy = new MockInterceptor(); interceptor.setNext(dummy); TestingUtil.replaceInterceptorChain(cache, interceptor); SamplePojo pojo = new SamplePojo(21, "test"); setupTransactionsInInvocationCtx(cache); cache.put("/one/two", "key1", pojo); cache.put("/one/two", "key2", pojo); assertEquals(3, workspace.getNodes().size()); assertNotNull(workspace.getNode(Fqn.fromString("/one/two"))); assertNotNull(workspace.getNode(Fqn.fromString("/one/"))); assertEquals(null, workspace.getNode(Fqn.fromString("/one/two")).get("key1")); assertTrue(entry.getLocks().isEmpty()); assertTrue(!cache.exists("/one/two")); assertEquals(PutKeyValueCommand.class, dummy.getCalledCommandClass()); txManager.commit(); cache.stop(); } } ././@LongLink0000000000000000000000000000014500000000000011565 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/optimistic/NodeInterceptorRemoveNodeTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/optimistic/NodeInterceptorRemoveNodeTest.java0000755000175000017500000003255111005303114033410 0ustar twernertwerner/* * Created on 17-Feb-2005 * * * */ package org.jboss.cache.optimistic; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.Node; import org.jboss.cache.commands.tx.CommitCommand; import org.jboss.cache.commands.tx.OptimisticPrepareCommand; import org.jboss.cache.interceptors.CallInterceptor; import org.jboss.cache.transaction.GlobalTransaction; import org.jboss.cache.transaction.OptimisticTransactionEntry; import org.jboss.cache.transaction.TransactionTable; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.Transaction; import javax.transaction.TransactionManager; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; /** * @author xenephon */ @Test(groups = "functional") @SuppressWarnings("unchecked") public class NodeInterceptorRemoveNodeTest extends AbstractOptimisticTestCase { private CacheSPI cache; private TestListener listener; private MockInterceptor dummy; private TransactionManager mgr; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { listener = new TestListener(); cache = createCacheWithListener(listener); dummy = new MockInterceptor(); // dummy.setCache(cache); cache.addInterceptor(dummy, CallInterceptor.class); cache.removeInterceptor(CallInterceptor.class); mgr = cache.getTransactionManager(); } @AfterMethod(alwaysRun = true) public void tearDown() { super.tearDown(); cache.stop(); } public void testTransactionRemoveNotExistsNodeMethod() throws Exception { mgr.begin(); Transaction tx = mgr.getTransaction(); // inject InvocationContext cache.getInvocationContext().setTransaction(tx); cache.getInvocationContext().setGlobalTransaction(cache.getCurrentTransaction(tx, true)); cache.removeNode("/one/two"); TransactionTable table = cache.getTransactionTable(); GlobalTransaction gtx = table.get(tx); OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table.get(gtx); TransactionWorkspace workspace = entry.getTransactionWorkSpace(); mgr.commit(); assert 2 == dummy.getAllCalled().size(); assert dummy.getAllCalled().contains(CommitCommand.class); assert dummy.getAllCalled().contains(OptimisticPrepareCommand.class); //assert what should be the results of our call assertEquals(0, workspace.getNodes().size()); assertTrue(entry.getLocks().isEmpty()); assertTrue(!cache.exists("/one/two")); assertEquals(0, listener.getNodesAdded()); } public void testTransactionRemoveNodeMethod() throws Exception { mgr.begin(); Transaction tx = mgr.getTransaction(); // inject InvocationContext cache.getInvocationContext().setTransaction(tx); cache.getInvocationContext().setGlobalTransaction(cache.getCurrentTransaction(tx, true)); Map temp = new HashMap(); temp.put("key1", "value"); cache.put("/one/two", temp); cache.removeNode("/one/two"); assert dummy.getAllCalled().isEmpty(); TransactionTable table = cache.getTransactionTable(); GlobalTransaction gtx = table.get(tx); OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table.get(gtx); TransactionWorkspace workspace = entry.getTransactionWorkSpace(); mgr.commit(); //assert what should be the results of our call assertEquals(3, workspace.getNodes().size()); assertNotNull(workspace.getNode(Fqn.fromString("/one/two"))); assertEquals(true, workspace.getNode(Fqn.fromString("/one/two")).isDeleted()); assertNotNull(workspace.getNode(Fqn.fromString("/one"))); assertEquals(false, workspace.getNode(Fqn.fromString("/one")).isDeleted()); List> mergedChildren = workspace.getNode(Fqn.fromString("/one")).getMergedChildren(); assertEquals(1, mergedChildren.get(1).size()); System.out.println(entry.getModifications()); assertTrue(!cache.exists("/one/two")); } public void testTransactionRemoveIntermediateNodeMethod() throws Exception { mgr.begin(); Transaction tx = mgr.getTransaction(); // inject InvocationContext cache.getInvocationContext().setTransaction(tx); cache.getInvocationContext().setGlobalTransaction(cache.getCurrentTransaction(tx, true)); Map temp = new HashMap(); temp.put("key1", "value"); cache.put("/one/two", temp); cache.removeNode("/one"); assertEquals(null, dummy.getCalledCommand()); TransactionTable table = cache.getTransactionTable(); GlobalTransaction gtx = table.get(tx); OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table.get(gtx); TransactionWorkspace workspace = entry.getTransactionWorkSpace(); mgr.commit(); //assert what should be the results of our call assertEquals(3, workspace.getNodes().size()); assertNotNull(workspace.getNode(Fqn.fromString("/one/two"))); assertEquals(true, workspace.getNode(Fqn.fromString("/one/two")).isDeleted()); assertNotNull(workspace.getNode(Fqn.fromString("/one"))); assertEquals(true, workspace.getNode(Fqn.fromString("/one")).isDeleted()); assertEquals(0, workspace.getNode(Fqn.fromString("/one")).getMergedChildren().get(0).size()); assertTrue(!cache.exists("/one/two")); assert 2 == dummy.getAllCalled().size(); assert dummy.getAllCalled().contains(CommitCommand.class); assert dummy.getAllCalled().contains(OptimisticPrepareCommand.class); } public void testTransactionRemoveTwiceMethod() throws Exception { mgr.begin(); Transaction tx = mgr.getTransaction(); // inject InvocationContext cache.getInvocationContext().setTransaction(tx); cache.getInvocationContext().setGlobalTransaction(cache.getCurrentTransaction(tx, true)); Map temp = new HashMap(); temp.put("key1", "value"); cache.put("/one/two", temp); // get the transaction stuff TransactionTable table = cache.getTransactionTable(); GlobalTransaction gtx = table.get(tx); OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table.get(gtx); TransactionWorkspace workspace = entry.getTransactionWorkSpace(); workspace.getNode(Fqn.fromString("/one")); workspace.getNode(Fqn.fromString("/one/two")); cache.removeNode("/one"); assertNotNull(workspace.getNode(Fqn.fromString("/one/two"))); assertEquals(true, workspace.getNode(Fqn.fromString("/one/two")).isDeleted()); assertNotNull(workspace.getNode(Fqn.fromString("/one"))); assertEquals(true, workspace.getNode(Fqn.fromString("/one")).isDeleted()); //now put /one/two back in cache.removeNode("/one"); assertNotNull(workspace.getNode(Fqn.fromString("/one/two"))); assertEquals(true, workspace.getNode(Fqn.fromString("/one/two")).isDeleted()); assertNotNull(workspace.getNode(Fqn.fromString("/one"))); assertEquals(true, workspace.getNode(Fqn.fromString("/one")).isDeleted()); assertEquals(null, dummy.getCalledCommand()); mgr.commit(); //assert what should be the results of our call assertEquals(3, workspace.getNodes().size()); assertTrue(!cache.exists("/one/two")); assert 2 == dummy.getAllCalled().size(); assert dummy.getAllCalled().contains(CommitCommand.class); assert dummy.getAllCalled().contains(OptimisticPrepareCommand.class); } public void testTransactionRemovePutNodeMethod() throws Exception { mgr.begin(); Transaction tx = mgr.getTransaction(); // inject InvocationContext cache.getInvocationContext().setTransaction(tx); cache.getInvocationContext().setGlobalTransaction(cache.getCurrentTransaction(tx, true)); Map temp = new HashMap(); temp.put("key1", "value"); cache.put("/one/two", temp); // get the transaction stuff TransactionTable table = cache.getTransactionTable(); GlobalTransaction gtx = table.get(tx); OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table.get(gtx); TransactionWorkspace workspace = entry.getTransactionWorkSpace(); Node one = workspace.getNode(Fqn.fromString("/one")); Node two = workspace.getNode(Fqn.fromString("/one/two")); cache.removeNode("/one"); assertNotNull(workspace.getNode(Fqn.fromString("/one/two"))); assertEquals(true, workspace.getNode(Fqn.fromString("/one/two")).isDeleted()); assertNotNull(workspace.getNode(Fqn.fromString("/one"))); assertEquals(true, workspace.getNode(Fqn.fromString("/one")).isDeleted()); //now put /one/two back in cache.put("/one/two", temp); WorkspaceNode oneAfter = workspace.getNode(Fqn.fromString("/one")); WorkspaceNode twoAfter = workspace.getNode(Fqn.fromString("/one/two")); assertSame(one, oneAfter); assertEquals(false, oneAfter.isDeleted()); assertSame(two, twoAfter); assertEquals(false, twoAfter.isDeleted()); assertEquals(null, dummy.getCalledCommand()); mgr.commit(); //assert what should be the results of our call assertEquals(3, workspace.getNodes().size()); assert 2 == dummy.getAllCalled().size(); assert dummy.getAllCalled().contains(CommitCommand.class); assert dummy.getAllCalled().contains(OptimisticPrepareCommand.class); assertEquals(2, listener.getNodesAdded()); } public void testTransactionRemovePutkeyValMethod() throws Exception { mgr.begin(); Transaction tx = mgr.getTransaction(); // inject InvocationContext cache.getInvocationContext().setTransaction(tx); cache.getInvocationContext().setGlobalTransaction(cache.getCurrentTransaction(tx, true)); Map temp = new HashMap(); temp.put("key1", "value"); cache.put("/one/two", temp); // get the transaction stuff TransactionTable table = cache.getTransactionTable(); GlobalTransaction gtx = table.get(tx); OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table.get(gtx); TransactionWorkspace workspace = entry.getTransactionWorkSpace(); Node one = workspace.getNode(Fqn.fromString("/one")); Node two = workspace.getNode(Fqn.fromString("/one/two")); cache.removeNode("/one"); assertNotNull(workspace.getNode(Fqn.fromString("/one/two"))); assertEquals(true, workspace.getNode(Fqn.fromString("/one/two")).isDeleted()); assertNotNull(workspace.getNode(Fqn.fromString("/one"))); assertEquals(true, workspace.getNode(Fqn.fromString("/one")).isDeleted()); //now put /one back in cache.put(Fqn.fromString("/one"), "key1", "value2"); WorkspaceNode oneAfter = workspace.getNode(Fqn.fromString("/one")); WorkspaceNode twoAfter = workspace.getNode(Fqn.fromString("/one/two")); assertSame(one, oneAfter); assertEquals(false, oneAfter.isDeleted()); assertEquals(two, twoAfter); assertEquals(true, twoAfter.isDeleted()); assertEquals(null, dummy.getCalledCommand()); mgr.commit(); assertEquals("value2", workspace.getNode(Fqn.fromString("/one")).get("key1")); //assert what should be the results of our call assertEquals(3, workspace.getNodes().size()); assertTrue(!cache.exists("/one/two")); assert 2 == dummy.getAllCalled().size(); assert dummy.getAllCalled().contains(CommitCommand.class); assert dummy.getAllCalled().contains(OptimisticPrepareCommand.class); assertEquals(2, listener.getNodesAdded()); } public void testTransactionRemoveSubNodeMethod() throws Exception { mgr.begin(); Transaction tx = mgr.getTransaction(); // inject InvocationContext cache.getInvocationContext().setTransaction(tx); cache.getInvocationContext().setGlobalTransaction(cache.getCurrentTransaction(tx, true)); Map temp = new HashMap(); temp.put("key1", "value"); cache.put("/one/two", temp); // get the transaction stuff TransactionTable table = cache.getTransactionTable(); GlobalTransaction gtx = table.get(tx); OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table.get(gtx); TransactionWorkspace workspace = entry.getTransactionWorkSpace(); WorkspaceNode one = workspace.getNode(Fqn.fromString("/one")); assertEquals(1, one.getMergedChildren().get(0).size()); cache.removeNode("/one/two"); assertNotNull(workspace.getNode(Fqn.fromString("/one/two"))); assertEquals(true, workspace.getNode(Fqn.fromString("/one/two")).isDeleted()); assertNotNull(workspace.getNode(Fqn.fromString("/one"))); assertEquals(false, workspace.getNode(Fqn.fromString("/one")).isDeleted()); assertEquals(null, dummy.getCalledCommand()); mgr.commit(); assertEquals(1, workspace.getNode(Fqn.fromString("/one")).getMergedChildren().get(1).size()); //assert what should be the results of our call assertEquals(3, workspace.getNodes().size()); assertTrue(!cache.exists("/one/two")); assert 2 == dummy.getAllCalled().size(); assert dummy.getAllCalled().contains(CommitCommand.class); assert dummy.getAllCalled().contains(OptimisticPrepareCommand.class); assertEquals(2, listener.getNodesAdded()); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/optimistic/AbstractOptimisticTestCase.java0000644000175000017500000002717611017455042032743 0ustar twernertwerner/** * */ package org.jboss.cache.optimistic; import org.jboss.cache.Cache; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.commands.ReversibleCommand; import org.jboss.cache.commands.VersionedDataCommand; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.UnitTestCacheConfigurationFactory; import org.jboss.cache.factories.XmlConfigurationParser; import org.jboss.cache.interceptors.CacheMgmtInterceptor; import org.jboss.cache.interceptors.CallInterceptor; import org.jboss.cache.interceptors.NotificationInterceptor; import org.jboss.cache.interceptors.OptimisticLockingInterceptor; import org.jboss.cache.interceptors.OptimisticNodeInterceptor; import org.jboss.cache.interceptors.OptimisticValidatorInterceptor; import org.jboss.cache.interceptors.base.CommandInterceptor; import org.jboss.cache.loader.DummyInMemoryCacheLoader; import org.jboss.cache.loader.DummySharedInMemoryCacheLoader; import org.jboss.cache.lock.IsolationLevel; import org.jboss.cache.marshall.MethodCall; import org.jboss.cache.transaction.DummyTransactionManagerLookup; import org.jboss.cache.transaction.GlobalTransaction; import org.jboss.cache.transaction.TransactionSetup; import org.jboss.cache.xml.XmlHelper; import org.jgroups.Address; import org.testng.annotations.AfterMethod; import org.testng.annotations.Test; import org.w3c.dom.Element; import javax.transaction.SystemException; import javax.transaction.Transaction; import javax.transaction.TransactionManager; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.ObjectInput; import java.io.ObjectOutput; import java.util.LinkedList; import java.util.List; import java.util.Random; /** * @author manik */ @Test(groups = "functional") public class AbstractOptimisticTestCase { // some test data shared among all the test cases protected Fqn fqn = Fqn.fromString("/blah"); protected String key = "myKey", value = "myValue"; protected CacheSPI createCacheUnstarted() throws Exception { return createCacheUnstarted(true); } protected CacheSPI createCacheUnstarted(boolean optimistic) throws Exception { CacheSPI cache = (CacheSPI) new DefaultCacheFactory().createCache(UnitTestCacheConfigurationFactory.createConfiguration(Configuration.CacheMode.LOCAL), false); if (optimistic) cache.getConfiguration().setNodeLockingScheme("OPTIMISTIC"); return cache; } protected CacheSPI createCacheWithListener() throws Exception { return createCacheWithListener(new TestListener()); } protected CacheSPI createCacheWithListener(Object listener) throws Exception { CacheSPI cache = createCacheUnstarted(); cache.create(); cache.start(); cache.getNotifier().addCacheListener(listener); return cache; } /** * Returns a tree cache with passivation disabled in the loader. */ protected CacheSPI createCacheWithLoader() throws Exception { return createCacheWithLoader(false); } protected void setupTransactions(CacheSPI cache, Transaction tx) { cache.getInvocationContext().setTransaction(tx); GlobalTransaction gtx = cache.getCurrentTransaction(tx, true); cache.getInvocationContext().setGlobalTransaction(gtx); cache.getInvocationContext().setTransactionEntry(cache.getTransactionTable().get(gtx)); } protected CacheLoaderConfig getCacheLoaderConfig(boolean shared, boolean passivation) throws Exception { String xml = " \n" + " " + passivation + "\n" + " \n" + " " + shared + "\n" + " \n" + " " + (shared ? DummySharedInMemoryCacheLoader.class.getName() : DummyInMemoryCacheLoader.class.getName()) + "\n" + " \n" + " \n" + " false\n" + " " + (!shared) + "\n" + " false\n" + " \n" + " "; Element element = XmlHelper.stringToElement(xml); return XmlConfigurationParser.parseCacheLoaderConfig(element); } protected CacheSPI createCacheWithLoader(boolean passivationEnabled) throws Exception { CacheSPI cache = createCacheUnstarted(); Configuration c = cache.getConfiguration(); c.setCacheLoaderConfig(getCacheLoaderConfig(true, passivationEnabled)); cache.create(); cache.start(); return cache; } protected CacheSPI createCache() throws Exception { CacheSPI cache = createCacheUnstarted(); cache.create(); cache.start(); return cache; } protected void destroyCache(Cache c) { TestingUtil.killCaches(c); } protected CacheSPI createPessimisticCache() throws Exception { CacheSPI cache = (CacheSPI) new DefaultCacheFactory().createCache(false); Configuration c = cache.getConfiguration(); c.setClusterName("name"); c.setStateRetrievalTimeout(5000); c.setCacheMode(Configuration.CacheMode.REPL_SYNC); c.setIsolationLevel(IsolationLevel.REPEATABLE_READ); c.setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); cache.create(); cache.start(); return cache; } protected CacheSPI createPessimisticCacheLocal() throws Exception { CacheSPI cache = (CacheSPI) new DefaultCacheFactory().createCache(false); Configuration c = cache.getConfiguration(); c.setClusterName("name"); c.setStateRetrievalTimeout(5000); c.setCacheMode(Configuration.CacheMode.LOCAL); c.setIsolationLevel(IsolationLevel.REPEATABLE_READ); c.setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); cache.create(); cache.start(); return cache; } protected CacheSPI createReplicatedCache(Configuration.CacheMode mode) throws Exception { return createReplicatedCache("test", mode); } protected CacheSPI createReplicatedCache(String name, Configuration.CacheMode mode) throws Exception { return createReplicatedCache(name, mode, true); } protected CacheSPI createReplicatedCache(String name, Configuration.CacheMode mode, boolean start) throws Exception { CacheSPI cache = (CacheSPI) new DefaultCacheFactory().createCache(false); Configuration c = cache.getConfiguration(); c.setClusterName(name); c.setStateRetrievalTimeout(5000); c.setCacheMode(mode); if (mode == Configuration.CacheMode.REPL_SYNC) { // make sure commits and rollbacks are sync as well c.setSyncCommitPhase(true); c.setSyncRollbackPhase(true); } c.setNodeLockingScheme("OPTIMISTIC"); c.setTransactionManagerLookupClass(TransactionSetup.getManagerLookup()); if (start) { cache.create(); cache.start(); } return cache; } protected CacheSPI createReplicatedCacheWithLoader(boolean shared, Configuration.CacheMode cacheMode) throws Exception { return createReplicatedCacheWithLoader("temp-loader", shared, cacheMode); } protected CacheSPI createReplicatedCacheWithLoader(boolean shared) throws Exception { return createReplicatedCacheWithLoader("temp-loader", shared, Configuration.CacheMode.REPL_SYNC); } protected CacheSPI createReplicatedCacheWithLoader(String name, boolean shared) throws Exception { return createReplicatedCacheWithLoader(name, shared, Configuration.CacheMode.REPL_SYNC); } protected CacheSPI createReplicatedCacheWithLoader(String name, boolean shared, Configuration.CacheMode cacheMode) throws Exception { CacheSPI cache = (CacheSPI) new DefaultCacheFactory().createCache(false); Configuration c = cache.getConfiguration(); c.setClusterName(name); c.setStateRetrievalTimeout(5000); c.setCacheMode(cacheMode); c.setSyncCommitPhase(true); c.setSyncRollbackPhase(true); c.setNodeLockingScheme("OPTIMISTIC"); c.setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName()); c.setCacheLoaderConfig(getCacheLoaderConfig(shared, false)); cache.create(); cache.start(); return cache; } protected Random random = new Random(); protected void randomSleep(int min, int max) { long l = -1; while (l < min) l = random.nextInt(max); TestingUtil.sleepThread(l); } @AfterMethod(alwaysRun = true) public void tearDown() { TransactionManager mgr = TransactionSetup.getManager(); try { if (mgr.getTransaction() != null) { mgr.rollback(); } } catch (SystemException e) { // do nothing } } protected void setAlteredInterceptorChain(CommandInterceptor newLast, CacheSPI spi) { spi.removeInterceptor(CacheMgmtInterceptor.class); spi.removeInterceptor(NotificationInterceptor.class); spi.removeInterceptor(OptimisticLockingInterceptor.class); spi.removeInterceptor(OptimisticValidatorInterceptor.class); spi.removeInterceptor(CallInterceptor.class); spi.addInterceptor(newLast, OptimisticNodeInterceptor.class); } public abstract class ExceptionThread extends Thread { protected Exception exception; public void setException(Exception e) { exception = e; } public Exception getException() { return exception; } } protected List injectDataVersion(List modifications) { List newList = new LinkedList(); for (ReversibleCommand c : modifications) { if (c instanceof VersionedDataCommand) { ((VersionedDataCommand) c).setDataVersion(new DefaultDataVersion()); } // Object[] oa = c.getArgs(); // Object[] na = new Object[oa.length + 1]; // System.out.println("*** " + oa.length); // System.arraycopy(oa, 0, na, 0, oa.length); // na[oa.length] = new DefaultDataVersion(); // newList.add(MethodCallFactory.create(MethodDeclarations.getVersionedMethodId(c.getMethodId()), na)); } return modifications; } protected class DummyAddress implements Address { private static final long serialVersionUID = -2628268587640985944L; public int compareTo(Object arg0) { return 0; } public void readFrom(DataInputStream arg0) { } public void writeTo(DataOutputStream arg0) { } public void readExternal(ObjectInput arg0) { } public void writeExternal(ObjectOutput arg0) { } public int size() { return 0; } public boolean isMulticastAddress() { return false; } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/optimistic/VersioningOnReadTest.java0000644000175000017500000000630610735006726031552 0ustar twernertwernerpackage org.jboss.cache.optimistic; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.fail; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.Transaction; import javax.transaction.TransactionManager; /** * @author Manik Surtani */ @Test(groups = "functional") public class VersioningOnReadTest extends AbstractOptimisticTestCase { CacheSPI cache; Fqn fqn = Fqn.fromString("/a"); TransactionManager tm; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { cache = createCache(); tm = cache.getTransactionManager(); } @AfterMethod(alwaysRun = true) public void tearDown() { super.tearDown(); destroyCache(cache); } public void testUpdateOnWrite() throws Exception { cache.put(fqn, "k", "v"); assertEquals("v", cache.get(fqn, "k")); // now start a tx to mod the node tm.begin(); cache.put(fqn, "k", "v2"); // suspend the tx Transaction tx = tm.suspend(); // now modify the node cache.put(fqn, "k", "v3"); // resume the tx tm.resume(tx); try { tm.commit(); fail("Should have failed with a data version mismatch"); } catch (Exception e) { // do nothing } } public void testUpdateOnRemove() throws Exception { cache.put(fqn, "k", "v"); assertEquals("v", cache.get(fqn, "k")); // now start a tx to mod the node tm.begin(); cache.remove(fqn, "k"); // suspend the tx Transaction tx = tm.suspend(); // now modify the node cache.put(fqn, "k", "v3"); // resume the tx tm.resume(tx); try { tm.commit(); fail("Should have failed with a data version mismatch"); } catch (Exception e) { // do nothing } } public void testUpdateOnRemoveNode() throws Exception { cache.put(fqn, "k", "v"); assertEquals("v", cache.get(fqn, "k")); // now start a tx to mod the node tm.begin(); cache.removeNode(fqn); // suspend the tx Transaction tx = tm.suspend(); // now modify the node cache.put(fqn, "k", "v3"); // resume the tx tm.resume(tx); try { tm.commit(); fail("Should have failed with a data version mismatch"); } catch (Exception e) { // do nothing } } public void testUpdateOnRead() throws Exception { cache.put(fqn, "k", "v"); assertEquals("v", cache.get(fqn, "k")); // now start a tx to mod the node tm.begin(); cache.get(fqn, "k"); // suspend the tx Transaction tx = tm.suspend(); // now modify the node cache.put(fqn, "k", "v3"); // resume the tx tm.resume(tx); // now put some other stuff elsewhere cache.put(Fqn.fromString("/b"), "k", "v"); // this should succeed since there is no contention on writing to /a tm.commit(); } } ././@LongLink0000000000000000000000000000014500000000000011565 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/optimistic/OptimisticLockInterceptorTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/optimistic/OptimisticLockInterceptorTest.java0000644000175000017500000001377211017455042033510 0ustar twernertwernerpackage org.jboss.cache.optimistic; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.NodeSPI; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.commands.VisitableCommand; import org.jboss.cache.interceptors.OptimisticInterceptor; import org.jboss.cache.interceptors.OptimisticLockingInterceptor; import org.jboss.cache.lock.LockType; import static org.jboss.cache.lock.LockType.READ; import static org.jboss.cache.lock.LockType.WRITE; import org.jboss.cache.lock.NodeLock; import org.testng.AssertJUnit; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.TransactionManager; import java.util.HashMap; import java.util.Iterator; import java.util.Map; /** * // Test for JBCACHE-1228 * * @author Manik Surtani (manik@jboss.org) */ @Test(groups = "functional") public class OptimisticLockInterceptorTest extends AbstractOptimisticTestCase { private CacheSPI cache; private LockReportInterceptor lri; private Fqn parent = Fqn.fromString("/parent"); private Fqn child = Fqn.fromString("/parent/child"); private TransactionManager tm; @BeforeMethod protected void setUp() throws Exception { cache = createCache(); lri = new LockReportInterceptor(); TestingUtil.extractComponentRegistry(cache).wireDependencies(lri); TestingUtil.injectInterceptor(cache, lri, OptimisticLockingInterceptor.class); cache.put(child, "key", "value"); tm = cache.getTransactionManager(); } @AfterMethod public void tearDown() { cache.stop(); } public void testPut() throws Exception { tm.begin(); cache.put(child, "key2", "value2"); lri.reset(); lri.expectsReadLock(Fqn.ROOT); lri.expectsReadLock(parent); lri.expectsWriteLock(child); tm.commit(); lri.assertReceivedExpectedLocks(); assertNoStaleLocks(); } public void testGet() throws Exception { tm.begin(); cache.get(child, "key2"); lri.reset(); // nothing is stale, expecting nothing here. tm.commit(); lri.assertReceivedExpectedLocks(); assertNoStaleLocks(); } public void testRemove() throws Exception { tm.begin(); cache.remove(child, "key2"); lri.reset(); lri.expectsReadLock(Fqn.ROOT); lri.expectsReadLock(parent); lri.expectsWriteLock(child); tm.commit(); lri.assertReceivedExpectedLocks(); assertNoStaleLocks(); } public void testPutLockParentForCIR() throws Exception { cache.getConfiguration().setLockParentForChildInsertRemove(true); cache.removeNode(parent); cache.put(parent, "k", "v"); tm.begin(); cache.put(child, "key2", "value2"); lri.reset(); lri.expectsReadLock(Fqn.ROOT); lri.expectsWriteLock(parent); lri.expectsWriteLock(child); tm.commit(); lri.assertReceivedExpectedLocks(); assertNoStaleLocks(); } public void testGetLockParentForCIR() throws Exception { cache.getConfiguration().setLockParentForChildInsertRemove(true); tm.begin(); cache.get(child, "key2"); lri.reset(); // nothing is stale, expecting nothing here. tm.commit(); lri.assertReceivedExpectedLocks(); assertNoStaleLocks(); } public void testRemoveLockParentForCIR() throws Exception { cache.getConfiguration().setLockParentForChildInsertRemove(true); tm.begin(); cache.removeNode(child); lri.reset(); lri.expectsReadLock(Fqn.ROOT); lri.expectsWriteLock(parent); lri.expectsWriteLock(child); tm.commit(); lri.assertReceivedExpectedLocks(); assertNoStaleLocks(); } public void testPutNodeNotExists() throws Exception { cache.removeNode(Fqn.ROOT); tm.begin(); cache.put(child, "key2", "value2"); lri.reset(); lri.expectsReadLock(Fqn.ROOT); lri.expectsWriteLock(parent); lri.expectsWriteLock(child); tm.commit(); lri.assertReceivedExpectedLocks(); assertNoStaleLocks(); } public void testGetNodeNotExists() throws Exception { cache.removeNode(Fqn.ROOT); tm.begin(); cache.get(child, "key2"); lri.reset(); // nothing is stale, expecting nothing here. tm.commit(); lri.assertReceivedExpectedLocks(); assertNoStaleLocks(); } public void testRemoveNodeNotExists() throws Exception { cache.removeNode(Fqn.ROOT); tm.begin(); cache.remove(child, "key2"); lri.reset(); // nothing is stale, expecting nothing here. tm.commit(); lri.assertReceivedExpectedLocks(); assertNoStaleLocks(); } private void assertNoStaleLocks() { assert cache.getNumberOfLocksHeld() == 0; } } class LockReportInterceptor extends OptimisticInterceptor { private Map expected = new HashMap(); private Map actual = new HashMap(); void reset() { expected.clear(); actual.clear(); } void assertReceivedExpectedLocks() { AssertJUnit.assertEquals(expected, actual); } void expectsReadLock(Fqn f) { expected.put(f, READ); } void expectsWriteLock(Fqn f) { expected.put(f, WRITE); } @Override public Object handleDefault(InvocationContext ctx, VisitableCommand command) throws Throwable { TransactionWorkspace w = getTransactionWorkspace(ctx); Map nodeMap = w.getNodes(); for (Iterator i = nodeMap.keySet().iterator(); i.hasNext();) { WorkspaceNode wn = (WorkspaceNode) nodeMap.get(i.next()); NodeSPI n = wn.getNode(); NodeLock lock = n.getLock(); if (lock.isLocked()) { actual.put(n.getFqn(), lock.isReadLocked() ? READ : WRITE); } } return invokeNextInterceptor(ctx, command); } } ././@LongLink0000000000000000000000000000017200000000000011565 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/optimistic/ThreadedOptimisticCreateIfNotExistsInterceptorTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/optimistic/ThreadedOptimisticCreateIfNotExist0000755000175000017500000001327511017455042033443 0ustar twernertwerner/* * Created on 17-Feb-2005 * * * */ package org.jboss.cache.optimistic; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.interceptors.OptimisticCreateIfNotExistsInterceptor; import org.jboss.cache.interceptors.base.CommandInterceptor; import org.jboss.cache.loader.SamplePojo; import org.jboss.cache.transaction.DummyTransactionManager; import org.jboss.cache.transaction.OptimisticTransactionEntry; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertTrue; import org.testng.annotations.Test; import javax.transaction.Transaction; import javax.transaction.TransactionManager; /** * @author xenephon */ @Test(groups = "functional") public class ThreadedOptimisticCreateIfNotExistsInterceptorTest extends AbstractOptimisticTestCase { protected synchronized void setTransactionsInInvocationCtx(TransactionManager mgr, CacheSPI cache) throws Exception { cache.getInvocationContext().setTransaction(mgr.getTransaction()); cache.getInvocationContext().setGlobalTransaction(cache.getCurrentTransaction()); } protected void resetInvocationCtx(CacheSPI cache) { cache.getInvocationContext().setTransaction(null); cache.getInvocationContext().setGlobalTransaction(null); } public void testDifferentTransactions() throws Exception { int numThreads = 100; final int minSleep = 0; final int maxSleep = 1000; TestListener listener = new TestListener(); final CacheSPI cache = createCacheWithListener(listener); CommandInterceptor interceptor = new OptimisticCreateIfNotExistsInterceptor(); CommandInterceptor dummy = new MockInterceptor(); interceptor.setNext(dummy); TestingUtil.replaceInterceptorChain(cache, interceptor); // should just be the root node assertEquals(0, cache.getNumberOfNodes()); Runnable run = new Runnable() { public void run() { try { //start a new transaction in this thread DummyTransactionManager mgr = DummyTransactionManager.getInstance(); mgr.begin(); setTransactionsInInvocationCtx(mgr, cache); SamplePojo pojo = new SamplePojo(21, "test"); cache.put("/one", "key1", pojo); randomSleep(minSleep, maxSleep); cache.put("/one/two", "key2", pojo); OptimisticTransactionEntry entry = (OptimisticTransactionEntry) cache.getTransactionTable().get(cache.getCurrentTransaction()); assertEquals(3, entry.getTransactionWorkSpace().getNodes().size()); assertTrue(entry.getTransactionWorkSpace().getNode(Fqn.fromString("/")) != null); assertTrue(entry.getTransactionWorkSpace().getNode(Fqn.fromString("/one")) != null); assertTrue(entry.getTransactionWorkSpace().getNode(Fqn.fromString("/one/two")) != null); mgr.commit(); resetInvocationCtx(cache); } catch (Exception e) { e.printStackTrace(); } } }; Thread[] threads = new Thread[numThreads]; for (int i = 0; i < numThreads; i++) { Thread t = new Thread(run); t.start(); threads[i] = t; } for (int i = 0; i < numThreads; i++) { threads[i].join(); } cache.stop(); } public void testDifferentThreadsSameTransaction() throws Exception { int numThreads = 100; final int minSleep = 0; final int maxSleep = 500; TestListener listener = new TestListener(); final CacheSPI cache = createCacheWithListener(listener); CommandInterceptor interceptor = new OptimisticCreateIfNotExistsInterceptor(); CommandInterceptor dummy = new MockInterceptor(); interceptor.setNext(dummy); TestingUtil.replaceInterceptorChain(cache, interceptor); final DummyTransactionManager mgr = DummyTransactionManager.getInstance(); mgr.begin(); final Transaction tx = mgr.getTransaction(); Runnable run = new Runnable() { public void run() { try { //start a new transaction in this thread mgr.setTransaction(tx); SamplePojo pojo = new SamplePojo(21, "test"); setTransactionsInInvocationCtx(mgr, cache); cache.put("/one", "key1", pojo); OptimisticTransactionEntry entry = (OptimisticTransactionEntry) cache.getTransactionTable().get(cache.getCurrentTransaction()); randomSleep(minSleep, maxSleep); cache.put("/one/two", "key2", pojo); assertEquals(3, entry.getTransactionWorkSpace().getNodes().size()); assertTrue(entry.getTransactionWorkSpace().getNode(Fqn.fromString("/")) != null); assertTrue(entry.getTransactionWorkSpace().getNode(Fqn.fromString("/one")) != null); assertTrue(entry.getTransactionWorkSpace().getNode(Fqn.fromString("/one/two")) != null); } catch (Exception e) { e.printStackTrace(); } finally { resetInvocationCtx(cache); } } }; Thread[] threads = new Thread[numThreads]; for (int i = 0; i < numThreads; i++) { Thread t = new Thread(run); t.start(); threads[i] = t; } for (int i = 0; i < numThreads; i++) { threads[i].join(); } mgr.commit(); TestingUtil.sleepThread((long) 4000); cache.stop(); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/optimistic/TestListener.java0000755000175000017500000000204410640763624030123 0ustar twernertwerner/* * Created on 23-Feb-2005 * * * */ package org.jboss.cache.optimistic; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.notifications.annotation.CacheListener; import org.jboss.cache.notifications.annotation.NodeCreated; import org.jboss.cache.notifications.event.NodeEvent; /** * @author swoodcock */ @CacheListener public class TestListener { Log log = LogFactory.getLog(getClass()); private int nodesAdded = 0; /* (non-Javadoc) * @see org.jboss.cache.TreeCacheListener#nodeCreated(org.jboss.cache.Fqn) */ @NodeCreated public synchronized void nodeCreated(NodeEvent e) { if (e.isPre()) { nodesAdded++; log.info("DataNode created " + e.getFqn()); } } /** * @return Returns the nodesAdded. */ public int getNodesAdded() { return nodesAdded; } /** * @param nodesAdded The nodesAdded to set. */ public void setNodesAdded(int nodesAdded) { this.nodesAdded = nodesAdded; } } ././@LongLink0000000000000000000000000000014500000000000011565 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/optimistic/AsyncFullStackInterceptorTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/optimistic/AsyncFullStackInterceptorTest.java0000644000175000017500000003423211020007335033423 0ustar twernertwernerpackage org.jboss.cache.optimistic; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.commands.write.RemoveNodeCommand; import org.jboss.cache.commands.write.PutKeyValueCommand; import org.jboss.cache.config.Configuration; import org.jboss.cache.loader.SamplePojo; import org.jboss.cache.lock.LockManager; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.util.internals.ReplicationListener; import org.jboss.cache.transaction.GlobalTransaction; import org.jboss.cache.transaction.OptimisticTransactionEntry; import org.jboss.cache.transaction.TransactionTable; import static org.testng.AssertJUnit.*; import org.testng.annotations.Test; import javax.transaction.RollbackException; import javax.transaction.Transaction; import javax.transaction.TransactionManager; /** * @author xenephon */ @Test(groups = {"functional", "transaction"}) public class AsyncFullStackInterceptorTest extends AbstractOptimisticTestCase { /** * @param name */ private int groupIncreaser = 0; public void testSingleInstanceRollback() throws Exception { groupIncreaser++; CacheSPI cache = createAsyncReplicatedCache(); TransactionManager mgr = cache.getConfiguration().getRuntimeConfig().getTransactionManager(); assertNull(mgr.getTransaction()); mgr.begin(); assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache.getTransactionTable().getNumLocalTransactions()); SamplePojo pojo = new SamplePojo(21, "test"); cache.put("/one/two", "key1", pojo); mgr.rollback(); assertNull(mgr.getTransaction()); assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache.getTransactionTable().getNumLocalTransactions()); assertEquals(false, cache.exists(Fqn.fromString("/one/two"))); assertNull(cache.getNode("/one")); destroyCache(cache); } public void testSingleInstanceDuplicateCommit() throws Exception { groupIncreaser++; CacheSPI cache = createAsyncReplicatedCache(); TransactionManager mgr = cache.getConfiguration().getRuntimeConfig().getTransactionManager(); LockManager lockManager = TestingUtil.extractLockManager(cache); assertNull(mgr.getTransaction()); mgr.begin(); assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache.getTransactionTable().getNumLocalTransactions()); SamplePojo pojo = new SamplePojo(21, "test"); cache.put("/one/two", "key1", pojo); mgr.commit(); assertNull(mgr.getTransaction()); boolean fail = false; try { mgr.commit(); } catch (Exception e) { fail = true; } assertEquals(true, fail); assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache.getTransactionTable().getNumLocalTransactions()); assertTrue(cache.exists(Fqn.fromString("/one/two"))); assertNotNull(cache.getNode("/one")); assertEquals(false, lockManager.isLocked(cache.getRoot())); assertEquals(false, lockManager.isLocked(cache.getNode(Fqn.fromString("/one")))); assertEquals(false, lockManager.isLocked(cache.getNode(Fqn.fromString("/one/two")))); assertNotNull(cache.getNode("/one/two")); assertNotNull(cache.get(Fqn.fromString("/one/two"), "key1")); destroyCache(cache); } public void testValidationFailCommit() throws Exception { groupIncreaser++; CacheSPI cache = createAsyncReplicatedCache(); LockManager lockManager = TestingUtil.extractLockManager(cache); TransactionManager mgr = cache.getConfiguration().getRuntimeConfig().getTransactionManager(); assertNull(mgr.getTransaction()); mgr.begin(); Transaction tx = mgr.getTransaction(); assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache.getTransactionTable().getNumLocalTransactions()); SamplePojo pojo = new SamplePojo(21, "test"); cache.put("/one/two", "key1", pojo); mgr.suspend(); assertEquals(1, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(1, cache.getTransactionTable().getNumLocalTransactions()); assertNull(mgr.getTransaction()); mgr.begin(); SamplePojo pojo2 = new SamplePojo(22, "test2"); cache.put("/one/two", "key1", pojo2); mgr.commit(); assertEquals(1, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(1, cache.getTransactionTable().getNumLocalTransactions()); mgr.resume(tx); // one-phase-commits wont throw an exception on failure. try { mgr.commit(); assert false : "Expecting an exception"; } catch (RollbackException expected) { // this is good } assertNull(mgr.getTransaction()); assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache.getTransactionTable().getNumLocalTransactions()); assertTrue(cache.exists(Fqn.fromString("/one/two"))); assertNotNull(cache.getNode("/one")); assertEquals(false, lockManager.isLocked(cache.getRoot())); assertEquals(false, lockManager.isLocked(cache.getNode("/one"))); assertEquals(false, lockManager.isLocked(cache.getNode("/one/two"))); assertNotNull(cache.getNode("/one").getChild("two")); assertEquals(pojo2, cache.get(Fqn.fromString("/one/two"), "key1")); destroyCache(cache); } public void test2InstanceCommit() throws Exception { groupIncreaser++; CacheSPI cache = createAsyncReplicatedCache(); CacheSPI cache2 = createAsyncReplicatedCache(); ReplicationListener replListener2 = new ReplicationListener(cache2); LockManager lockManager = TestingUtil.extractLockManager(cache); LockManager lockManager2 = TestingUtil.extractLockManager(cache2); TransactionManager mgr = cache.getConfiguration().getRuntimeConfig().getTransactionManager(); assertNull(mgr.getTransaction()); mgr.begin(); assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache.getTransactionTable().getNumLocalTransactions()); SamplePojo pojo = new SamplePojo(21, "test"); replListener2.expectAny(); cache.put("/one/two", "key1", pojo); mgr.commit(); replListener2.waitForReplicationToOccur(1000); // cache asserts assertNull(mgr.getTransaction()); assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache.getTransactionTable().getNumLocalTransactions()); assertTrue(cache.exists(Fqn.fromString("/one/two"))); assertNotNull(cache.getNode("/one")); assertNotNull(cache.get(Fqn.fromString("/one/two"), "key1")); assertTrue(cache.exists(Fqn.fromString("/one/two"))); assertNotNull(cache.getNode("/one")); assertEquals(false, lockManager.isLocked(cache.getRoot())); assertEquals(false, lockManager.isLocked(cache.getNode("/one"))); assertEquals(false, lockManager.isLocked(cache.getNode("/one/two"))); assertNotNull(cache.getNode("/one").getChild("two")); assertNotNull(cache.get(Fqn.fromString("/one/two"), "key1")); // cache2 asserts assertEquals(0, cache2.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache2.getTransactionTable().getNumLocalTransactions()); assertTrue(cache2.exists(Fqn.fromString("/one/two"))); assertNotNull(cache2.getRoot().getChild("one")); assertNotNull(cache2.get(Fqn.fromString("/one/two"), "key1")); assertTrue(cache2.exists(Fqn.fromString("/one/two"))); assertNotNull(cache2.getRoot().getChild("one")); assertEquals(false, lockManager2.isLocked(cache2.getRoot())); assertEquals(false, lockManager2.isLocked(cache2.getNode("/one"))); assertEquals(false, lockManager2.isLocked(cache2.getNode("/one/two"))); assertNotNull(cache2.getNode("/one").getChild("two")); assertNotNull(cache2.get(Fqn.fromString("/one/two"), "key1")); destroyCache(cache); destroyCache(cache2); } public void test2InstanceRemove() throws Exception { groupIncreaser++; CacheSPI cache = createAsyncReplicatedCache(); ReplicationListener replListener = new ReplicationListener(cache); CacheSPI cache2 = createAsyncReplicatedCache(); ReplicationListener replListener2 = new ReplicationListener(cache2); LockManager lockManager = TestingUtil.extractLockManager(cache); LockManager lockManager2 = TestingUtil.extractLockManager(cache2); TransactionManager mgr = cache.getConfiguration().getRuntimeConfig().getTransactionManager(); assertNull(mgr.getTransaction()); mgr.begin(); assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache.getTransactionTable().getNumLocalTransactions()); SamplePojo pojo = new SamplePojo(21, "test"); replListener2.expectAny(); cache.put("/one/two", "key1", pojo); mgr.commit(); replListener2.waitForReplicationToOccur(1000); // cache asserts assertNull(mgr.getTransaction()); assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache.getTransactionTable().getNumLocalTransactions()); assertTrue(cache.exists(Fqn.fromString("/one/two"))); assertNotNull(cache.getNode("/one")); assertNotNull(cache.get(Fqn.fromString("/one/two"), "key1")); assertTrue(cache.exists(Fqn.fromString("/one/two"))); assertNotNull(cache.getNode("/one")); assertEquals(false, lockManager.isLocked(cache.getRoot())); assertEquals(false, lockManager.isLocked(cache.getNode("/one"))); assertEquals(false, lockManager.isLocked(cache.getNode("/one/two"))); assertNotNull(cache.getNode("/one").getChild("two")); assertNotNull(cache.get(Fqn.fromString("/one/two"), "key1")); // cache2 asserts assertEquals(0, cache2.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache2.getTransactionTable().getNumLocalTransactions()); assertTrue(cache2.exists(Fqn.fromString("/one/two"))); assertNotNull(cache2.getRoot().getChild("one")); assertNotNull(cache2.get(Fqn.fromString("/one/two"), "key1")); assertTrue(cache2.exists(Fqn.fromString("/one/two"))); assertNotNull(cache2.getRoot().getChild("one")); assertEquals(false, lockManager2.isLocked(cache2.getRoot())); assertEquals(false, lockManager2.isLocked(cache2.getNode("/one"))); assertEquals(false, lockManager2.isLocked(cache2.getNode("/one/two"))); assertNotNull(cache2.getNode("/one").getChild("two")); assertNotNull(cache2.get(Fqn.fromString("/one/two"), "key1")); replListener2.expect(RemoveNodeCommand.class); cache.removeNode("/one/two"); replListener2.waitForReplicationToOccur(1000); assertEquals(false, cache.exists("/one/two")); assertEquals(null, cache.get("/one/two", "key1")); assertEquals(false, cache2.exists("/one/two")); assertEquals(null, cache2.get("/one/two", "key1")); destroyCache(cache); destroyCache(cache2); } public void testValidationFailCommit2Instances() throws Exception { groupIncreaser++; CacheSPI cache = createAsyncReplicatedCache(); ReplicationListener replListener = new ReplicationListener(cache); CacheSPI cache2 = createAsyncReplicatedCache(); LockManager lockManager = TestingUtil.extractLockManager(cache); TransactionManager mgr = cache.getConfiguration().getRuntimeConfig().getTransactionManager(); assertNull(mgr.getTransaction()); mgr.begin(); Transaction tx = mgr.getTransaction(); assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache.getTransactionTable().getNumLocalTransactions()); SamplePojo pojo = new SamplePojo(21, "test"); cache.put("/one/two", "key1", pojo); mgr.suspend(); assertEquals(1, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(1, cache.getTransactionTable().getNumLocalTransactions()); GlobalTransaction gtx = cache.getCurrentTransaction(tx, true); TransactionTable table = cache.getTransactionTable(); OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table.get(gtx); assertEquals(3, entry.getTransactionWorkSpace().getNodes().size()); assertNull(mgr.getTransaction()); mgr.begin(); SamplePojo pojo2 = new SamplePojo(22, "test2"); cache2.put("/one/two", "key1", pojo2); mgr.commit(); // let async calls propagate TestingUtil.sleepThread((long) 1000); assertEquals(1, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(1, cache.getTransactionTable().getNumLocalTransactions()); mgr.resume(tx); try { mgr.commit(); assert false : "Expecting an exception"; } catch (RollbackException expected) { // this is good } assertNull(mgr.getTransaction()); assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache.getTransactionTable().getNumLocalTransactions()); assertEquals(0, entry.getTransactionWorkSpace().getNodes().size()); assertTrue(cache.exists(Fqn.fromString("/one/two"))); assertNotNull(cache.getNode("/one")); assertEquals(false, lockManager.isLocked(cache.getRoot())); assertEquals(false, lockManager.isLocked(cache.getNode("/one"))); assertEquals(false, lockManager.isLocked(cache.getNode("/one/two"))); assertNotNull(cache.getNode("/one").getChild("two")); assertEquals(pojo2, cache.get(Fqn.fromString("/one/two"), "key1")); destroyCache(cache); destroyCache(cache2); } @SuppressWarnings("unchecked") protected CacheSPI createAsyncReplicatedCache() throws Exception { return createReplicatedCache("temp" + groupIncreaser, Configuration.CacheMode.REPL_ASYNC); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/optimistic/FullStackInterceptorTest.java0000644000175000017500000006673611017455776032471 0ustar twernertwernerpackage org.jboss.cache.optimistic; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.config.Configuration; import org.jboss.cache.loader.SamplePojo; import org.jboss.cache.lock.LockManager; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.transaction.GlobalTransaction; import org.jboss.cache.transaction.OptimisticTransactionEntry; import org.jboss.cache.transaction.TransactionTable; import static org.testng.AssertJUnit.*; import org.testng.annotations.Test; import javax.transaction.RollbackException; import javax.transaction.Transaction; import javax.transaction.TransactionManager; import java.util.Set; /** * @author xenephon */ @Test(groups = "functional") public class FullStackInterceptorTest extends AbstractOptimisticTestCase { private Log log = LogFactory.getLog(FullStackInterceptorTest.class); private int groupIncreaser = 0; public void testLocalTransaction() throws Exception { CacheSPI cache = createCacheWithListener(); TransactionManager mgr = cache.getConfiguration().getRuntimeConfig().getTransactionManager(); assertNull(mgr.getTransaction()); mgr.begin(); assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache.getTransactionTable().getNumLocalTransactions()); SamplePojo pojo = new SamplePojo(21, "test"); cache.put("/one/two", "key1", pojo); mgr.commit(); assertNull(mgr.getTransaction()); assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache.getTransactionTable().getNumLocalTransactions()); assertTrue(cache.exists(Fqn.fromString("/one/two"))); assertNotNull(cache.getRoot().getChild("one")); assertNotNull(cache.get(Fqn.fromString("/one/two"), "key1")); // flesh this out a bit more destroyCache(cache); } public void testNoLocalTransaction() throws Exception { TestListener listener = new TestListener(); CacheSPI cache = createCacheWithListener(listener); LockManager lockManager = TestingUtil.extractLockManager(cache); TransactionManager mgr = cache.getConfiguration().getRuntimeConfig().getTransactionManager(); assertNull(mgr.getTransaction()); assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache.getTransactionTable().getNumLocalTransactions()); SamplePojo pojo = new SamplePojo(21, "test"); cache.put("/one/two", "key1", pojo); assertNull(mgr.getTransaction()); assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache.getTransactionTable().getNumLocalTransactions()); assertTrue(cache.exists(Fqn.fromString("/one/two"))); assertNotNull(cache.getRoot().getChild("one")); assertEquals(false, lockManager.isLocked(cache.getRoot())); assertEquals(false, lockManager.isLocked(cache.getNode("/one"))); assertEquals(false, lockManager.isLocked(cache.getNode("/one/two"))); assertNotNull(cache.getNode("/one").getChild("two")); assertNotNull(cache.get(Fqn.fromString("/one/two"), "key1")); assertEquals(2, listener.getNodesAdded()); assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions()); destroyCache(cache); } public void testSingleInstanceCommit() throws Exception { groupIncreaser++; CacheSPI cache = createCacheWithListener(); LockManager lockManager = TestingUtil.extractLockManager(cache); TransactionManager mgr = cache.getConfiguration().getRuntimeConfig().getTransactionManager(); assertNull(mgr.getTransaction()); mgr.begin(); assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache.getTransactionTable().getNumLocalTransactions()); SamplePojo pojo = new SamplePojo(21, "test"); cache.put("/one/two", "key1", pojo); mgr.commit(); assertNull(mgr.getTransaction()); assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache.getTransactionTable().getNumLocalTransactions()); assertTrue(cache.exists(Fqn.fromString("/one/two"))); assertNotNull(cache.getRoot().getChild("one")); assertNotNull(cache.get(Fqn.fromString("/one/two"), "key1")); assertTrue(cache.exists(Fqn.fromString("/one/two"))); assertNotNull(cache.getRoot().getChild("one")); assertEquals(false, lockManager.isLocked(cache.getRoot())); assertEquals(false, lockManager.isLocked(cache.getNode("/one"))); assertEquals(false, lockManager.isLocked(cache.getNode("/one/two"))); assertNotNull(cache.getNode("/one").getChild("two")); assertNotNull(cache.get(Fqn.fromString("/one/two"), "key1")); destroyCache(cache); } public void testSingleInstanceRollback() throws Exception { groupIncreaser++; CacheSPI cache = createSyncReplicatedCache(); TransactionManager mgr = cache.getConfiguration().getRuntimeConfig().getTransactionManager(); assertNull(mgr.getTransaction()); mgr.begin(); assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache.getTransactionTable().getNumLocalTransactions()); SamplePojo pojo = new SamplePojo(21, "test"); cache.put("/one/two", "key1", pojo); mgr.rollback(); assertNull(mgr.getTransaction()); assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache.getTransactionTable().getNumLocalTransactions()); assertEquals(false, cache.exists(Fqn.fromString("/one/two"))); assertNull(cache.getRoot().getChild("one")); destroyCache(cache); } public void testSingleInstanceDuplicateCommit() throws Exception { groupIncreaser++; CacheSPI cache = createSyncReplicatedCache(); LockManager lockManager = TestingUtil.extractLockManager(cache); TransactionManager mgr = cache.getConfiguration().getRuntimeConfig().getTransactionManager(); assertNull(mgr.getTransaction()); mgr.begin(); assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache.getTransactionTable().getNumLocalTransactions()); SamplePojo pojo = new SamplePojo(21, "test"); cache.put("/one/two", "key1", pojo); mgr.commit(); assertNull(mgr.getTransaction()); boolean fail = false; try { mgr.commit(); } catch (Exception e) { fail = true; } assertEquals(true, fail); assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache.getTransactionTable().getNumLocalTransactions()); assertTrue(cache.exists(Fqn.fromString("/one/two"))); assertNotNull(cache.getRoot().getChild("one")); assertEquals(false, lockManager.isLocked(cache.getRoot())); assertEquals(false, lockManager.isLocked(cache.getNode("/one"))); assertEquals(false, lockManager.isLocked(cache.getNode("/one/two"))); assertNotNull(cache.getNode("/one").getChild("two")); assertNotNull(cache.get(Fqn.fromString("/one/two"), "key1")); destroyCache(cache); } public void testValidationFailCommit() throws Exception { groupIncreaser++; CacheSPI cache = createSyncReplicatedCache(); LockManager lockManager = TestingUtil.extractLockManager(cache); TransactionManager mgr = cache.getConfiguration().getRuntimeConfig().getTransactionManager(); assertNull(mgr.getTransaction()); mgr.begin(); Transaction tx = mgr.getTransaction(); assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache.getTransactionTable().getNumLocalTransactions()); SamplePojo pojo = new SamplePojo(21, "test"); cache.put("/one/two", "key1", pojo); mgr.suspend(); assertEquals(1, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(1, cache.getTransactionTable().getNumLocalTransactions()); assertNull(mgr.getTransaction()); mgr.begin(); SamplePojo pojo2 = new SamplePojo(22, "test2"); cache.put("/one/two", "key1", pojo2); mgr.commit(); assertEquals(1, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(1, cache.getTransactionTable().getNumLocalTransactions()); mgr.resume(tx); boolean fail = false; try { mgr.commit(); } catch (Exception e) { fail = true; } assertNull(mgr.getTransaction()); assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache.getTransactionTable().getNumLocalTransactions()); assertEquals(true, fail); assertTrue(cache.exists(Fqn.fromString("/one/two"))); assertNotNull(cache.getRoot().getChild("one")); assertEquals(false, lockManager.isLocked(cache.getRoot())); assertEquals(false, lockManager.isLocked(cache.getNode("/one"))); assertEquals(false, lockManager.isLocked(cache.getNode("/one/two"))); assertNotNull(cache.getNode("/one").getChild("two")); assertEquals(pojo2, cache.get(Fqn.fromString("/one/two"), "key1")); destroyCache(cache); } public void test2InstanceCommit() throws Exception { groupIncreaser++; CacheSPI cache = createSyncReplicatedCache(); CacheSPI cache2 = createSyncReplicatedCache(); LockManager lockManager = TestingUtil.extractLockManager(cache); LockManager lockManager2 = TestingUtil.extractLockManager(cache2); TransactionManager mgr = cache.getConfiguration().getRuntimeConfig().getTransactionManager(); assertNull(mgr.getTransaction()); mgr.begin(); assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache.getTransactionTable().getNumLocalTransactions()); SamplePojo pojo = new SamplePojo(21, "test"); cache.put("/one/two", "key1", pojo); mgr.commit(); // cache asserts assertNull(mgr.getTransaction()); assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache.getTransactionTable().getNumLocalTransactions()); assertTrue(cache.exists(Fqn.fromString("/one/two"))); assertNotNull(cache.getRoot().getChild("one")); assertNotNull(cache.get(Fqn.fromString("/one/two"), "key1")); assertTrue(cache.exists(Fqn.fromString("/one/two"))); assertNotNull(cache.getRoot().getChild("one")); assertEquals(false, lockManager.isLocked(cache.getRoot())); assertEquals(false, lockManager.isLocked(cache.getNode("/one"))); assertEquals(false, lockManager.isLocked(cache.getNode("/one/two"))); assertNotNull(cache.getNode("/one").getChild("two")); assertNotNull(cache.get(Fqn.fromString("/one/two"), "key1")); // cache2 asserts assertEquals(0, cache2.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache2.getTransactionTable().getNumLocalTransactions()); assertTrue(cache2.exists(Fqn.fromString("/one/two"))); assertNotNull(cache2.getRoot().getChild("one")); assertNotNull(cache2.get(Fqn.fromString("/one/two"), "key1")); assertTrue(cache2.exists(Fqn.fromString("/one/two"))); assertNotNull(cache2.getRoot().getChild("one")); assertEquals(false, lockManager2.isLocked(cache2.getRoot())); assertEquals(false, lockManager2.isLocked(cache2.getNode("/one"))); assertEquals(false, lockManager2.isLocked(cache2.getNode("/one/two"))); assertNotNull(cache2.getNode("/one").getChild("two")); assertNotNull(cache2.get(Fqn.fromString("/one/two"), "key1")); destroyCache(cache); destroyCache(cache2); } public void test2InstanceRemove() throws Exception { groupIncreaser++; CacheSPI cache = createSyncReplicatedCache(); CacheSPI cache2 = createSyncReplicatedCache(); LockManager lockManager = TestingUtil.extractLockManager(cache); LockManager lockManager2 = TestingUtil.extractLockManager(cache2); TransactionManager mgr = cache.getConfiguration().getRuntimeConfig().getTransactionManager(); assertNull(mgr.getTransaction()); mgr.begin(); assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache.getTransactionTable().getNumLocalTransactions()); SamplePojo pojo = new SamplePojo(21, "test"); cache.put("/one/two", "key1", pojo); mgr.commit(); // cache asserts assertNull(mgr.getTransaction()); assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache.getTransactionTable().getNumLocalTransactions()); assertTrue(cache.exists(Fqn.fromString("/one/two"))); assertNotNull(cache.getRoot().getChild("one")); assertNotNull(cache.get(Fqn.fromString("/one/two"), "key1")); assertTrue(cache.exists(Fqn.fromString("/one/two"))); assertNotNull(cache.getRoot().getChild("one")); assertEquals(false, lockManager.isLocked(cache.getRoot())); assertEquals(false, lockManager.isLocked(cache.getNode("/one"))); assertEquals(false, lockManager.isLocked(cache.getNode("/one/two"))); assertNotNull(cache.getNode("/one").getChild("two")); assertNotNull(cache.get(Fqn.fromString("/one/two"), "key1")); // cache2 asserts assertEquals(0, cache2.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache2.getTransactionTable().getNumLocalTransactions()); assertTrue(cache2.exists(Fqn.fromString("/one/two"))); assertNotNull(cache2.getRoot().getChild("one")); assertNotNull(cache2.get(Fqn.fromString("/one/two"), "key1")); assertTrue(cache2.exists(Fqn.fromString("/one/two"))); assertNotNull(cache2.getRoot().getChild("one")); assertEquals(false, lockManager2.isLocked(cache2.getRoot())); assertEquals(false, lockManager2.isLocked(cache2.getNode("/one"))); assertEquals(false, lockManager2.isLocked(cache2.getNode("/one/two"))); assertNotNull(cache2.getNode("/one").getChild("two")); assertNotNull(cache2.get(Fqn.fromString("/one/two"), "key1")); cache.removeNode("/one/two"); assertEquals(false, cache.exists("/one/two")); assertEquals(false, cache2.exists("/one/two")); assertEquals(null, cache.get("/one/two", "key1")); assertEquals(null, cache2.get("/one/two", "key1")); destroyCache(cache); destroyCache(cache2); } public void testValidationFailCommit2Instances() throws Exception { groupIncreaser++; CacheSPI cache = createSyncReplicatedCache(); CacheSPI cache2 = createSyncReplicatedCache(); LockManager lockManager = TestingUtil.extractLockManager(cache); TransactionManager mgr = cache.getConfiguration().getRuntimeConfig().getTransactionManager(); assertNull(mgr.getTransaction()); mgr.begin(); Transaction tx = mgr.getTransaction(); assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache.getTransactionTable().getNumLocalTransactions()); SamplePojo pojo = new SamplePojo(21, "test"); cache.put("/one/two", "key1", pojo); mgr.suspend(); assertEquals(1, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(1, cache.getTransactionTable().getNumLocalTransactions()); GlobalTransaction gtx = cache.getCurrentTransaction(tx, true); TransactionTable table = cache.getTransactionTable(); OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table .get(gtx); assertEquals(3, entry.getTransactionWorkSpace().getNodes().size()); assertNull(mgr.getTransaction()); mgr.begin(); SamplePojo pojo2 = new SamplePojo(22, "test2"); cache2.put("/one/two", "key1", pojo2); mgr.commit(); assertEquals(1, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(1, cache.getTransactionTable().getNumLocalTransactions()); mgr.resume(tx); boolean fail = false; try { mgr.commit(); } catch (Exception e) { fail = true; } assertEquals(true, fail); assertNull(mgr.getTransaction()); assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache.getTransactionTable().getNumLocalTransactions()); assertEquals(0, entry.getTransactionWorkSpace().getNodes().size()); assertTrue(cache.exists(Fqn.fromString("/one/two"))); assertNotNull(cache.getRoot().getChild("one")); assertEquals(false, lockManager.isLocked(cache.getRoot())); assertEquals(false, lockManager.isLocked(cache.getNode("/one"))); assertEquals(false, lockManager.isLocked(cache.getNode("/one/two"))); assertNotNull(cache.getNode("/one").getChild("two")); assertEquals(pojo2, cache.get(Fqn.fromString("/one/two"), "key1")); destroyCache(cache); destroyCache(cache2); } public void testGetKeyValIsolationTransaction() throws Exception { SamplePojo pojo1 = new SamplePojo(21, "test-1"); SamplePojo pojo2 = new SamplePojo(21, "test-2"); CacheSPI cache = createCacheWithListener(); TransactionManager mgr = cache.getConfiguration().getRuntimeConfig().getTransactionManager(); assertNull(mgr.getTransaction()); // first put in a value mgr.begin(); assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache.getTransactionTable().getNumLocalTransactions()); cache.put("/one/two", "key1", pojo1); mgr.commit(); mgr.begin(); Transaction tx = mgr.getTransaction(); System.out.println("Current TX " + mgr.getTransaction()); assertEquals(pojo1, cache.get("/one/two", "key1")); // start another mgr.suspend(); mgr.begin(); System.out.println("Current TX " + mgr.getTransaction()); cache.put("/one/two", "key2", pojo2); // assert we can see this INSIDE the existing tx //assertEquals(pojo2, cache.get("/one/two", "key2")); mgr.commit(); // assert we can see this outside the existing tx assertEquals(pojo2, cache.get("/one/two", "key2")); System.out.println("Current TX " + mgr.getTransaction()); // resume the suspended one mgr.resume(tx); System.out.println("Current TX " + mgr.getTransaction()); // assert we can't see the change from tx2 as we already touched the node assertEquals(null, cache.get("/one/two", "key2")); mgr.commit(); destroyCache(cache); } public void testGetKeysIsolationTransaction() throws Exception { CacheSPI cache = createCacheWithListener(); TransactionManager mgr = cache.getConfiguration().getRuntimeConfig().getTransactionManager(); if (mgr.getTransaction() != null) mgr.rollback(); assertNull(mgr.getTransaction()); // first put in a value mgr.begin(); assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache.getTransactionTable().getNumLocalTransactions()); SamplePojo pojo = new SamplePojo(21, "test"); cache.put("/one/two", "key1", pojo); mgr.commit(); mgr.begin(); Transaction tx = mgr.getTransaction(); assertEquals(1, cache.getNode("/one/two").getKeys().size()); // start another mgr.suspend(); mgr.begin(); cache.put("/one/two", "key2", pojo); mgr.commit(); // assert we can see this outsode the existing tx assertEquals(2, cache.getNode("/one/two").getKeys().size()); // resume the suspended one mgr.resume(tx); // assert we can't see thge change from tx2 as we already touched the node assertEquals(1, cache.getNode("/one/two").getKeys().size()); mgr.commit(); destroyCache(cache); } public void testTxRollbackThroughConcurrentWrite() throws Exception { CacheSPI cache = createCacheWithListener(); Set keys; TransactionManager mgr = cache.getConfiguration().getRuntimeConfig().getTransactionManager(); if (mgr.getTransaction() != null) mgr.rollback(); assertNull(mgr.getTransaction()); // first put in a value mgr.begin(); assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache.getTransactionTable().getNumLocalTransactions()); cache.put("/one/two", "key1", "val1"); mgr.commit(); keys = cache.getNode("/one/two").getKeys(); System.out.println("keys after TX #1: " + keys); assertEquals(1, keys.size()); // First TX mgr.begin(); Transaction tx = mgr.getTransaction(); cache.put("/one/two", "key2", "val2");// version for this is 1 // start another mgr.suspend(); // Second TX mgr.begin(); cache.put("/one/two", "key3", "val3"); mgr.commit();// now version is 2, attrs are key1 and key3 // assert we can see this outside the existing tx keys = cache.getNode("/one/two").getKeys(); System.out.println("keys after TX #3 committed: " + keys); assertEquals(2, keys.size()); // resume the suspended one mgr.resume(tx); // assert we can't see the change from tx2 as we already touched the node keys = cache.getNode("/one/two").getKeys(); System.out.println("keys after TX #2 resumed (in private workspace of TX #2): " + keys); assertEquals(2, keys.size());// we will see key1 and key2, but *not* key3 // this will fail as our workspace has version 1, whereas cache has 2; TX will be rolled back try { mgr.commit(); fail("TX should fail as other TX incremented version number"); } catch (RollbackException rollback_ex) { System.out.println("TX was rolled back because the other TX committed first and incremented version ID." + " This is the expected behavior"); } keys = cache.getNode("/one/two").getKeys(); System.out.println("keys after TX #2 was rolled back: " + keys); assertEquals(2, keys.size());// key1 and key2 destroyCache(cache); } @SuppressWarnings("unchecked") protected CacheSPI createSyncReplicatedCache() throws Exception { return createReplicatedCache("temp" + groupIncreaser, Configuration.CacheMode.REPL_SYNC); } protected CacheSPI createSyncReplicatedCacheAsyncCommit() throws Exception { CacheSPI cache = createReplicatedCache("temp" + groupIncreaser, Configuration.CacheMode.REPL_SYNC, false); cache.getConfiguration().setSyncCommitPhase(false); cache.getConfiguration().setSyncRollbackPhase(false); cache.create(); cache.start(); return cache; } public void testPuts() throws Exception { CacheSPI cache = createCache(); Transaction tx; TransactionManager mgr = cache.getConfiguration().getRuntimeConfig().getTransactionManager(); assertNull(cache.getNode(fqn)); mgr.begin(); cache.put(fqn, key, value); assertEquals(value, cache.get(fqn, key)); tx = mgr.getTransaction(); mgr.suspend(); mgr.begin(); assertNull(cache.get(fqn, key)); mgr.commit(); mgr.resume(tx); assertEquals(value, cache.get(fqn, key)); mgr.commit(); assertEquals(value, cache.get(fqn, key)); } public void testRemoves() throws Exception { CacheSPI cache = createCache(); Transaction tx; TransactionManager mgr = cache.getConfiguration().getRuntimeConfig().getTransactionManager(); assertNull(cache.getNode(fqn)); cache.put(fqn, key, value); assertEquals(value, cache.get(fqn, key)); mgr.begin(); assertEquals(value, cache.get(fqn, key)); cache.removeNode(fqn); assertNull(cache.getNode(fqn)); tx = mgr.getTransaction(); mgr.suspend(); mgr.begin(); assertEquals(value, cache.get(fqn, key)); mgr.commit(); mgr.resume(tx); assertNull(cache.getNode(fqn)); mgr.commit(); assertNull(cache.getNode(fqn)); } public void testRemovesBeforeGet() throws Exception { CacheSPI cache = createCache(); Transaction tx; TransactionManager mgr = cache.getConfiguration().getRuntimeConfig().getTransactionManager(); assertNull(cache.getNode(fqn)); cache.put(fqn, key, value); assertEquals(value, cache.get(fqn, key)); mgr.begin(); cache.removeNode(fqn); assertNull(cache.getNode(fqn)); tx = mgr.getTransaction(); mgr.suspend(); mgr.begin(); assertEquals(value, cache.get(fqn, key)); mgr.commit(); mgr.resume(tx); assertNull(cache.getNode(fqn)); mgr.commit(); assertNull(cache.getNode(fqn)); } public void testLoopedPutAndGet() throws Exception { try { log.debug("Starting test"); CacheSPI cache1 = createSyncReplicatedCache(); CacheSPI cache2 = createSyncReplicatedCache(); log.debug("Created caches"); TransactionManager mgr = cache1.getConfiguration().getRuntimeConfig().getTransactionManager(); int numLoops = 5, numPuts = 5; log.debug("Starting " + numLoops + " loops"); for (int i = 0; i < numLoops; i++) { log.debug(" *** in loop " + i); mgr.begin(); for (int j = 0; j < numPuts; j++) { cache1.put(Fqn.fromString("/profiler/node" + i), "key" + j, "value" + j); } log.debug("*** >> Out of put loop"); mgr.commit(); //cache2.get(Fqn.fromString("/profiler/node" + i)); } destroyCache(cache1); destroyCache(cache2); } catch (Exception e) { log.debug("Error: ", e); assertFalse("Threw exception!", true); throw e; } } /** * Tests that if synchronous commit messages are not used, the proper * data is returned from remote nodes after a tx that does a local * put returns. * * @throws Exception */ @Test(enabled = false) // known failure - JBCACHE-1201 public void testAsynchronousCommit() throws Exception { CacheSPI cache1 = createSyncReplicatedCacheAsyncCommit(); CacheSPI cache2 = createSyncReplicatedCacheAsyncCommit(); // Test will pass if we set up the caches with SyncCommitPhaseTrue // CacheSPI cache1 = createSyncReplicatedCache(); // CacheSPI cache2 = createSyncReplicatedCache(); TransactionManager tm1 = cache1.getConfiguration().getRuntimeConfig().getTransactionManager(); Fqn fqn = Fqn.fromString("/test/node"); String KEY = "key"; String VALUE1 = "value1"; tm1.begin(); cache1.put(fqn, KEY, VALUE1); tm1.commit(); // A simple sleep will also make this test pass // try { Thread.sleep(100); } catch (InterruptedException e) {} assertEquals("Known issue JBCACHE-1201: Correct node2 value", VALUE1, cache2.get(fqn, KEY)); assertEquals("Correct node1 value", VALUE1, cache1.get(fqn, KEY)); destroyCache(cache1); destroyCache(cache2); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/optimistic/RemoveBeforeCreateTest.java0000644000175000017500000000435411017455042032034 0ustar twernertwernerpackage org.jboss.cache.optimistic; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.NodeSPI; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.config.Configuration; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertNull; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.TransactionManager; /** * Tests removal of a node before the node is even created. */ @Test(groups = "functional") public class RemoveBeforeCreateTest extends AbstractOptimisticTestCase { CacheSPI[] c = null; TransactionManager t; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { c = new CacheSPI[2]; c[0] = createReplicatedCache(Configuration.CacheMode.REPL_ASYNC); c[1] = createReplicatedCache(Configuration.CacheMode.REPL_ASYNC); TestingUtil.blockUntilViewsReceived(c, 20000); t = c[0].getTransactionManager(); } @AfterMethod(alwaysRun = true) public void tearDown() { if (c != null) { destroyCache(c[0]); destroyCache(c[1]); c = null; } } @SuppressWarnings("unchecked") public void testControl() throws Exception { t.begin(); c[0].put("/control", "key", "value"); t.commit(); TestingUtil.sleepThread(200); assertEquals("value", c[0].get("/control", "key")); assertEquals("value", c[1].get("/control", "key")); DefaultDataVersion v1 = (DefaultDataVersion) ((NodeSPI) c[0].getNode("/control")).getVersion(); assertEquals(1, v1.getRawVersion()); DefaultDataVersion v2 = (DefaultDataVersion) ((NodeSPI) c[1].getNode("/control")).getVersion(); assertEquals(1, v2.getRawVersion()); } @SuppressWarnings("unchecked") public void testRemoveBeforePut() throws Exception { Fqn f = Fqn.fromString("/test"); assertNull(c[0].getNode(f)); assertNull(c[1].getNode(f)); t.begin(); c[0].removeNode(f); // should NOT barf!!! t.commit(); TestingUtil.sleepThread(200); assertNull(c[0].getNode(f)); assertNull(c[1].getNode(f)); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/optimistic/ValidationFailureTest.java0000644000175000017500000000311710735006726031735 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.optimistic; import org.jboss.cache.CacheSPI; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertTrue; import org.testng.annotations.Test; import javax.transaction.Transaction; import javax.transaction.TransactionManager; /** * Tests a failure in validating a concurrently updated node * * @author Manik Surtani (manik@jboss.org) */ @Test(groups = "functional") public class ValidationFailureTest extends AbstractOptimisticTestCase { public void testValidationFailureLockRelease() throws Exception { CacheSPI cache = createCache(); TransactionManager mgr = cache.getTransactionManager(); mgr.begin(); cache.put("/a", "key", "value"); mgr.commit(); // should succeed, 0 locks left over assertEquals("value", cache.get("/a", "key")); assertEquals(0, cache.getNumberOfLocksHeld()); mgr.begin(); cache.put("/b", "key", "value"); Transaction tx1 = mgr.suspend(); mgr.begin(); cache.put("/b", "key", "value2"); mgr.commit(); mgr.resume(tx1); try { mgr.commit(); assertTrue("Should have failed", false); } catch (Exception e) { assertTrue("Expecting this to fail", true); } // nothing should have been locked. assertEquals(0, cache.getNumberOfLocksHeld()); destroyCache(cache); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/optimistic/LockParentVersionTest.java0000644000175000017500000000052710735006726031745 0ustar twernertwernerpackage org.jboss.cache.optimistic; import org.testng.annotations.Test; /** * @author Manik Surtani * @since 2.0.0 */ @Test(groups = "functional") public class LockParentVersionTest extends ParentVersionTest { public LockParentVersionTest() { lockParentForChildInsertRemove = true; } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/optimistic/ValidatorInterceptorTest.java0000755000175000017500000003567111017455042032505 0ustar twernertwerner/* * Created on 17-Feb-2005 * * * */ package org.jboss.cache.optimistic; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.NodeSPI; import org.jboss.cache.commands.VisitableCommand; import org.jboss.cache.commands.tx.CommitCommand; import org.jboss.cache.commands.tx.OptimisticPrepareCommand; import org.jboss.cache.commands.tx.RollbackCommand; import org.jboss.cache.interceptors.InvocationContextInterceptor; import org.jboss.cache.interceptors.OptimisticCreateIfNotExistsInterceptor; import org.jboss.cache.interceptors.OptimisticNodeInterceptor; import org.jboss.cache.interceptors.OptimisticValidatorInterceptor; import org.jboss.cache.interceptors.base.CommandInterceptor; import org.jboss.cache.loader.SamplePojo; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.transaction.GlobalTransaction; import org.jboss.cache.transaction.OptimisticTransactionEntry; import org.jboss.cache.transaction.TransactionTable; import org.jboss.cache.util.CachePrinter; import org.jgroups.Address; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.Transaction; import javax.transaction.TransactionManager; import java.util.Collections; import java.util.HashMap; import java.util.Map; /** * @author xenephon */ @Test(groups = "functional") public class ValidatorInterceptorTest extends AbstractOptimisticTestCase { private CacheSPI cache; private TransactionManager mgr; private MockInterceptor dummy; @BeforeMethod public void setUp() throws Exception { cache = createCacheWithListener(); mgr = cache.getTransactionManager(); CommandInterceptor ici = TestingUtil.findInterceptor(cache, InvocationContextInterceptor.class); CommandInterceptor validateInterceptor = TestingUtil.findInterceptor(cache, OptimisticValidatorInterceptor.class); CommandInterceptor interceptor = TestingUtil.findInterceptor(cache, OptimisticCreateIfNotExistsInterceptor.class); CommandInterceptor nodeInterceptor = TestingUtil.findInterceptor(cache, OptimisticNodeInterceptor.class); dummy = new MockInterceptor(); ici.setNext(validateInterceptor); validateInterceptor.setNext(interceptor); interceptor.setNext(nodeInterceptor); nodeInterceptor.setNext(dummy); TestingUtil.replaceInterceptorChain(cache, ici); cache.addInterceptor(new ResetRemoteFlagInterceptor(), InvocationContextInterceptor.class); System.out.println("Interceptors: " + CachePrinter.printCacheInterceptors(cache)); } @AfterMethod public void tearDown() { TestingUtil.killCaches(cache); } public void testTransactionvalidateMethod() throws Throwable { mgr.begin(); Transaction tx = mgr.getTransaction(); // inject InvocationContext cache.getInvocationContext().setTransaction(tx); cache.getInvocationContext().setGlobalTransaction(cache.getCurrentTransaction(tx, true)); SamplePojo pojo = new SamplePojo(21, "test"); Map temp = new HashMap(); temp.put("key1", pojo); cache.put("/one/two", temp); assertEquals(null, dummy.getCalledCommand()); TransactionTable table = cache.getTransactionTable(); GlobalTransaction gtx = table.get(tx); OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table.get(gtx); TransactionWorkspace workspace = entry.getTransactionWorkSpace(); assertEquals(3, workspace.getNodes().size()); assertNotNull(workspace.getNode(Fqn.fromString("/one/two"))); assertEquals(pojo, workspace.getNode(Fqn.fromString("/one/two")).get("key1")); assertEquals(1, workspace.getNode(Fqn.fromString("/one/two")).getMergedData().size()); assertTrue(entry.getLocks().isEmpty()); assertEquals(1, entry.getModifications().size()); assertTrue(!cache.exists("/one/two")); assertEquals(null, dummy.getCalledCommand()); OptimisticPrepareCommand prepareCommand = new OptimisticPrepareCommand(gtx, entry.getModifications(), (Map) null, (Address) gtx.getAddress(), Boolean.FALSE); //now let us do a prepare TestingUtil.replicateCommand(cache, prepareCommand); assertEquals(3, workspace.getNodes().size()); assertNotNull(workspace.getNode(Fqn.fromString("/one/two"))); assertEquals(pojo, workspace.getNode(Fqn.fromString("/one/two")).get("key1")); assertEquals(1, workspace.getNode(Fqn.fromString("/one/two")).getMergedData().size()); assertTrue(entry.getLocks().isEmpty()); assertEquals(1, entry.getModifications().size()); assertTrue(!cache.exists("/one/two")); assertEquals(prepareCommand, dummy.getCalledCommand()); mgr.commit(); } public void testTransactionValidateFailureMethod() throws Exception { mgr.begin(); Transaction tx = mgr.getTransaction(); // inject InvocationContext cache.getInvocationContext().setTransaction(tx); cache.getInvocationContext().setGlobalTransaction(cache.getCurrentTransaction(tx, true)); SamplePojo pojo = new SamplePojo(21, "test"); Map temp = new HashMap(); temp.put("key1", pojo); cache.put("/one/two", temp); assertEquals(null, dummy.getCalledCommand()); TransactionTable table = cache.getTransactionTable(); GlobalTransaction gtx = table.get(tx); OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table.get(gtx); TransactionWorkspace workspace = entry.getTransactionWorkSpace(); assertEquals(3, workspace.getNodes().size()); assertNotNull(workspace.getNode(Fqn.fromString("/one/two"))); assertEquals(pojo, workspace.getNode(Fqn.fromString("/one/two")).get("key1")); assertEquals(1, workspace.getNode(Fqn.fromString("/one/two")).getMergedData().size()); assertTrue(entry.getLocks().isEmpty()); assertEquals(1, entry.getModifications().size()); assertTrue(!cache.exists("/one/two")); assertEquals(null, dummy.getCalledCommand()); //lets change one of the underlying version numbers workspace.getNode(Fqn.fromString("/one/two")).getNode().setVersion(new DefaultDataVersion(2)); //now let us do a prepare OptimisticPrepareCommand prepareCommand = new OptimisticPrepareCommand(gtx, entry.getModifications(), (Map) null, (Address) gtx.getAddress(), Boolean.FALSE); try { TestingUtil.replicateCommand(cache, prepareCommand); fail(); } catch (Throwable t) { assertTrue(true); } mgr.commit(); } public void testTransactionValidateCommitMethod() throws Throwable { mgr.begin(); Transaction tx = mgr.getTransaction(); // inject InvocationContext cache.getInvocationContext().setTransaction(tx); cache.getInvocationContext().setGlobalTransaction(cache.getCurrentTransaction(tx, true)); Object pojo = new SamplePojo(21, "test"); cache.put("/one/two", Collections.singletonMap((Object) "key1", pojo)); assertEquals(null, dummy.getCalledCommand()); TransactionTable table = cache.getTransactionTable(); GlobalTransaction gtx = table.get(tx); OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table.get(gtx); TransactionWorkspace workspace = entry.getTransactionWorkSpace(); assertEquals(3, workspace.getNodes().size()); assertNotNull(workspace.getNode(Fqn.fromString("/one/two"))); assertEquals(pojo, workspace.getNode(Fqn.fromString("/one/two")).get("key1")); assertEquals(1, workspace.getNode(Fqn.fromString("/one/two")).getMergedData().size()); assertTrue(entry.getLocks().isEmpty()); assertEquals(1, entry.getModifications().size()); assertTrue(!cache.exists("/one/two")); assertEquals(null, dummy.getCalledCommand()); //lets change one of the underlying version numbers //now let us do a prepare OptimisticPrepareCommand prepareCommand = new OptimisticPrepareCommand(gtx, entry.getModifications(), (Map) null, (Address) gtx.getAddress(), Boolean.FALSE); try { TestingUtil.replicateCommand(cache, prepareCommand); fail(); } catch (Throwable t) { assertTrue(true); } CommitCommand commitCommand = new CommitCommand(gtx); TestingUtil.replicateCommand(cache, commitCommand); assertEquals(3, workspace.getNodes().size()); assertNotNull(workspace.getNode(Fqn.fromString("/one/two"))); assertEquals(pojo, workspace.getNode(Fqn.fromString("/one/two")).get("key1")); assertEquals(1, workspace.getNode(Fqn.fromString("/one/two")).getMergedData().size()); assertTrue(entry.getLocks().isEmpty()); assertEquals(1, entry.getModifications().size()); assertEquals(commitCommand, dummy.getCalledCommand()); NodeSPI node = workspace.getNode(Fqn.ROOT).getNode(); //assert we can navigate assertNotNull(node); node = (NodeSPI) node.getChild("one"); assertEquals(new DefaultDataVersion(0), node.getVersion()); assertNotNull(node); node = (NodeSPI) node.getChild("two"); assertNotNull(node); assertEquals(new DefaultDataVersion(1), node.getVersion()); assertEquals(pojo, node.get("key1")); mgr.commit(); } public void testTransactionValidateFailRemoteCommitMethod() throws Throwable { mgr.begin(); Transaction tx = mgr.getTransaction(); // inject InvocationContext cache.getInvocationContext().setTransaction(tx); cache.getInvocationContext().setGlobalTransaction(cache.getCurrentTransaction(tx, true)); SamplePojo pojo = new SamplePojo(21, "test"); Map temp = new HashMap(); temp.put("key1", pojo); cache.put("/one/two", temp); assertEquals(null, dummy.getCalledCommand()); TransactionTable table = cache.getTransactionTable(); GlobalTransaction gtx = table.get(tx); OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table.get(gtx); @SuppressWarnings("unchecked") TransactionWorkspace workspace = entry.getTransactionWorkSpace(); assertEquals(3, workspace.getNodes().size()); assertNotNull(workspace.getNode(Fqn.fromString("/one/two"))); assertEquals(pojo, workspace.getNode(Fqn.fromString("/one/two")).get("key1")); assertEquals(1, workspace.getNode(Fqn.fromString("/one/two")).getMergedData().size()); assertTrue(entry.getLocks().isEmpty()); assertEquals(1, entry.getModifications().size()); assertTrue(!cache.exists("/one/two")); assertEquals(null, dummy.getCalledCommand()); //lets change one of the underlying version numbers //now let us do a prepare OptimisticPrepareCommand prepareCommand = new OptimisticPrepareCommand(gtx, entry.getModifications(), (Map) null, (Address) gtx.getAddress(), Boolean.FALSE); try { TestingUtil.replicateCommand(cache, prepareCommand); fail(); } catch (Throwable t) { assertTrue(true); } CommitCommand commitCommand = new CommitCommand(gtx); TestingUtil.replicateCommand(cache, commitCommand); assertEquals(3, workspace.getNodes().size()); assertNotNull(workspace.getNode(Fqn.fromString("/one/two"))); assertEquals(pojo, workspace.getNode(Fqn.fromString("/one/two")).get("key1")); assertEquals(1, workspace.getNode(Fqn.fromString("/one/two")).getMergedData().size()); assertTrue(entry.getLocks().isEmpty()); assertEquals(1, entry.getModifications().size()); assertEquals(commitCommand, dummy.getCalledCommand()); NodeSPI node = workspace.getNode(Fqn.fromString("/")).getNode(); //assert we can navigate assertNotNull(node); node = (NodeSPI) node.getChild("one"); assertEquals(new DefaultDataVersion(0), node.getVersion()); assertNotNull(node); assertTrue(cache.exists(node.getFqn())); node = (NodeSPI) node.getChild("two"); assertNotNull(node); assertTrue(cache.exists(node.getFqn())); assertEquals(new DefaultDataVersion(1), node.getVersion()); assertEquals(pojo, node.get("key1")); mgr.commit(); } public void testTransactionValidateRollbackMethod() throws Throwable { mgr.begin(); Transaction tx = mgr.getTransaction(); // inject InvocationContext cache.getInvocationContext().setTransaction(tx); cache.getInvocationContext().setGlobalTransaction(cache.getCurrentTransaction(tx, true)); SamplePojo pojo = new SamplePojo(21, "test"); Map temp = new HashMap(); temp.put("key1", pojo); cache.put("/one/two", temp); assertEquals(null, dummy.getCalledCommand()); TransactionTable table = cache.getTransactionTable(); GlobalTransaction gtx = table.get(tx); OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table.get(gtx); TransactionWorkspace workspace = entry.getTransactionWorkSpace(); assertEquals(3, workspace.getNodes().size()); assertNotNull(workspace.getNode(Fqn.fromString("/one/two"))); assertEquals(pojo, workspace.getNode(Fqn.fromString("/one/two")).get("key1")); assertEquals(1, workspace.getNode(Fqn.fromString("/one/two")).getMergedData().size()); assertTrue(entry.getLocks().isEmpty()); assertEquals(1, entry.getModifications().size()); assertTrue(!cache.exists("/one/two")); assertEquals(null, dummy.getCalledCommand()); //lets change one of the underlying version numbers //now let us do a prepare OptimisticPrepareCommand prepareCommand = new OptimisticPrepareCommand(gtx, entry.getModifications(), (Map) null, (Address) gtx.getAddress(), Boolean.FALSE); try { TestingUtil.replicateCommand(cache, prepareCommand); fail(); } catch (Throwable t) { assertTrue(true); } RollbackCommand rollbackCommand = new RollbackCommand(gtx); TestingUtil.replicateCommand(cache, rollbackCommand); assertEquals(0, workspace.getNodes().size()); assertNull(workspace.getNode(Fqn.fromString("/one/two"))); assertNull(workspace.getNode(Fqn.fromString("/one"))); assertTrue(entry.getLocks().isEmpty()); assertEquals(1, entry.getModifications().size()); mgr.commit(); } public static class ResetRemoteFlagInterceptor extends CommandInterceptor { public Object handleDefault(InvocationContext ctx, VisitableCommand command) throws Throwable { log.trace("Setting isRemote on globalTransaction " + ctx.getGlobalTransaction() + " to true"); ctx.getGlobalTransaction().setRemote(true); return invokeNextInterceptor(ctx, command); } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/optimistic/NodeInterceptorGetKeyValTest.java0000644000175000017500000002072111017455042033204 0ustar twernertwernerpackage org.jboss.cache.optimistic; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.interceptors.OptimisticCreateIfNotExistsInterceptor; import org.jboss.cache.interceptors.OptimisticNodeInterceptor; import org.jboss.cache.interceptors.base.CommandInterceptor; import org.jboss.cache.loader.SamplePojo; import org.jboss.cache.transaction.GlobalTransaction; import org.jboss.cache.transaction.OptimisticTransactionEntry; import org.jboss.cache.transaction.TransactionTable; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.Transaction; import javax.transaction.TransactionManager; /** * @author xenephon */ @Test(groups = "functional") public class NodeInterceptorGetKeyValTest extends AbstractOptimisticTestCase { private CacheSPI cache; private TransactionManager mgr; private TestListener listener; private MockInterceptor dummy; @BeforeMethod public void setUp() throws Exception { listener = new TestListener(); cache = createCacheWithListener(listener); CommandInterceptor interceptor = TestingUtil.findInterceptor(cache, OptimisticCreateIfNotExistsInterceptor.class); CommandInterceptor nodeInterceptor = TestingUtil.findInterceptor(cache, OptimisticNodeInterceptor.class); dummy = new MockInterceptor(); interceptor.setNext(nodeInterceptor); nodeInterceptor.setNext(dummy); TestingUtil.replaceInterceptorChain(cache, interceptor); mgr = cache.getTransactionManager(); } @AfterMethod public void tearDown() { TestingUtil.killCaches(cache); } public void testTransactionGetKeyMethod() throws Exception { mgr.begin(); Transaction tx = mgr.getTransaction(); // inject InvocationContext setupTransactions(cache, tx); SamplePojo pojo = new SamplePojo(21, "test"); cache.put("/one/two", "key1", pojo); assertEquals(null, dummy.getCalledCommand()); TransactionTable table = cache.getTransactionTable(); GlobalTransaction gtx = table.get(tx); OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table.get(gtx); @SuppressWarnings("unchecked") TransactionWorkspace workspace = entry.getTransactionWorkSpace(); //assert we can see this with a key value get in the transaction assertEquals(pojo, cache.get("/one/two", "key1")); mgr.commit(); //assert what should be the results of our call assertEquals(3, workspace.getNodes().size()); assertNotNull(workspace.getNode(Fqn.fromString("/one/two"))); assertEquals(pojo, workspace.getNode(Fqn.fromString("/one/two")).get("key1")); assertTrue(entry.getLocks().isEmpty()); assertEquals(1, entry.getModifications().size()); assertTrue(!cache.exists("/one/two")); assertEquals(null, dummy.getCalledCommand()); //assert that we cannot see the change if we have not put it into the cache // we need to do this as we do not have the tx interceptor in this stack mgr.begin(); Transaction tx2 = mgr.getTransaction(); // inject InvocationContext setupTransactions(cache, tx2); assertNull(cache.get("/one/two", "key1")); mgr.commit(); } public void testTransactionGetKeyValOverwriteMethod() throws Exception { mgr.begin(); Transaction tx = mgr.getTransaction(); // inject InvocationContext setupTransactions(cache, tx); SamplePojo pojo = new SamplePojo(21, "test"); cache.put("/one/two", "key1", pojo); //overwrite the map we just put in SamplePojo pojo2 = new SamplePojo(22, "test2"); cache.put("/one/two", "key1", pojo2); assertEquals(null, dummy.getCalledCommand()); TransactionTable table = cache.getTransactionTable(); GlobalTransaction gtx = table.get(tx); OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table.get(gtx); @SuppressWarnings("unchecked") TransactionWorkspace workspace = entry.getTransactionWorkSpace(); assertEquals(pojo2, cache.get("/one/two", "key1")); mgr.commit(); //assert what should be the results of our call assertEquals(3, workspace.getNodes().size()); assertNotNull(workspace.getNode(Fqn.fromString("/one/two"))); assertEquals(pojo2, workspace.getNode(Fqn.fromString("/one/two")).get("key1")); assertTrue(entry.getLocks().isEmpty()); assertEquals(2, entry.getModifications().size()); assertTrue(!cache.exists("/one/two")); assertEquals(null, dummy.getCalledCommand()); } public void testTransactionGetKeyValOverwriteNullMethod() throws Exception { mgr.begin(); Transaction tx = mgr.getTransaction(); // inject InvocationContext setupTransactions(cache, tx); SamplePojo pojo = new SamplePojo(21, "test"); cache.put("/one/two", "key1", pojo); cache.put("/one/two", "key1", null); assertEquals(null, dummy.getCalledCommand()); TransactionTable table = cache.getTransactionTable(); GlobalTransaction gtx = table.get(tx); OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table.get(gtx); @SuppressWarnings("unchecked") TransactionWorkspace workspace = entry.getTransactionWorkSpace(); assertEquals(null, cache.get("/one/two", "key1")); mgr.commit(); //assert what should be the results of our call assertEquals(3, workspace.getNodes().size()); assertNotNull(workspace.getNode(Fqn.fromString("/one/two"))); assertEquals(null, workspace.getNode(Fqn.fromString("/one/two")).get("key1")); assertTrue(entry.getLocks().isEmpty()); assertEquals(2, entry.getModifications().size()); assertTrue(!cache.exists("/one/two")); assertEquals(null, dummy.getCalledCommand()); } public void testTwoTransactionGetIsolationKeyValMethod() throws Exception { mgr.begin(); Transaction tx = mgr.getTransaction(); // inject InvocationContext setupTransactions(cache, tx); SamplePojo pojo = new SamplePojo(21, "test"); cache.put("/one/two", "key1", pojo); assertEquals(pojo, cache.get("/one/two", "key1")); //suspend current transaction mgr.suspend(); //start a new transaction mgr.begin(); Transaction tx2 = mgr.getTransaction(); // inject InvocationContext setupTransactions(cache, tx2); SamplePojo pojo2 = new SamplePojo(22, "test2"); cache.put("/one/two", "key2", pojo2); assertEquals(null, cache.get("/one/two", "key1")); assertEquals(pojo2, cache.get("/one/two", "key2")); assertEquals(null, dummy.getCalledCommand()); TransactionTable table = cache.getTransactionTable(); GlobalTransaction gtx = table.get(tx); OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table.get(gtx); @SuppressWarnings("unchecked") TransactionWorkspace workspace = entry.getTransactionWorkSpace(); //resume the suspended transaction GlobalTransaction gtx2 = table.get(tx2); OptimisticTransactionEntry entry2 = (OptimisticTransactionEntry) table.get(gtx2); @SuppressWarnings("unchecked") TransactionWorkspace workspace2 = entry2.getTransactionWorkSpace(); //commit both tx mgr.commit(); mgr.resume(tx); mgr.commit(); //assert that our keys are in one space assertEquals(3, workspace.getNodes().size()); assertNotNull(workspace.getNode(Fqn.fromString("/one/two"))); assertEquals(pojo, workspace.getNode(Fqn.fromString("/one/two")).get("key1")); assertEquals(null, workspace.getNode(Fqn.fromString("/one/two")).get("key2")); assertTrue(entry.getLocks().isEmpty()); assertEquals(1, entry.getModifications().size()); //assert that our keys are in one space assertEquals(3, workspace2.getNodes().size()); assertNotNull(workspace2.getNode(Fqn.fromString("/one/two"))); assertEquals(null, workspace2.getNode(Fqn.fromString("/one/two")).get("key1")); assertEquals(pojo2, workspace2.getNode(Fqn.fromString("/one/two")).get("key2")); assertTrue(entry2.getLocks().isEmpty()); assertEquals(1, entry2.getModifications().size()); assertTrue(!cache.exists("/one/two")); assertEquals(null, dummy.getCalledCommand()); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/optimistic/OptimisticVersioningTest.java0000644000175000017500000001151711003316744032517 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.optimistic; import junit.framework.Assert; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.Node; import org.jboss.cache.NodeSPI; import org.jboss.cache.config.Configuration; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertTrue; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.RollbackException; import javax.transaction.Transaction; import javax.transaction.TransactionManager; /** * Unit test that covers versioning of data and workspace nodes when using optimistic locking. * * @author Manik Surtani (manik@jboss.org) */ @SuppressWarnings("unchecked") @Test(groups = "functional") public class OptimisticVersioningTest extends AbstractOptimisticTestCase { private static final Log log = LogFactory.getLog(OptimisticVersioningTest.class); CacheSPI cache1, cache2; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { cache1 = createReplicatedCache(Configuration.CacheMode.REPL_SYNC); cache2 = createReplicatedCache(Configuration.CacheMode.REPL_SYNC); } @AfterMethod(alwaysRun = true) public void tearDown() { super.tearDown(); destroyCache(cache1); destroyCache(cache2); cache1 = null; cache2 = null; } public void testVersionPropagation() { Fqn fqn = Fqn.fromString("/a/b"); String key = "key"; cache1.put(fqn, key, "value"); DataVersion v1 = ((NodeSPI) cache1.getNode(fqn)).getVersion(); DataVersion v2 = ((NodeSPI) cache2.getNode(fqn)).getVersion(); assertEquals("value", cache1.get(fqn, key)); assertEquals("value", cache2.get(fqn, key)); assertEquals("Version info should have propagated", v1, v2); // change stuff in the node again... cache1.put(fqn, key, "value2"); v1 = cache1.getNode(fqn).getVersion(); v2 = cache2.getNode(fqn).getVersion(); assertEquals("value2", cache1.get(fqn, key)); assertEquals("value2", cache2.get(fqn, key)); assertEquals("Version info should have propagated", v1, v2); } public void testTwoCachesUpdatingSimultaneously() throws Exception { TransactionManager mgr1 = cache1.getTransactionManager(); TransactionManager mgr2 = cache2.getTransactionManager(); Transaction tx1, tx2; Fqn fqn = Fqn.fromString("/a/b"); String key = "key"; cache1.put(fqn, key, "value"); DataVersion v1 = cache1.getNode(fqn).getVersion(); DataVersion v2 = cache2.getNode(fqn).getVersion(); assertEquals("value", cache1.get(fqn, key)); assertEquals("value", cache2.get(fqn, key)); assertEquals("Version info should have propagated", v1, v2); // Start a tx on cache 1 mgr1.begin(); cache1.put(fqn, key, "value2"); tx1 = mgr1.suspend(); // start a tx on cache 2 mgr2.begin(); cache2.put(fqn, key, "value3"); tx2 = mgr2.suspend(); // which tx completes, which fail? mgr1.resume(tx1); // should succeed... mgr1.commit(); try { mgr2.resume(tx2); mgr2.commit(); assertTrue("Should have failed", false); } catch (RollbackException rbe) { assertTrue("Should have failed", true); } // data versions should be in sync. v1 = ((NodeSPI) cache1.getNode(fqn)).getVersion(); v2 = ((NodeSPI) cache2.getNode(fqn)).getVersion(); assertEquals("Version info should have propagated", v1, v2); } public void testRemovalWithSpecifiedVersion() throws Exception { Fqn fqn = Fqn.fromString("/test/node"); Node root = cache1.getRoot(); cache1.getInvocationContext().getOptionOverrides().setDataVersion(new NonLockingDataVersion()); root.addChild(fqn); cache1.getInvocationContext().getOptionOverrides().setDataVersion(new NonLockingDataVersion()); cache1.removeNode(fqn); Assert.assertNull(cache1.getRoot().getChild(fqn)); } private static class NonLockingDataVersion implements DataVersion { /** * The serialVersionUID */ private static final long serialVersionUID = 1L; public boolean newerThan(DataVersion dataVersion) { if (dataVersion instanceof DefaultDataVersion) { log.info("unexpectedly validating against a DefaultDataVersion", new Exception("Just a stack trace")); } else { log.trace("non locking lock check..."); } return false; } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/optimistic/MockInterceptor.java0000755000175000017500000000324611005354146030602 0ustar twernertwernerpackage org.jboss.cache.optimistic; import org.jboss.cache.InvocationContext; import org.jboss.cache.commands.ReplicableCommand; import org.jboss.cache.commands.VisitableCommand; import org.jboss.cache.interceptors.base.CommandInterceptor; import java.util.ArrayList; import java.util.List; /** * Handles putXXX() methods: if the given node doesn't exist, it will be created * (depending on the create_if_not_exists argument) * * @author Bela Ban * @version $Id: CreateIfNotExistsInterceptor.java,v 1.7 2005/01/26 11:45:14 * belaban Exp $ */ public class MockInterceptor extends CommandInterceptor { ReplicableCommand calledCommand; private List> calledlist = new ArrayList>(); private List calledIdsList = new ArrayList(); @Override public synchronized Object handleDefault(InvocationContext ctx, VisitableCommand command) throws Throwable { calledlist.add(command.getClass()); calledIdsList.add(command.getCommandId()); calledCommand = command; return null; } /** * @return Returns the called. */ public ReplicableCommand getCalledCommand() { return calledCommand; } public Class getCalledCommandClass() { return calledCommand.getClass(); } public List> getAllCalled() { return calledlist; } public List getAllCalledIds() { return calledIdsList; } /** * @param called The called to set. */ public void setCalled(ReplicableCommand called) { this.calledCommand = called; } }././@LongLink0000000000000000000000000000014700000000000011567 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/optimistic/NodeInterceptorRemoveKeyValTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/optimistic/NodeInterceptorRemoveKeyValTest.ja0000755000175000017500000001221311017455042033373 0ustar twernertwerner/* * Created on 17-Feb-2005 * * * */ package org.jboss.cache.optimistic; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.interceptors.OptimisticCreateIfNotExistsInterceptor; import org.jboss.cache.interceptors.OptimisticNodeInterceptor; import org.jboss.cache.interceptors.base.CommandInterceptor; import org.jboss.cache.loader.SamplePojo; import org.jboss.cache.transaction.GlobalTransaction; import org.jboss.cache.transaction.OptimisticTransactionEntry; import org.jboss.cache.transaction.TransactionTable; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.Transaction; import javax.transaction.TransactionManager; import java.util.HashMap; import java.util.Map; /** * @author xenephon */ @SuppressWarnings("unchecked") @Test(groups = "functional") public class NodeInterceptorRemoveKeyValTest extends AbstractOptimisticTestCase { private CacheSPI cache; private TransactionManager mgr; private TestListener listener; private MockInterceptor dummy; @BeforeMethod public void setUp() throws Exception { listener = new TestListener(); cache = createCacheWithListener(listener); CommandInterceptor interceptor = TestingUtil.findInterceptor(cache, OptimisticCreateIfNotExistsInterceptor.class); CommandInterceptor nodeInterceptor = TestingUtil.findInterceptor(cache, OptimisticNodeInterceptor.class); dummy = new MockInterceptor(); interceptor.setNext(nodeInterceptor); nodeInterceptor.setNext(dummy); TestingUtil.replaceInterceptorChain(cache, interceptor); mgr = cache.getTransactionManager(); } @AfterMethod public void tearDown() { TestingUtil.killCaches(cache); } public void testTransactionRemoveNoNodeKeyValMethod() throws Exception { mgr.begin(); Transaction tx = mgr.getTransaction(); setupTransactions(cache, tx); cache.remove("/one/two", "keyOne"); assertEquals(null, dummy.getCalledCommand()); TransactionTable table = cache.getTransactionTable(); GlobalTransaction gtx = table.get(tx); OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table.get(gtx); TransactionWorkspace workspace = entry.getTransactionWorkSpace(); mgr.commit(); //assert what should be the results of our call assertEquals(0, workspace.getNodes().size()); assertNull(workspace.getNode(Fqn.fromString("/one/two"))); assertTrue(entry.getLocks().isEmpty()); assertEquals(1, entry.getModifications().size()); assertTrue(!cache.exists("/one/two")); assertEquals(null, dummy.getCalledCommand()); } public void testTransactionRemoveNoKeyValMethod() throws Exception { mgr.begin(); Transaction tx = mgr.getTransaction(); setupTransactions(cache, tx); SamplePojo pojo = new SamplePojo(21, "test"); Map temp = new HashMap(); temp.put("key1", pojo); cache.put("/one/two", temp); cache.remove("/one/two", "key2"); assertEquals(null, dummy.getCalledCommand()); TransactionTable table = cache.getTransactionTable(); GlobalTransaction gtx = table.get(tx); OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table.get(gtx); TransactionWorkspace workspace = entry.getTransactionWorkSpace(); mgr.commit(); //assert what should be the results of our call assertEquals(3, workspace.getNodes().size()); assertNotNull(workspace.getNode(Fqn.fromString("/one/two"))); assertNotNull(workspace.getNode(Fqn.fromString("/one/two")).get("key1")); assertNull(workspace.getNode(Fqn.fromString("/one/two")).get("key2")); assertTrue(entry.getLocks().isEmpty()); assertEquals(2, entry.getModifications().size()); assertTrue(!cache.exists("/one/two")); assertEquals(null, dummy.getCalledCommand()); } public void testTransactionRemoveKeyValMethod() throws Exception { mgr.begin(); Transaction tx = mgr.getTransaction(); setupTransactions(cache, tx); SamplePojo pojo = new SamplePojo(21, "test"); Map temp = new HashMap(); temp.put("key1", pojo); cache.put("/one/two", temp); cache.remove("/one/two", "key1"); assertEquals(null, dummy.getCalledCommand()); TransactionTable table = cache.getTransactionTable(); GlobalTransaction gtx = table.get(tx); OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table.get(gtx); TransactionWorkspace workspace = entry.getTransactionWorkSpace(); mgr.commit(); //assert what should be the results of our call assertEquals(3, workspace.getNodes().size()); assertNotNull(workspace.getNode(Fqn.fromString("/one/two"))); assertNull(workspace.getNode(Fqn.fromString("/one/two")).get("key1")); assertTrue(entry.getLocks().isEmpty()); assertEquals(2, entry.getModifications().size()); assertTrue(!cache.exists("/one/two")); assertEquals(null, dummy.getCalledCommand()); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/optimistic/MockFailureInterceptor.java0000755000175000017500000000375711005354146032121 0ustar twernertwernerpackage org.jboss.cache.optimistic; import org.jboss.cache.InvocationContext; import org.jboss.cache.commands.ReplicableCommand; import org.jboss.cache.commands.VisitableCommand; import org.jboss.cache.interceptors.base.CommandInterceptor; import java.util.ArrayList; import java.util.List; /** * Handles putXXX() methods: if the given node doesn't exist, it will be created * (depending on the create_if_not_exists argument) * * @author Bela Ban * @version $Id: CreateIfNotExistsInterceptor.java,v 1.7 2005/01/26 11:45:14 * belaban Exp $ */ public class MockFailureInterceptor extends CommandInterceptor { private List> allCalled = new ArrayList>(); private List> failurelist = new ArrayList>(); private List allCalledIdsList = new ArrayList(); @Override public Object handleDefault(InvocationContext ctx, VisitableCommand command) throws Throwable { if (failurelist.contains(command.getClass())) throw new Exception("Failure in method " + command); allCalled.add(command.getClass()); allCalledIdsList.add(command.getCommandId()); return null; } /** * @return Returns the failurelist. */ public List> getFailurelist() { return failurelist; } /** * @param failurelist The failurelist to set. */ public void setFailurelist(List> failurelist) { this.failurelist = failurelist; } /** * @return Returns the called. */ public List> getAllCalled() { return allCalled; } /** * @param called The called to set. */ public void setAllCalled(List> called) { this.allCalled = called; } public List getAllCalledIds() { return allCalledIdsList; } }jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/optimistic/NodeInterceptorGetKeysTest.java0000644000175000017500000001166611017455042032734 0ustar twernertwernerpackage org.jboss.cache.optimistic; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.interceptors.OptimisticCreateIfNotExistsInterceptor; import org.jboss.cache.interceptors.OptimisticNodeInterceptor; import org.jboss.cache.interceptors.base.CommandInterceptor; import org.jboss.cache.loader.SamplePojo; import org.jboss.cache.transaction.GlobalTransaction; import org.jboss.cache.transaction.OptimisticTransactionEntry; import org.jboss.cache.transaction.TransactionTable; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.Transaction; import javax.transaction.TransactionManager; /** * @author xenephon */ @Test(groups = "functional") public class NodeInterceptorGetKeysTest extends AbstractOptimisticTestCase { private TestListener listener; private CacheSPI cache; private TransactionManager mgr; private MockInterceptor dummy; @BeforeMethod public void setUp() throws Exception { listener = new TestListener(); cache = createCacheWithListener(listener); CommandInterceptor interceptor = TestingUtil.findInterceptor(cache, OptimisticCreateIfNotExistsInterceptor.class); CommandInterceptor nodeInterceptor = TestingUtil.findInterceptor(cache, OptimisticNodeInterceptor.class); dummy = new MockInterceptor(); interceptor.setNext(nodeInterceptor); nodeInterceptor.setNext(dummy); TestingUtil.replaceInterceptorChain(cache, interceptor); mgr = cache.getTransactionManager(); } @AfterMethod public void tearDown() { TestingUtil.killCaches(cache); } public void testTransactionGetKeysMethod() throws Exception { mgr.begin(); Transaction tx = mgr.getTransaction(); setupTransactions(cache, tx); SamplePojo pojo = new SamplePojo(21, "test"); cache.put("/one/two", "key1", pojo); assertEquals(null, dummy.getCalledCommand()); TransactionTable table = cache.getTransactionTable(); GlobalTransaction gtx = table.get(tx); OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table.get(gtx); @SuppressWarnings("unchecked") TransactionWorkspace workspace = entry.getTransactionWorkSpace(); //assert we can see this with a key value get in the transaction assertEquals(1, cache.getNode("/one/two").getKeys().size()); mgr.commit(); //assert what should be the results of our call assertEquals(3, workspace.getNodes().size()); assertNotNull(workspace.getNode(Fqn.fromString("/one/two"))); assertEquals(pojo, workspace.getNode(Fqn.fromString("/one/two")).get("key1")); assertTrue(entry.getLocks().isEmpty()); assertEquals(1, entry.getModifications().size()); assertTrue(!cache.exists("/one/two")); assertEquals(null, dummy.getCalledCommand()); //assert that we cannot see the change if we have not put it into the cache // we need to do this as we do not have the tx interceptor in this stack mgr.begin(); Transaction tx2 = mgr.getTransaction(); setupTransactions(cache, tx2); assertNull(cache.get("/one/two", "key1")); mgr.commit(); } public void testTransactionGetNoKeysMethod() throws Exception { mgr.begin(); Transaction tx = mgr.getTransaction(); setupTransactions(cache, tx); assertEquals(null, dummy.getCalledCommand()); TransactionTable table = cache.getTransactionTable(); GlobalTransaction gtx = table.get(tx); OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table.get(gtx); //assert we can see this with a key value get in the transaction assertEquals(0, cache.getRoot().getKeys().size()); mgr.commit(); assertTrue(entry.getLocks().isEmpty()); assertEquals(0, entry.getModifications().size()); assertTrue(!cache.exists("/one/two")); assertEquals(null, dummy.getCalledCommand()); } public void testTransactionGetKeysIteratorMethod() throws Exception { mgr.begin(); Transaction tx = mgr.getTransaction(); setupTransactions(cache, tx); SamplePojo pojo = new SamplePojo(21, "test"); cache.put("/one/two", "key1", pojo); assertEquals(null, dummy.getCalledCommand()); TransactionTable table = cache.getTransactionTable(); GlobalTransaction gtx = table.get(tx); OptimisticTransactionEntry entry = (OptimisticTransactionEntry) table.get(gtx); //assert we can see this with a key value get in the transaction assertEquals(1, cache.getNode("/one/two").getKeys().size()); mgr.commit(); assertTrue(entry.getLocks().isEmpty()); assertEquals(1, entry.getModifications().size()); assertTrue(!cache.exists("/one/two")); assertEquals(null, dummy.getCalledCommand()); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/optimistic/AsyncCacheTest.java0000644000175000017500000001053211021015774030323 0ustar twernertwerner/* * Created on 17-Feb-2005 * */ package org.jboss.cache.optimistic; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.config.Configuration; import org.jboss.cache.loader.SamplePojo; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.transaction.TransactionSetup; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.Transaction; import javax.transaction.TransactionManager; @Test(groups = {"functional", "transaction"}) public class AsyncCacheTest extends AbstractOptimisticTestCase { private CacheSPI cache, cache2; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { cache = createReplicatedCache(Configuration.CacheMode.REPL_ASYNC); cache2 = createReplicatedCache(Configuration.CacheMode.REPL_ASYNC); } @AfterMethod(alwaysRun = true) public void tearDown() { super.tearDown(); destroyCache(cache); destroyCache(cache2); cache = null; cache2 = null; } @Override protected CacheSPI createCacheUnstarted(boolean optimistic) throws Exception { CacheSPI cache = super.createCacheUnstarted(optimistic); cache.getConfiguration().setTransactionManagerLookupClass(TransactionSetup.getManagerLookup()); return cache; } public void testRemoteCacheBroadcast() throws Exception { assertEquals(2, cache.getMembers().size()); assertEquals(2, cache2.getMembers().size()); TransactionManager mgr = cache.getConfiguration().getRuntimeConfig().getTransactionManager(); //start local transaction mgr.begin(); //this sets SamplePojo pojo = new SamplePojo(21, "test"); cache.put("/one/two", "key1", pojo); //GlobalTransaction globalTransaction = cache.getCurrentTransaction(tx); assertNotNull(mgr.getTransaction()); mgr.commit(); assertNull(mgr.getTransaction()); //assert that the local cache is in the right state assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache.getTransactionTable().getNumLocalTransactions()); assertTrue(cache.exists(Fqn.fromString("/one/two"))); assertTrue(cache.exists(Fqn.fromString("/one"))); assertEquals(pojo, cache.get(Fqn.fromString("/one/two"), "key1")); // allow changes to replicate since this is async TestingUtil.sleepThread((long) 1000); assertEquals(0, cache2.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache2.getTransactionTable().getNumLocalTransactions()); assertTrue(cache2.exists(Fqn.fromString("/one/two"))); assertTrue(cache2.exists(Fqn.fromString("/one"))); assertEquals(pojo, cache2.get(Fqn.fromString("/one/two"), "key1")); } public void testTwoWayRemoteCacheBroadcast() throws Exception { assertEquals(2, cache.getMembers().size()); assertEquals(2, cache2.getMembers().size()); TransactionManager mgr = cache.getConfiguration().getRuntimeConfig().getTransactionManager(); //start local transaction mgr.begin(); Transaction tx = mgr.getTransaction(); //this sets cache.getCurrentTransaction(tx, false); SamplePojo pojo = new SamplePojo(21, "test"); cache.put("/one/two", "key1", pojo); assertNotNull(mgr.getTransaction()); mgr.commit(); assertNull(mgr.getTransaction()); //assert that the local cache is in the right state assertEquals(0, cache.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache.getTransactionTable().getNumLocalTransactions()); assertTrue(cache.exists(Fqn.fromString("/one/two"))); assertTrue(cache.exists(Fqn.fromString("/one"))); assertEquals(pojo, cache.get(Fqn.fromString("/one/two"), "key1")); // let the async calls complete TestingUtil.sleepThread((long) 1000); assertEquals(0, cache2.getTransactionTable().getNumGlobalTransactions()); assertEquals(0, cache2.getTransactionTable().getNumLocalTransactions()); assertTrue(cache2.exists(Fqn.fromString("/one/two"))); assertTrue(cache2.exists(Fqn.fromString("/one"))); assertEquals(pojo, cache2.get(Fqn.fromString("/one/two"), "key1")); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/loader/0000755000175000017500000000000011376173763023720 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/loader/SamplePojo.java0000644000175000017500000000264610665031725026633 0ustar twernertwernerpackage org.jboss.cache.loader; import java.io.Serializable; import java.util.ArrayList; import java.util.List; /** * Sample object for testing. * * @author Bela Ban * @version $Id: SamplePojo.java 4444 2007-08-28 14:40:21Z jason.greene@jboss.com $ */ public class SamplePojo implements Serializable { private static final long serialVersionUID = -8505492581349365824L; int age; String name; List hobbies=new ArrayList(); public SamplePojo(int age, String name) { this.age=age; this.name=name; } public int getAge() { return age; } public void setAge(int age) { this.age=age; } public String getName() { return name; } public void setName(String name) { this.name=name; } public List getHobbies() { return hobbies; } public void setHobbies(List hobbies) { this.hobbies=hobbies; } public String toString() { return "name=" + name + ", age=" + age + ", hobbies=" + hobbies; } public boolean equals(Object o) { if (!(o instanceof SamplePojo)) { return false; } SamplePojo other = (SamplePojo) o; boolean equals = (name.equals(other.getName())) && (age == other.getAge()) && (hobbies.equals(other.getHobbies())); return equals; } public int hashCode() { return name.hashCode() ^ age ^ hobbies.hashCode(); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/loader/CacheLoaderManagerTest.java0000644000175000017500000010631111013534365031035 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.loader; import org.jboss.cache.CacheSPI; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.factories.XmlConfigurationParser; import org.jboss.cache.xml.XmlHelper; import static org.testng.AssertJUnit.*; import org.testng.annotations.Test; import org.w3c.dom.Element; import java.util.List; import java.util.Properties; /** * Tests the construction of a cache laoder based on an XML element passed in. * * @author Manik Surtani (manik@jboss.org) * @author Galder Zamarreno */ @Test(groups = "functional") public class CacheLoaderManagerTest extends AbstractCacheLoaderTestBase { private CacheLoaderConfig createCacheLoaderCfg(boolean passivation) { CacheLoaderConfig cfg = new CacheLoaderConfig(); cfg.setPassivation(passivation); return cfg; } private CacheLoaderConfig.IndividualCacheLoaderConfig createIndividualCacheLoaderConfig(CacheLoaderConfig parent, boolean async, String classname) { CacheLoaderConfig.IndividualCacheLoaderConfig cfg = new CacheLoaderConfig.IndividualCacheLoaderConfig(); cfg.setAsync(async); cfg.setClassName(classname); cfg.setFetchPersistentState(false); Properties p = new Properties(); p.setProperty("location", getTempDir()); p.setProperty("cache.jdbc.driver", "com.mysql.jdbc.Driver"); p.setProperty("cache.jdbc.url", "jdbc:mysql://localhost/test"); p.setProperty("cache.jdbc.user", "user"); p.setProperty("cache.jdbc.password", "pwd"); cfg.setProperties(p); return cfg; } private String getTempDir() { return System.getProperty("java.io.tempdir", "/tmp"); } private static Element strToElement(String s) throws Exception { return XmlHelper.stringToElement(s); } public void testSingleCacheLoader() throws Exception { // without async CacheLoaderManager mgr = new CacheLoaderManager(); CacheLoaderConfig cfg = createCacheLoaderCfg(false); cfg.addIndividualCacheLoaderConfig(createIndividualCacheLoaderConfig(cfg, false, "org.jboss.cache.loader.FileCacheLoader")); mgr.setConfig(cfg, null, null); CacheLoader cl = mgr.getCacheLoader(); assertEquals(FileCacheLoader.class, cl.getClass()); // with async cfg = createCacheLoaderCfg(false); cfg.addIndividualCacheLoaderConfig(createIndividualCacheLoaderConfig(cfg, true, "org.jboss.cache.loader.FileCacheLoader")); mgr.setConfig(cfg, null, null); cl = mgr.getCacheLoader(); assertEquals(AsyncCacheLoader.class, cl.getClass()); } public void testSingleCacheLoaderPassivation() throws Exception { // without async CacheLoaderConfig cfg = createCacheLoaderCfg(true); cfg.addIndividualCacheLoaderConfig(createIndividualCacheLoaderConfig(cfg, false, "org.jboss.cache.loader.FileCacheLoader")); cfg.addIndividualCacheLoaderConfig(createIndividualCacheLoaderConfig(cfg, false, "org.jboss.cache.loader.bdbje.BdbjeCacheLoader")); cfg.addIndividualCacheLoaderConfig(createIndividualCacheLoaderConfig(cfg, false, "org.jboss.cache.loader.JDBCCacheLoader")); CacheLoaderManager mgr = new CacheLoaderManager(); mgr.setConfig(cfg, null, null); CacheLoader cl = mgr.getCacheLoader(); assertEquals(FileCacheLoader.class, cl.getClass()); // with async cfg = createCacheLoaderCfg(true); cfg.addIndividualCacheLoaderConfig(createIndividualCacheLoaderConfig(cfg, true, "org.jboss.cache.loader.FileCacheLoader")); cfg.addIndividualCacheLoaderConfig(createIndividualCacheLoaderConfig(cfg, true, "org.jboss.cache.loader.bdbje.BdbjeCacheLoader")); cfg.addIndividualCacheLoaderConfig(createIndividualCacheLoaderConfig(cfg, true, "org.jboss.cache.loader.JDBCCacheLoader")); mgr.setConfig(cfg, null, null); cl = mgr.getCacheLoader(); assertEquals(AsyncCacheLoader.class, cl.getClass()); } public void testSingleCacheLoaderFromXml() throws Exception { // without async CacheLoaderConfig clc = getSingleCacheLoaderConfig("", "org.jboss.cache.loader.FileCacheLoader", "location=" + getTempDir(), false, false, false); CacheLoaderManager mgr = new CacheLoaderManager(); mgr.setConfig(clc, null, null); CacheLoader cl = mgr.getCacheLoader(); assertEquals(FileCacheLoader.class, cl.getClass()); // with async clc = getSingleCacheLoaderConfig("", "org.jboss.cache.loader.FileCacheLoader", "location=" + getTempDir(), true, false, false); mgr.setConfig(clc, null, null); cl = mgr.getCacheLoader(); assertEquals(AsyncCacheLoader.class, cl.getClass()); } public void testSingleCacheLoaderPassivationFromXml() throws Exception { // without async String conf = "" + "true" + "" + " org.jboss.cache.loader.FileCacheLoader" + " false" + " false" + " false" + " " + " location=" + getTempDir() + " " + "" + "" + " org.jboss.cache.loader.bdbje.BdbjeCacheLoader" + " false" + " false" + " false" + " " + " location=" + getTempDir() + " " + "" + "" + " org.jboss.cache.loader.JDBCCacheLoader" + " false" + " false" + " false" + " " + " location=" + getTempDir() + " " + ""; CacheLoaderConfig clc = XmlConfigurationParser.parseCacheLoaderConfig(strToElement(conf)); CacheLoaderManager mgr = new CacheLoaderManager(); mgr.setConfig(clc, null, null); CacheLoader cl = mgr.getCacheLoader(); assertEquals(FileCacheLoader.class, cl.getClass()); // with async conf = "true" + "" + " org.jboss.cache.loader.FileCacheLoader" + " true" + " false" + " false" + " " + " location=" + getTempDir() + " " + "" + "" + " org.jboss.cache.loader.bdbje.BdbjeCacheLoader" + " true" + " false" + " false" + " " + " location=" + getTempDir() + " " + "" + "" + " org.jboss.cache.loader.JDBCCacheLoader" + " true" + " false" + " false" + " " + " location=" + getTempDir() + " " + ""; clc = XmlConfigurationParser.parseCacheLoaderConfig(strToElement(conf)); mgr.setConfig(clc, null, null); cl = mgr.getCacheLoader(); assertEquals(AsyncCacheLoader.class, cl.getClass()); } public void testChainingCacheLoader() throws Exception { // async = false CacheLoaderConfig cfg = createCacheLoaderCfg(false); cfg.addIndividualCacheLoaderConfig(createIndividualCacheLoaderConfig(cfg, false, "org.jboss.cache.loader.FileCacheLoader")); cfg.addIndividualCacheLoaderConfig(createIndividualCacheLoaderConfig(cfg, false, "org.jboss.cache.loader.JDBCCacheLoader")); CacheLoaderManager mgr = new CacheLoaderManager(); mgr.setConfig(cfg, null, null); CacheLoader cl = mgr.getCacheLoader(); assertEquals(ChainingCacheLoader.class, cl.getClass()); assertEquals(2, ((ChainingCacheLoader) cl).getSize()); List loaders = ((ChainingCacheLoader) cl).getCacheLoaders(); assertEquals(FileCacheLoader.class, loaders.get(0).getClass()); assertEquals(JDBCCacheLoader.class, loaders.get(1).getClass()); // async = true cfg = createCacheLoaderCfg(false); cfg.addIndividualCacheLoaderConfig(createIndividualCacheLoaderConfig(cfg, false, "org.jboss.cache.loader.FileCacheLoader")); cfg.addIndividualCacheLoaderConfig(createIndividualCacheLoaderConfig(cfg, true, "org.jboss.cache.loader.JDBCCacheLoader")); mgr.setConfig(cfg, null, null); cl = mgr.getCacheLoader(); assertEquals(ChainingCacheLoader.class, cl.getClass()); assertEquals(2, ((ChainingCacheLoader) cl).getSize()); loaders = ((ChainingCacheLoader) cl).getCacheLoaders(); assertEquals(FileCacheLoader.class, loaders.get(0).getClass()); assertEquals(AsyncCacheLoader.class, loaders.get(1).getClass()); } public void testChainingCacheLoaderFromXml() throws Exception { // async = false String conf = "false" + "" + " org.jboss.cache.loader.FileCacheLoader" + " false" + " false" + " false" + " " + " location=" + getTempDir() + " " + "" + "" + " org.jboss.cache.loader.JDBCCacheLoader" + " false" + " false" + " false" + " " + "cache.jdbc.driver=com.mysql.jdbc.Driver\ncache.jdbc.url=jdbc:mysql://localhost/test\ncache.jdbc.user=user\ncache.jdbc.password=pwd" + " " + ""; CacheLoaderConfig clc = XmlConfigurationParser.parseCacheLoaderConfig(strToElement(conf)); CacheLoaderManager mgr = new CacheLoaderManager(); mgr.setConfig(clc, null, null); CacheLoader cl = mgr.getCacheLoader(); assertEquals(ChainingCacheLoader.class, cl.getClass()); assertEquals(2, ((ChainingCacheLoader) cl).getSize()); List loaders = ((ChainingCacheLoader) cl).getCacheLoaders(); assertEquals(FileCacheLoader.class, loaders.get(0).getClass()); assertEquals(JDBCCacheLoader.class, loaders.get(1).getClass()); // async = true conf = "" + "false" + "" + " org.jboss.cache.loader.FileCacheLoader" + " false" + " false" + " false" + " " + " location=" + getTempDir() + " " + "" + "" + " org.jboss.cache.loader.JDBCCacheLoader" + " true" + " false" + " false" + " " + "cache.jdbc.driver=com.mysql.jdbc.Driver\ncache.jdbc.url=jdbc:mysql://localhost/test\ncache.jdbc.user=user\ncache.jdbc.password=pwd" + " " + ""; clc = XmlConfigurationParser.parseCacheLoaderConfig(strToElement(conf)); mgr.setConfig(clc, null, null); cl = mgr.getCacheLoader(); assertEquals(ChainingCacheLoader.class, cl.getClass()); assertEquals(2, ((ChainingCacheLoader) cl).getSize()); loaders = ((ChainingCacheLoader) cl).getCacheLoaders(); assertEquals(FileCacheLoader.class, loaders.get(0).getClass()); assertEquals(AsyncCacheLoader.class, loaders.get(1).getClass()); } public void testMoreThanOneFetchPersistentState() throws Exception { CacheLoaderManager mgr = new CacheLoaderManager(); CacheLoaderConfig cfg = createCacheLoaderCfg(false); CacheLoaderConfig.IndividualCacheLoaderConfig i = createIndividualCacheLoaderConfig(cfg, true, "org.jboss.cache.loader.FileCacheLoader"); i.setFetchPersistentState(true); CacheLoaderConfig.IndividualCacheLoaderConfig i2 = createIndividualCacheLoaderConfig(cfg, true, "org.jboss.cache.loader.FileCacheLoader"); i2.setFetchPersistentState(true); cfg.addIndividualCacheLoaderConfig(i); cfg.addIndividualCacheLoaderConfig(i2); assertEquals(2, cfg.getIndividualCacheLoaderConfigs().size()); try { mgr.setConfig(cfg, null, null); assertTrue("Should throw exception since we have > 1 cache loader with fetchPersistentState as true", false); } catch (Exception e) { assertTrue(true); } // control cases which should not throw exceptions mgr = new CacheLoaderManager(); cfg = createCacheLoaderCfg(false); i = createIndividualCacheLoaderConfig(cfg, true, "org.jboss.cache.loader.FileCacheLoader"); i.setFetchPersistentState(true); cfg.addIndividualCacheLoaderConfig(i); assertEquals(1, cfg.getIndividualCacheLoaderConfigs().size()); mgr.setConfig(cfg, null, null); // control cases which should not throw exceptions mgr = new CacheLoaderManager(); cfg = createCacheLoaderCfg(false); i = createIndividualCacheLoaderConfig(cfg, true, "org.jboss.cache.loader.FileCacheLoader"); i.setFetchPersistentState(true); i2 = createIndividualCacheLoaderConfig(cfg, true, "org.jboss.cache.loader.FileCacheLoader"); i2.setFetchPersistentState(false); cfg.addIndividualCacheLoaderConfig(i); cfg.addIndividualCacheLoaderConfig(i2); assertEquals(2, cfg.getIndividualCacheLoaderConfigs().size()); mgr.setConfig(cfg, null, null); // control cases which should not throw exceptions mgr = new CacheLoaderManager(); cfg = createCacheLoaderCfg(false); i = createIndividualCacheLoaderConfig(cfg, true, "org.jboss.cache.loader.FileCacheLoader"); i.setFetchPersistentState(false); i2 = createIndividualCacheLoaderConfig(cfg, true, "org.jboss.cache.loader.FileCacheLoader"); i2.setFetchPersistentState(false); cfg.addIndividualCacheLoaderConfig(i); cfg.addIndividualCacheLoaderConfig(i2); assertEquals(2, cfg.getIndividualCacheLoaderConfigs().size()); mgr.setConfig(cfg, null, null); } public void testConfigurationParsing() throws Exception { String conf = "" + "false" + "/, /blah, /blah2" + "" + " org.jboss.cache.loader.FileCacheLoader" + " false" + " false" + " true" + " " + " location=" + getTempDir() + " " + "" + "" + " org.jboss.cache.loader.JDBCCacheLoader" + " true" + " true" + " false" + " " + "cache.jdbc.driver=com.mysql.jdbc.Driver\ncache.jdbc.url=jdbc:mysql://localhost/test\ncache.jdbc.user=user\ncache.jdbc.password=pwd" + " " + ""; CacheLoaderConfig clc = XmlConfigurationParser.parseCacheLoaderConfig(strToElement(conf)); CacheLoaderManager mgr = new CacheLoaderManager(); mgr.setConfig(clc, null, null); assertEquals(ChainingCacheLoader.class, mgr.getCacheLoader().getClass()); assertTrue("Should be true", mgr.isFetchPersistentState()); assertTrue("Passivation shuld be false", !mgr.isPassivation()); CacheLoaderConfig c = mgr.getCacheLoaderConfig(); assertTrue("Should be using a chaining cache loader", c.useChainingCacheLoader()); assertEquals("/, /blah, /blah2", c.getPreload()); assertEquals(2, c.getIndividualCacheLoaderConfigs().size()); CacheLoaderConfig.IndividualCacheLoaderConfig icfg = c.getIndividualCacheLoaderConfigs().get(0); assertEquals("org.jboss.cache.loader.FileCacheLoader", icfg.getClassName()); assertTrue("Async shld be false", !icfg.isAsync()); assertTrue("fetchPersistentState shld be false", !icfg.isFetchPersistentState()); assertTrue("IgnoreMods should be true", icfg.isIgnoreModifications()); assertEquals(1, icfg.getProperties().size()); icfg = c.getIndividualCacheLoaderConfigs().get(1); assertEquals("org.jboss.cache.loader.JDBCCacheLoader", icfg.getClassName()); assertTrue("Async shld be true", icfg.isAsync()); assertTrue("fetchPersistentState shld be true", icfg.isFetchPersistentState()); assertTrue("IgnoreMods should be false", !icfg.isIgnoreModifications()); assertEquals(4, icfg.getProperties().size()); // fetch PersistentState shld be false now conf = "" + "false" + "/, /blah, /blah2" + "" + " org.jboss.cache.loader.FileCacheLoader" + " false" + " false" + " true" + " " + " location=" + getTempDir() + " " + "" + "" + " org.jboss.cache.loader.JDBCCacheLoader" + " true" + " false" + " false" + " " + "cache.jdbc.driver=com.mysql.jdbc.Driver\ncache.jdbc.url=jdbc:mysql://localhost/test\ncache.jdbc.user=user\ncache.jdbc.password=pwd" + " " + ""; clc = XmlConfigurationParser.parseCacheLoaderConfig(strToElement(conf)); mgr = new CacheLoaderManager(); mgr.setConfig(clc, null, null); assertTrue("Should be false", !mgr.isFetchPersistentState()); } public void testSingletonConfiguration() throws Exception { String conf = "" + "false" + "/, /blah, /blah2" + "" + " org.jboss.cache.loader.FileCacheLoader" + " false" + " false" + " true" + " " + " location=" + getTempDir() + " " + "" + ""; CacheLoaderConfig clc = XmlConfigurationParser.parseCacheLoaderConfig(strToElement(conf)); CacheLoaderManager mgr = new CacheLoaderManager(); mgr.setConfig(clc, null, null); CacheLoaderConfig.IndividualCacheLoaderConfig iclc = mgr.getCacheLoaderConfig().getFirstCacheLoaderConfig(); assertNull("Singleton has not been configured", iclc.getSingletonStoreConfig()); /************************************************************************************************************/ conf = "" + "false" + "/, /blah, /blah2" + "" + " org.jboss.cache.loader.FileCacheLoader" + " false" + " false" + " true" + " " + " location=" + getTempDir() + " " + " " + " false" + " " + "" + ""; clc = XmlConfigurationParser.parseCacheLoaderConfig(strToElement(conf)); mgr = new MockCacheLoaderManager(); mgr.setConfig(clc, null, null); iclc = mgr.getCacheLoaderConfig().getFirstCacheLoaderConfig(); assertNotNull("Singleton has been configured", iclc.getSingletonStoreConfig()); assertFalse("Singleton is not enabled", iclc.getSingletonStoreConfig().isSingletonStoreEnabled()); /************************************************************************************************************/ conf = "" + "false" + "/, /blah, /blah2" + "" + " org.jboss.cache.loader.FileCacheLoader" + " false" + " false" + " true" + " " + " location=" + getTempDir() + " " + " " + " true" + " " + "" + ""; clc = XmlConfigurationParser.parseCacheLoaderConfig(strToElement(conf)); mgr = new MockCacheLoaderManager(); mgr.setConfig(clc, null, null); iclc = mgr.getCacheLoaderConfig().getFirstCacheLoaderConfig(); assertNotNull("Singleton has been configured", iclc.getSingletonStoreConfig()); assertTrue("Singleton should enabled", iclc.getSingletonStoreConfig().isSingletonStoreEnabled()); assertEquals("Singleton class should be default", SingletonStoreCacheLoader.class.getName(), iclc.getSingletonStoreConfig().getSingletonStoreClass()); assertNotNull("Singleton properties should be not null", iclc.getSingletonStoreConfig().getSingletonStoreproperties()); assertTrue("Singleton properties should be empty", iclc.getSingletonStoreConfig().getSingletonStoreproperties().keySet().isEmpty()); SingletonStoreDefaultConfig ssdc = ((SingletonStoreCacheLoader) mgr.getCacheLoader()).getSingletonStoreDefaultConfig(); assertTrue("Singleton pushStateWhenCoordinator should be true (default)", ssdc.isPushStateWhenCoordinator()); assertEquals("Singleton pushStateWhenCoordinatorTimeout should be default value", 20000, ssdc.getPushStateWhenCoordinatorTimeout()); /************************************************************************************************************/ conf = "" + "false" + "/, /blah, /blah2" + "" + " org.jboss.cache.loader.FileCacheLoader" + " false" + " false" + " true" + " " + " location=" + getTempDir() + " " + " " + " true" + " org.jboss.cache.loader.CacheLoaderManagerTest$MockSingletonStoreCacheLoader" + " " + "" + ""; clc = XmlConfigurationParser.parseCacheLoaderConfig(strToElement(conf)); mgr = new CacheLoaderManager(); mgr.setConfig(clc, null, null); iclc = mgr.getCacheLoaderConfig().getFirstCacheLoaderConfig(); assertNotNull("Singleton has been configured", iclc.getSingletonStoreConfig()); assertTrue("Singleton should enabled", iclc.getSingletonStoreConfig().isSingletonStoreEnabled()); assertEquals("Singleton class should be a user defined one", MockSingletonStoreCacheLoader.class.getName(), iclc.getSingletonStoreConfig().getSingletonStoreClass()); assertNotNull("Singleton properties should be not null", iclc.getSingletonStoreConfig().getSingletonStoreproperties()); assertTrue("Singleton properties should be empty", iclc.getSingletonStoreConfig().getSingletonStoreproperties().keySet().isEmpty()); assertTrue(mgr.getCacheLoader() instanceof MockSingletonStoreCacheLoader); /************************************************************************************************************/ conf = "" + "false" + "/, /blah, /blah2" + "" + " org.jboss.cache.loader.FileCacheLoader" + " false" + " false" + " true" + " " + " location=" + getTempDir() + " " + " " + " false" + " org.jboss.cache.loader.FileCacheLoader" + " " + "" + ""; clc = XmlConfigurationParser.parseCacheLoaderConfig(strToElement(conf)); mgr = new CacheLoaderManager(); mgr.setConfig(clc, null, null); iclc = mgr.getCacheLoaderConfig().getFirstCacheLoaderConfig(); assertNotNull("Singleton has been configured", iclc.getSingletonStoreConfig()); assertFalse("Singleton is not enabled", iclc.getSingletonStoreConfig().isSingletonStoreEnabled()); /************************************************************************************************************/ conf = "" + "false" + "/, /blah, /blah2" + "" + " org.jboss.cache.loader.FileCacheLoader" + " false" + " false" + " true" + " " + " location=" + getTempDir() + " " + " " + " true" + " " + " " + "" + ""; clc = XmlConfigurationParser.parseCacheLoaderConfig(strToElement(conf)); mgr = new MockCacheLoaderManager(); mgr.setConfig(clc, null, null); iclc = mgr.getCacheLoaderConfig().getFirstCacheLoaderConfig(); assertNotNull("Singleton has been configured", iclc.getSingletonStoreConfig()); assertTrue("Singleton should enabled", iclc.getSingletonStoreConfig().isSingletonStoreEnabled()); assertEquals("Singleton class should be default", SingletonStoreCacheLoader.class.getName(), iclc.getSingletonStoreConfig().getSingletonStoreClass()); assertNotNull("Singleton properties should be defined, but empty", iclc.getSingletonStoreConfig().getSingletonStoreproperties()); ssdc = ((SingletonStoreCacheLoader) mgr.getCacheLoader()).getSingletonStoreDefaultConfig(); assertTrue("Singleton pushStateWhenCoordinator should be true", ssdc.isPushStateWhenCoordinator()); /************************************************************************************************************/ conf = "" + "false" + "/, /blah, /blah2" + "" + " org.jboss.cache.loader.FileCacheLoader" + " false" + " false" + " true" + " " + " location=" + getTempDir() + " " + " " + " true" + " " + " pushStateWhenCoordinator = false" + " " + " " + "" + ""; clc = XmlConfigurationParser.parseCacheLoaderConfig(strToElement(conf)); mgr = new MockCacheLoaderManager(); mgr.setConfig(clc, null, null); iclc = mgr.getCacheLoaderConfig().getFirstCacheLoaderConfig(); assertNotNull("Singleton has been configured", iclc.getSingletonStoreConfig()); assertTrue("Singleton should enabled", iclc.getSingletonStoreConfig().isSingletonStoreEnabled()); assertEquals("Singleton class should be default", SingletonStoreCacheLoader.class.getName(), iclc.getSingletonStoreConfig().getSingletonStoreClass()); assertNotNull("Singleton properties should be defined", iclc.getSingletonStoreConfig().getSingletonStoreproperties()); ssdc = ((SingletonStoreCacheLoader) mgr.getCacheLoader()).getSingletonStoreDefaultConfig(); assertFalse("Singleton pushStateWhenCoordinator should be false", ssdc.isPushStateWhenCoordinator()); /************************************************************************************************************/ conf = "" + "false" + "/, /blah, /blah2" + "" + " org.jboss.cache.loader.FileCacheLoader" + " false" + " false" + " true" + " " + " location=" + getTempDir() + " " + " " + " true" + " " + " pushStateWhenCoordinator = true\n" + " pushStateWhenCoordinatorTimeout = 5000\n" + " " + " " + "" + ""; clc = XmlConfigurationParser.parseCacheLoaderConfig(strToElement(conf)); mgr = new MockCacheLoaderManager(); mgr.setConfig(clc, null, null); iclc = mgr.getCacheLoaderConfig().getFirstCacheLoaderConfig(); assertNotNull("Singleton has been configured", iclc.getSingletonStoreConfig()); assertTrue("Singleton should enabled", iclc.getSingletonStoreConfig().isSingletonStoreEnabled()); assertEquals("Singleton class should be default", SingletonStoreCacheLoader.class.getName(), iclc.getSingletonStoreConfig().getSingletonStoreClass()); assertNotNull("Singleton properties should not be defined", iclc.getSingletonStoreConfig().getSingletonStoreproperties()); ssdc = ((SingletonStoreCacheLoader) mgr.getCacheLoader()).getSingletonStoreDefaultConfig(); assertTrue("Singleton pushStateWhenCoordinator should be true", ssdc.isPushStateWhenCoordinator()); assertEquals("Singleton pushStateWhenCoordinatorTimeout should be default value", 5000, ssdc.getPushStateWhenCoordinatorTimeout()); /************************************************************************************************************/ conf = "" + "false" + "true" + "/, /blah, /blah2" + "" + " org.jboss.cache.loader.FileCacheLoader" + " false" + " false" + " true" + " " + " location=" + getTempDir() + " " + " " + " true" + " " + "" + ""; clc = XmlConfigurationParser.parseCacheLoaderConfig(strToElement(conf)); mgr = new CacheLoaderManager(); try { mgr.setConfig(clc, null, null); fail("A cache loader cannot be configured as singleton and shared, should have thrown an Exception"); } catch (Exception e) { } /************************************************************************************************************/ conf = "" + "false" + "/, /blah, /blah2" + "" + " org.jboss.cache.loader.FileCacheLoader" + " false" + " false" + " true" + " " + " location=" + getTempDir() + " " + " " + " true" + " org.jboss.cache.loader.DummyInMemoryCacheLoader" + " " + "" + ""; clc = XmlConfigurationParser.parseCacheLoaderConfig(strToElement(conf)); mgr = new CacheLoaderManager(); try { mgr.setConfig(clc, null, null); fail("A singleton store class implementation must extend AbstractDelegatingCacheLoader"); } catch (Exception e) { } } private class MockCacheLoaderManager extends CacheLoaderManager { @Override protected void setCacheInLoader(CacheSPI c, CacheLoader loader) { /* do nothing */ } } @SuppressWarnings("unchecked") public static class MockSingletonStoreCacheLoader extends AbstractDelegatingCacheLoader { public MockSingletonStoreCacheLoader() { super(null); } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/loader/TcpCacheLoaderTest.java0000644000175000017500000002063511017455042030213 0ustar twernertwernerpackage org.jboss.cache.loader; import org.jboss.cache.CacheException; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.UnitTestCacheConfigurationFactory; import org.jboss.cache.interceptors.OrderedSynchronizationHandler; import org.jboss.cache.loader.tcp.TcpCacheServer; import org.jboss.cache.notifications.annotation.CacheListener; import org.jboss.cache.notifications.annotation.NodeCreated; import org.jboss.cache.notifications.event.Event; import org.jboss.cache.transaction.GlobalTransaction; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import javax.transaction.Synchronization; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; /** * Tests the TcpDelegatingCacheLoader * * @author Bela Ban * @version $Id: TcpCacheLoaderTest.java 5906 2008-05-29 07:24:18Z mircea.markus $ */ @Test(groups = "functional") public class TcpCacheLoaderTest extends CacheLoaderTestsBase { protected static final int CACHE_SERVER_RESTART_DELAY_MS = 1000; protected static final int TCP_CACHE_LOADER_TIMEOUT_MS = 2000; protected static int START_COUNT = 0; static TcpCacheServer cacheServer = null; @BeforeClass public static void startCacheServer() { final CountDownLatch startedSignal = new CountDownLatch(1); Thread t = new Thread() { public void run() { try { System.out.println("Starting TcpCacheServer"); cacheServer = new TcpCacheServer(); cacheServer.setBindAddress("127.0.0.1"); cacheServer.setPort(12121); Configuration config = UnitTestCacheConfigurationFactory.createConfiguration(Configuration.CacheMode.LOCAL, true); CacheSPI cache = (CacheSPI) new DefaultCacheFactory().createCache(config); cacheServer.setCache(cache); cacheServer.create(); cacheServer.start(); START_COUNT++; startedSignal.countDown(); } catch (Exception ex) { ex.printStackTrace(); } } }; t.setDaemon(true); t.start(); // Wait for the cache server to start up. boolean started = false; try { started = startedSignal.await(120, TimeUnit.SECONDS); } catch (InterruptedException e) { // do nothing } if (!started) { // the TcpCacheServer was unable to start up for some reason!! throw new RuntimeException("Unable to start the TcpCacheServer after 120 seconds!!"); } } @AfterClass public static void stopCacheServer() { if (cacheServer != null) { System.out.println("Stopping TcpCacheServer"); cacheServer.stop(); } } protected static void restartCacheServer() { stopCacheServer(); startCacheServer(); } @Override public void testPartialLoadAndStore() { // do nothing } @Override public void testBuddyBackupStore() { // do nothing } protected void configureCache() throws Exception { cache.getConfiguration().setCacheLoaderConfig(getSingleCacheLoaderConfig("", TcpDelegatingCacheLoader.class.getName(), "host=127.0.0.1\nport=12121\ntimeout=" + TCP_CACHE_LOADER_TIMEOUT_MS, false, true, false)); } // restart tests public void testCacheServerRestartMidCall() throws Exception { CacheServerRestarter restarter = new CacheServerRestarter(); restarter.restart = true; cache.addCacheListener(restarter); int oldStartCount = START_COUNT; // a restart of the cache server will happen before the cache loader interceptor is called. cache.put(FQN, "key", "value"); assert oldStartCount + 1 == START_COUNT : "Cache server should have restarted!"; assert loader.get(FQN).equals(Collections.singletonMap("key", "value")); } public void testCacheServerDelayedRestartMidCall() throws Exception { CacheServerRestarter restarter = new CacheServerRestarter(); restarter.restart = false; restarter.delayedRestart = true; restarter.startAfter = CACHE_SERVER_RESTART_DELAY_MS; cache.addCacheListener(restarter); int oldStartCount = START_COUNT; // the cache server will STOP before the cache laoder interceptor is called. // it will be restarted in a separate thread, startAfter millis later. // this should be less than the TcpCacheLoader timeout. cache.put(FQN, "key", "value"); assert oldStartCount + 1 == START_COUNT : "Cache server should have restarted!"; assert loader.get(FQN).equals(Collections.singletonMap("key", "value")); } public void testCacheServerTimeoutMidCall() throws Exception { CacheServerRestarter restarter = new CacheServerRestarter(); restarter.restart = false; restarter.delayedRestart = true; restarter.startAfter = -1; cache.addCacheListener(restarter); int oldStartCount = START_COUNT; // the cache server will STOP before the cache laoder interceptor is called. // it will be restarted in a separate thread, startAfter millis later. // this should be less than the TcpCacheLoader timeout. try { cache.put(FQN, "key", "value"); assert false : "Should have failed"; } catch (CacheException expected) { } assert oldStartCount == START_COUNT : "Cache server should NOT have restarted!"; // start the TCP server again startCacheServer(); assert loader.get(FQN) == null; } public void testCacheServerRestartMidTransaction() throws Exception { int oldStartCount = START_COUNT; cache.getTransactionManager().begin(); cache.put(FQN, "key", "value"); restartCacheServer(); cache.put(FQN, "key2", "value2"); cache.getTransactionManager().commit(); Map m = new HashMap(); m.put("key", "value"); m.put("key2", "value2"); assert oldStartCount + 1 == START_COUNT : "Cache server should have restarted!"; assert loader.get(FQN).equals(m); } public void testCacheServerRestartMidTransactionAfterPrepare() throws Exception { int oldStartCount = START_COUNT; cache.getTransactionManager().begin(); cache.put(FQN, "key", "value"); cache.put(FQN, "key2", "value2"); GlobalTransaction gtx = cache.getTransactionTable().get(cache.getTransactionManager().getTransaction()); OrderedSynchronizationHandler osh = cache.getTransactionTable().get(gtx).getOrderedSynchronizationHandler(); // OrderedSynchronizationHandler.getInstance(cache.getTransactionManager().getTransaction()).registerAtTail( osh.registerAtTail( new Synchronization() { public void beforeCompletion() { // this will be called after the cache's prepare() phase. Restart the cache server. restartCacheServer(); } public void afterCompletion(int i) { // do nothing } } ); cache.getTransactionManager().commit(); Map m = new HashMap(); m.put("key", "value"); m.put("key2", "value2"); assert oldStartCount + 1 == START_COUNT : "Cache server should have restarted!"; assert loader.get(FQN).equals(m); } @CacheListener public static class CacheServerRestarter { boolean restart; boolean delayedRestart; int startAfter; @NodeCreated public void restart(Event e) { if (e.isPre()) { if (restart) { restartCacheServer(); } else if (delayedRestart) { stopCacheServer(); new Thread() { public void run() { if (startAfter > 0) { TestingUtil.sleepThread(startAfter); startCacheServer(); } } }.start(); } } } } }jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/loader/CacheLoaderMethodCallCounterTest.java0000644000175000017500000000574011017455042033041 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.loader; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.util.TestingUtil; import static org.testng.AssertJUnit.assertEquals; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * A simple non-failing unit test to measure how many times each method on a cache loader is called. * * @author Manik Surtani (manik@jboss.org) */ @Test(groups = "functional") public class CacheLoaderMethodCallCounterTest extends AbstractCacheLoaderTestBase { private CacheSPI cache; private DummyCountingCacheLoader dummyLoader; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { if (cache != null) tearDown(); cache = (CacheSPI) new DefaultCacheFactory().createCache(false); cache.getConfiguration().setCacheLoaderConfig(getSingleCacheLoaderConfig("", DummyCountingCacheLoader.class.getName(), "", false, false, false)); cache.start(); dummyLoader = (DummyCountingCacheLoader) cache.getCacheLoaderManager().getCacheLoader(); } @AfterMethod(alwaysRun = true) public void tearDown() { TestingUtil.killCaches(cache); } public void testPut() { cache.put("/node", "key", "value"); printReport("putKeyValue", dummyLoader); } public void testGet() { cache.get("/node", "key"); printReport("getKey", dummyLoader); } public void testRemove() { cache.remove("/node", "key"); printReport("removeKey", dummyLoader); } private void printReport(String test, DummyCountingCacheLoader d) { System.out.println("------------------------------"); System.out.println(" Test name: " + test); System.out.println(" cache loader stats:"); System.out.println(" put count: " + d.getPutCount()); System.out.println(" get count: " + d.getGetCount()); System.out.println(" exists count: " + d.getExistsCount()); System.out.println(" remove count: " + d.getRemoveCount()); System.out.println(" getChildrenNames count: " + d.getGetChildrenNamesCount()); System.out.println("------------------------------"); } public void testLoopedGets() { // put an object in cache cache.put("/test", "key", "value"); // we should see this put in the cl assertEquals(1, dummyLoader.getPutCount()); // the cloader interceptor does a get as well when doing the put ... ? assertEquals(1, dummyLoader.getGetCount()); for (int i = 0; i < 2000; i++) { cache.getNode("/test"); } assertEquals(1, dummyLoader.getPutCount()); assertEquals(1, dummyLoader.getGetCount()); assertEquals(0, dummyLoader.getRemoveCount()); assertEquals(0, dummyLoader.getExistsCount()); } }jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/loader/PreloadTest.java0000644000175000017500000000556411021006017026771 0ustar twernertwernerpackage org.jboss.cache.loader; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.factories.UnitTestCacheConfigurationFactory; import org.jboss.cache.util.TestingUtil; import org.testng.annotations.AfterMethod; import org.testng.annotations.Test; @Test(groups = "functional") public class PreloadTest extends AbstractCacheLoaderTestBase { CacheSPI cache; Fqn fqn = Fqn.fromString("/a/b/c"); Object key = "key", value = "value"; @AfterMethod public void tearDown() { if (cache != null) TestingUtil.killCaches(cache); } public void testPreload() throws Exception { Configuration c = UnitTestCacheConfigurationFactory.createConfiguration(CacheMode.LOCAL); c.setCacheLoaderConfig(getSingleCacheLoaderConfig("/a", DummySharedInMemoryCacheLoader.class.getName(), "", false, false, false)); cache = (CacheSPI) new DefaultCacheFactory().createCache(c.clone()); cache.put(fqn, key, value); assertExists(); cache.destroy(); cache = (CacheSPI) new DefaultCacheFactory().createCache(c.clone()); assertExists(); } public void testPreloadMultiRegions() throws Exception { Configuration c = UnitTestCacheConfigurationFactory.createConfiguration(CacheMode.LOCAL); c.setCacheLoaderConfig(getSingleCacheLoaderConfig("/a", DummySharedInMemoryCacheLoader.class.getName(), "", false, false, false)); cache = (CacheSPI) new DefaultCacheFactory().createCache(c.clone()); cache.put(fqn, key, value); assertExists(); cache.destroy(); c.setCacheLoaderConfig(getSingleCacheLoaderConfig("/c,/a,/b", DummySharedInMemoryCacheLoader.class.getName(), "", false, false, false)); cache = (CacheSPI) new DefaultCacheFactory().createCache(c.clone()); assertExists(); c.setCacheLoaderConfig(getSingleCacheLoaderConfig("/c, /a, /b", DummySharedInMemoryCacheLoader.class.getName(), "", false, false, false)); cache = (CacheSPI) new DefaultCacheFactory().createCache(c.clone()); assertExists(); c.setCacheLoaderConfig(getSingleCacheLoaderConfig(" /c, /a, /b", DummySharedInMemoryCacheLoader.class.getName(), "", false, false, false)); cache = (CacheSPI) new DefaultCacheFactory().createCache(c.clone()); assertExists(); } private void assertExists() throws Exception { CacheLoader loader = cache.getCacheLoaderManager().getCacheLoader(); assert loader.get(fqn).get(key).equals(value); assert cache.peek(fqn, false).getDataDirect().get(key).equals(value); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/loader/UnnecessaryLoadingTest.java0000644000175000017500000003061211074652365031215 0ustar twernertwernerpackage org.jboss.cache.loader; import static org.easymock.EasyMock.*; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.Node; import org.jboss.cache.NodeSPI; import org.jboss.cache.config.CacheLoaderConfig; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Set; /** * @author Manik Surtani * @since 2.0.0 */ @Test(groups = {"functional"}) public class UnnecessaryLoadingTest { private CacheSPI cache; private Fqn parent = Fqn.fromString("/parent"); private Fqn child = Fqn.fromString("/parent/child"); private String k = "k", v = "v"; private CacheLoader mockCacheLoader; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { cache = (CacheSPI) new DefaultCacheFactory().createCache(false); CacheLoaderConfig clc = new CacheLoaderConfig(); CacheLoaderConfig.IndividualCacheLoaderConfig iclc = new CacheLoaderConfig.IndividualCacheLoaderConfig(); clc.addIndividualCacheLoaderConfig(iclc); cache.getConfiguration().setCacheLoaderConfig(clc); mockCacheLoader = createMock(CacheLoader.class); expect(mockCacheLoader.getConfig()).andReturn(null).anyTimes(); mockCacheLoader.setCache((CacheSPI) anyObject()); expectLastCall().anyTimes(); mockCacheLoader.setConfig((CacheLoaderConfig.IndividualCacheLoaderConfig) anyObject()); expectLastCall().anyTimes(); mockCacheLoader.create(); expectLastCall().anyTimes(); mockCacheLoader.start(); expectLastCall().anyTimes(); mockCacheLoader.stop(); expectLastCall().anyTimes(); mockCacheLoader.destroy(); expectLastCall().anyTimes(); replay(mockCacheLoader); iclc.setCacheLoader(mockCacheLoader); cache.start(); reset(mockCacheLoader); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { reset(mockCacheLoader); expect(mockCacheLoader.getConfig()).andReturn(null).anyTimes(); mockCacheLoader.setCache((CacheSPI) anyObject()); expectLastCall().anyTimes(); mockCacheLoader.setConfig((CacheLoaderConfig.IndividualCacheLoaderConfig) anyObject()); expectLastCall().anyTimes(); mockCacheLoader.create(); expectLastCall().anyTimes(); mockCacheLoader.start(); expectLastCall().anyTimes(); mockCacheLoader.stop(); expectLastCall().anyTimes(); mockCacheLoader.destroy(); expectLastCall().anyTimes(); replay(mockCacheLoader); cache.stop(); } protected void assertDataLoaded(Fqn f) { assertTrue("Data should be loaded for node " + f, cache.peek(f, false).isDataLoaded()); } protected void assertDataNotLoaded(Fqn f) { NodeSPI n = cache.peek(f, true); assertFalse("Data should not be loaded for node " + f, n != null && n.isDataLoaded()); } public void testNoLoading() throws Exception { // we expect these nodes to be stored. expect(mockCacheLoader.put(eq(parent), eq(k), eq(v))).andReturn(null); expect(mockCacheLoader.put(eq(child), eq(k), eq(v))).andReturn(null); // create parent and child with data // new nodes being created, will result in loading them from the cache loader first expect(mockCacheLoader.get(eq(parent))).andReturn(null); expect(mockCacheLoader.get(eq(child))).andReturn(null); replay(mockCacheLoader); cache.put(parent, k, v); cache.put(child, k, v); // should be NO cache loading involved whatsoever // so no exceptions should be thrown by the mock CL verify(mockCacheLoader); } public void testLoadChild() throws Exception { // we expect these nodes to be stored. expect(mockCacheLoader.put(eq(parent), eq(k), eq(v))).andReturn(null); expect(mockCacheLoader.put(eq(child), eq(k), eq(v))).andReturn(null); // create parent and child with data // new nodes being created, will result in loading them from the cache loader first expect(mockCacheLoader.get(eq(parent))).andReturn(null); expect(mockCacheLoader.get(eq(child))).andReturn(null).times(2); replay(mockCacheLoader); cache.put(parent, k, v); cache.put(child, k, v); cache.evict(child, false); // there is no REAL cache loader so this return value should not be tested cache.get(child, k); // should be NO cache loading involved whatsoever // so no exceptions should be thrown by the mock CL verify(mockCacheLoader); } public void testDontLoadChild() throws Exception { // we expect these nodes to be stored. expect(mockCacheLoader.put(eq(parent), eq(k), eq(v))).andReturn(null); expect(mockCacheLoader.put(eq(child), eq(k), eq(v))).andReturn(null); // create parent and child with data // new nodes being created, will result in loading them from the cache loader first expect(mockCacheLoader.get(eq(parent))).andReturn(null); expect(mockCacheLoader.get(eq(child))).andReturn(null); replay(mockCacheLoader); cache.put(parent, k, v); cache.put(child, k, v); // nodes should be marked as having data loaded. assertDataLoaded(parent); assertDataLoaded(child); // now evict the parent cache.evict(parent, false); // only the evicted parent should have isDataLoaded() set to false. Not the child. assertDataNotLoaded(parent); assertDataLoaded(child); assertNotNull(cache.peek(child, false)); // there is no REAL cache loader so this return value should not be tested cache.get(child, k); // should be NO cache loading involved whatsoever verify(mockCacheLoader); } @SuppressWarnings("unchecked") public void testUnnecessaryMultipleLoading() throws Exception { Map m = new HashMap(); m.put("foo", "bar"); // expecting a put on child mockCacheLoader.put(eq(child), eq(m)); expect(mockCacheLoader.get(eq(child))).andReturn(null); expect((Set) mockCacheLoader.getChildrenNames(eq(parent))).andReturn(Collections.singleton(child.getLastElementAsString())); replay(mockCacheLoader); cache.put(child, m); // FIXME Changes for JBCACHE-1423 require changing a number of asserts // below from "assertDataNotLoaded" to "assertDataLoaded". This test // should be revisited to confirm it still tests what it was intended to assertDataLoaded(child); // should load child data // mockCacheLoader.expects(once()).method("get").with(eq(child)); cache.get(child, "foo"); assertDataLoaded(child); cache.get(child, "foo"); assertDataLoaded(child); cache.get(child, "foo2"); // does not exist, will trigger a load assertDataLoaded(child); // should not load Node node = cache.getRoot().getChild(parent); assertDataLoaded(child); assertDataNotLoaded(parent); // needs to load children at this stage in case there are other children that have been evicted. Set children = node.getChildren(); //getchildrennames /parent assertEquals(1, children.size()); assertDataLoaded(child); cache.get(child, "foo"); //get /parent/child assertDataLoaded(child); verify(mockCacheLoader); } public void testDontLoadDataWhenGettingNode() throws Exception { expect(mockCacheLoader.put(eq(parent), eq(k), eq(v))).andReturn(null); expect(mockCacheLoader.get(eq(parent))).andReturn(null); expect(mockCacheLoader.exists(eq(parent))).andReturn(true); replay(mockCacheLoader); cache.put(parent, k, v); assertDataLoaded(parent); // evict the parent cache.evict(parent, false); assertNull(cache.peek(parent, false)); // now get node. Node n = cache.getRoot().getChild(parent); assertNotNull(n); assertDataNotLoaded(parent); verify(mockCacheLoader); } public void testDontLoadDataWhenClearingNode() throws Exception { expect(mockCacheLoader.put(eq(parent), eq(k), eq(v))).andReturn(null); expect(mockCacheLoader.get(eq(parent))).andReturn(null); expect(mockCacheLoader.exists(eq(parent))).andReturn(true); mockCacheLoader.removeData(eq(parent)); replay(mockCacheLoader); cache.put(parent, k, v); assertDataLoaded(parent); // evict the parent cache.evict(parent, false); assertNull(cache.peek(parent, false)); // now get node. Node n = cache.getRoot().getChild(parent); assertNotNull(n); assertDataNotLoaded(parent); // should not load node but should change isDataLoaded to true // will trigger a removedata() though n.clearData(); assertDataLoaded(parent); verify(mockCacheLoader); } public void testDontLoadDataWhenReplacingNode() throws Exception { expect(mockCacheLoader.put(eq(parent), eq(k), eq(v))).andReturn(null); expect(mockCacheLoader.get(eq(parent))).andReturn(null); expect(mockCacheLoader.exists(eq(parent))).andReturn(true); mockCacheLoader.removeData(eq(parent)); mockCacheLoader.put(eq(parent), eq(Collections.singletonMap((Object) "hello", (Object) "world"))); replay(mockCacheLoader); cache.put(parent, k, v); assertTrue(cache.peek(parent, false).isDataLoaded()); // evict the parent cache.evict(parent, false); assertNull(cache.peek(parent, false)); // now get node. Node n = cache.getRoot().getChild(parent); assertNotNull(n); assertDataNotLoaded(parent); // should not load node but should change isDataLoaded to true // will trigger a put() though // for the moment this does a get as well - which while unnecessary is the best we can do for now until we bring in // an AOP framework to work on nodes directly. n.replaceAll(Collections.singletonMap((Object) "hello", (Object) "world")); assertDataLoaded(parent); verify(mockCacheLoader); } public void testLazyLoadDataWhenWorkingWithNode() throws Exception { expect(mockCacheLoader.put(eq(parent), eq(k), eq(v))).andReturn(null); expect(mockCacheLoader.get(eq(parent))).andReturn(null); expect(mockCacheLoader.exists(eq(parent))).andReturn(true); expect(mockCacheLoader.get(eq(parent))).andReturn(Collections.singletonMap((Object) k, (Object) v)); replay(mockCacheLoader); cache.put(parent, k, v); assertDataLoaded(parent); // evict the parent cache.evict(parent, false); assertNull(cache.peek(parent, false)); // now get node. Node n = cache.getRoot().getChild(parent); assertNotNull(n); assertDataNotLoaded(parent); // will trigger a load assertEquals(v, n.get(k)); // should change isDataLoaded to true assertDataLoaded(parent); verify(mockCacheLoader); } public void testDontLoadWhenKeyInMemory() throws Exception { Map m = new HashMap(); m.put("k2", "v2"); expect(mockCacheLoader.get(eq(parent))).andReturn(null); expect(mockCacheLoader.put(eq(parent), eq(k), eq(v))).andReturn(null); mockCacheLoader.put(eq(parent), eq(m)); expect(mockCacheLoader.get(eq(parent))).andReturn(Collections.singletonMap((Object) k, (Object) v)); replay(mockCacheLoader); cache.put(parent, k, v); assertDataLoaded(parent); // now evict cache.evict(parent, false); assertDataNotLoaded(parent); // should not load cache.put(parent, m); // FIXME Changes for JBCACHE-1423 require changing a number of asserts // below from "assertDataNotLoaded" to "assertDataLoaded". This test // should be revisited to confirm it still tests what it was intended to assertDataLoaded(parent); // now a get for an existing key should not trigger a load! assertEquals("v2", cache.get(parent, "k2")); assertDataLoaded(parent); // but going a get for a nonexistent key should! assertEquals(v, cache.get(parent, k)); // should not have overwritten in-memory data assertEquals("v2", cache.get(parent, "k2")); verify(mockCacheLoader); } }jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/loader/JDBCCacheLoaderConnectionTest.java0000644000175000017500000000526110777521744032224 0ustar twernertwernerpackage org.jboss.cache.loader; import org.jboss.cache.Cache; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.sql.Connection; /** * To test the closing of JDBC connections */ @Test(groups = "functional") public class JDBCCacheLoaderConnectionTest extends AbstractCacheLoaderTestBase { private Cache cache; @BeforeMethod public void setUp() throws Exception { String props = " cache.jdbc.table.name=jbosscache_engine_cache\n" + " cache.jdbc.table.create=true\n" + " cache.jdbc.table.drop=true\n" + " cache.jdbc.table.primarykey=jbosscache_engine_cache_pk\n" + " cache.jdbc.fqn.column=fqn\n" + " cache.jdbc.fqn.type=varchar(255)\n" + " cache.jdbc.node.column=node\n" + " cache.jdbc.node.type=blob\n" + " cache.jdbc.parent.column=parent\n" + " cache.jdbc.sql-concat=1 || 2\n" + " cache.jdbc.driver=org.apache.derby.jdbc.EmbeddedDriver\n" + " cache.jdbc.url=jdbc:derby:jbossdb;create=true\n" + " cache.jdbc.user=user1\n" + " cache.jdbc.password=user1"; cache = new DefaultCacheFactory().createCache(false); cache.getConfiguration().setCacheLoaderConfig(getSingleCacheLoaderConfig("", JDBCCacheLoader.class.getName(), props, false, false, true, false)); cache.start(); } @AfterMethod public void tearDown() { cache.stop(); } public void testConnectionRelease() throws Exception { cache.removeNode(Fqn.fromString("C")); for (int i = 0; i < 100; i++) { cache.put(Fqn.fromElements("C", Integer.toString(i)), "Blah", Integer.toString(i)); System.out.println("added " + i + " dummy node to Jboss cache."); } assertConnectionsClosed(); } private void assertConnectionsClosed() throws Exception { JDBCCacheLoader loader = (JDBCCacheLoader) ((CacheSPI) cache).getCacheLoaderManager().getCacheLoader(); NonManagedConnectionFactory cf = (NonManagedConnectionFactory) loader.cf; Connection conn = cf.connection.get(); if (conn != null) { // make sure it is closed/released! assert conn.isClosed(); } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/loader/JDBCCacheLoaderTest.java0000644000175000017500000000701311031017265030157 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.loader; import org.jboss.cache.Fqn; import static org.testng.AssertJUnit.*; import org.testng.annotations.Test; import java.util.Properties; /** * This test runs cache loader tests using Database as the cache loader store. * The default test is configured using Derby embedded framework. * The server and database configuration is read from a properties file located at * /etc/cache-jdbc.properties. *

* To run this test with any other RDBMS, The appropriate JDBC driver * (i.e mysql-connector-java-3.0.10-stable-bin.jar) * must be in the lib directory. * * @author Hany Mesha * @author Galder Zamarreno * @version $Revision: 6072 $ */ @Test(groups = {"functional"}) public class JDBCCacheLoaderTest extends CacheLoaderTestsBase { protected void configureCache() throws Exception { Properties prop = getProperties(); String props = "cache.jdbc.driver =" + prop.getProperty("cache.jdbc.driver") + "\n" + "cache.jdbc.url=" + prop.getProperty("cache.jdbc.url") + "\n" + "cache.jdbc.user=" + prop.getProperty("cache.jdbc.user") + "\n" + "cache.jdbc.password=" + prop.getProperty("cache.jdbc.password") + "\n" + "cache.jdbc.node.type=" + prop.getProperty("cache.jdbc.node.type") + "\n" + "cache.jdbc.sql-concat=" + prop.getProperty("cache.jdbc.sql-concat") + "\n" + "cache.jdbc.table.name=" + prop.getProperty("cache.jdbc.table.name") + "\n" + "cache.jdbc.table.drop=false"; // make sure dropTable is false!! cache.getConfiguration().setCacheLoaderConfig(getSingleCacheLoaderConfig("", "org.jboss.cache.loader.JDBCCacheLoader", props, false, true, false)); } public void testLargeObject() { try { String key = "LargeObj"; // create an object with size bigger than 4k (k=1024 bytes) StringBuilder text = new StringBuilder("LargeObject"); while (text.toString().getBytes().length < (1024 * 100)) { text.append(text); } String initialValue = text.toString(); // insert it into the cache loader loader.remove(Fqn.fromString("/")); Object retVal = loader.put(FQN, key, initialValue); assertNull(retVal); addDelay(); // load the object from the cache loader and validate it assertEquals(initialValue, (String) loader.get(FQN).get(key)); // update the object and validate it String updatedValue = initialValue.concat(("UpdatedValue")); retVal = loader.put(FQN, key, updatedValue); assertEquals(initialValue, (String) retVal); assertEquals(updatedValue, (String) loader.get(FQN).get(key)); } catch (Exception e) { fail(e.toString()); } } protected Properties getProperties() throws Exception { Properties properties = new Properties(); try { properties.load(this.getClass().getClassLoader().getResourceAsStream("cache-jdbc.properties")); return properties; } catch (Exception e) { throw new Exception("Error loading jdbc properties ", e); } } public void testRootIsCreated() throws Exception { loader.put(Fqn.fromString("/a/b/c"), "a", "b"); assertTrue(loader.exists(Fqn.ROOT)); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/loader/ConcurrentPutRemoveEvictTest.java0000644000175000017500000000624311021102270032360 0ustar twernertwernerpackage org.jboss.cache.loader; import org.jboss.cache.Cache; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.config.Configuration; import org.jboss.cache.util.TestingUtil; import org.testng.annotations.AfterTest; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; /** * To test JBCACHE-1355 * * @author Manik Surtani (manik@jboss.org) * @since 2.2.0 */ @Test(groups = "functional", enabled = false) // TODO: 2.2.0: Figure out why this occasionally hangs!! public class ConcurrentPutRemoveEvictTest extends AbstractCacheLoaderTestBase { Cache cache; Fqn fqn = Fqn.fromString("/a"); String key = "key"; boolean run = true; Set exceptions = new HashSet(); @BeforeTest public void setUp() throws Exception { CacheLoaderConfig cacheLoaderConfig = getSingleCacheLoaderConfig("", DummyInMemoryCacheLoader.class.getName(), "", false, false, false); Configuration cfg = new Configuration(); cfg.setCacheLoaderConfig(cacheLoaderConfig); cache = new DefaultCacheFactory().createCache(cfg); cache.put(fqn, key, "value"); } @AfterTest public void tearDown() { TestingUtil.killCaches(cache); } public void doTest() throws Exception { List threads = new ArrayList(); threads.add(new Getter()); threads.add(new RandomAdder()); threads.add(new Evicter()); for (Thread t : threads) t.start(); // let these run for a while. TestingUtil.sleepThread(10000); run = false; for (Thread t : threads) t.join(); if (!exceptions.isEmpty()) { for (Exception e : exceptions) throw e; } } private class RandomAdder extends Thread { public void run() { int i = 0; while (run) { try { cache.put(fqn, key + (i++), ""); } catch (Exception e) { // ignore } } } } private class Getter extends Thread { public void run() { while (run) { try { // note that we sometimes get a null back. This is incorrect and inconsistent, but has to do with locks being held // on nodes. Very similar to http://jira.jboss.org/jira/browse/JBCACHE-1165 String value = cache.get(fqn, key); System.out.println("Thread " + getName() + " got value " + value); } catch (Exception e) { exceptions.add(e); } } } } private class Evicter extends Thread { public void run() { while (run) { try { cache.evict(fqn); } catch (Exception e) { // who cares } } } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/loader/deadlock/0000755000175000017500000000000011376173763025466 5ustar twernertwerner././@LongLink0000000000000000000000000000015300000000000011564 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/loader/deadlock/ConcurrentCreationDeadlockTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/loader/deadlock/ConcurrentCreationDeadlockTes0000644000175000017500000003041710737311311033307 0ustar twernertwernerpackage org.jboss.cache.loader.deadlock; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.factories.UnitTestCacheConfigurationFactory; import org.jboss.cache.loader.AbstractCacheLoaderTestBase; import org.jboss.cache.loader.DummyInMemoryCacheLoader; import org.jboss.cache.transaction.DummyTransactionManager; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.naming.Context; import javax.naming.InitialContext; import javax.transaction.UserTransaction; import java.util.Properties; import java.util.concurrent.CountDownLatch; /** * Test based on a contribution by Marian Nokolov/Paul Miodonski at Siemens AG. *

* This test has been created to simulate a unexpected TimeoutException that is * thrown by the JBossCache. The original scenario that has been observed:
* Cache in either LOCAL or REPL_SYNC mode with CacheLoader. *

    *
  • 1. Concurrent threads A, B and C, each associated with a transaction. * Threads A and B try to modify FQN X, thread C tries to modify FQN Y.
  • *
  • 2. Thread A locks X.
  • *
  • 3. Thread B blocks on X (correct since A is the active writer).
  • *
  • 4. Thread A tries to do multiple modifications and suddenly blocks on X * (although it is the active writer already) - this is the 1-st problem.
  • *
  • 5. Thread C blocks somewhere as well, although it has nothing to do with * X and is the only one that works on Y - this is a 2-nd problem.
  • *
  • 6. Thread B fails with TimeoutException and its transaction is rolled * back - this is correct given that A is still holding the lock on X.
  • *
  • 7. Thread A continues its job and its transaction completes successfully * (after unexpected locking timeout delay).
  • *
  • 8. Thread C continues its job and successfully commits the transaction * (with unexpected locking timeout delay).
  • *
*
* There are two problems with this: *
    *
  • 1. One or more concurrent transactions fail, although the pessimistic * locking should sequentialize them and guarantee that all should succeed.
  • *
  • 2. Any other thread that tries to acquire a lock in the cache (even for * a different FQN!?) is blocked for the duration of the locking timeout, i.e. * the whole application is locked for few seconds. The active writer for the * corresponding FQN is blocked as well in the middle of the transaction!
  • *
*
* At least until now, the error can be reproduced only if the following is * true: *
    *
  • Concurrent transactions forcing creation of the same FQN at the same * time.
  • *
  • More than one update per TX per FQN - trying to acquire lock on the same * FQN multiple times per TX.
  • *
  • Cache with CacheLoader - maybe it has something to do with the * CacheLoader/StoreInterceptor's...
  • *
*/ @Test(groups = {"functional"}, enabled = true) // Disabling since this has issues with ReadWriteWithUpgradeLock. See JBCACHE-461 public class ConcurrentCreationDeadlockTest extends AbstractCacheLoaderTestBase { /** * The number of worker threads to start concurrently. */ private static final int NUM_WORKERS = 10; /** * The number of test runs to perform. */ private static final int NUM_RUNS = 100; /** * The number of FQN's per test run. */ private static final int NUM_FQNS_PER_RUN = 10; /** * The initial context factory properties. */ private static final Properties PROPERTIES; /** * The context factory to be used for the test. */ private static final String CONTEXT_FACTORY = "org.jboss.cache.transaction.DummyContextFactory"; /** * The original context factory to be restored after the test. */ private String m_contextFactory = null; /** * Exception recorded if any of the worker threads fails. */ private static volatile Exception mcl_exception = null; /** * The cache under test. */ private CacheSPI cache = null; static { PROPERTIES = new Properties(); PROPERTIES.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.cache.transaction.DummyContextFactory"); } /** * {@inheritDoc} */ @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { mcl_exception = null; m_contextFactory = System.getProperty(Context.INITIAL_CONTEXT_FACTORY); System.setProperty(Context.INITIAL_CONTEXT_FACTORY, CONTEXT_FACTORY); Configuration c = UnitTestCacheConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC); c.setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); cache = (CacheSPI) new DefaultCacheFactory().createCache(c, false); } /** * {@inheritDoc} */ @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { DummyTransactionManager.destroy(); cache.stop(); cache = null; if (m_contextFactory != null) { System.setProperty(Context.INITIAL_CONTEXT_FACTORY, m_contextFactory); m_contextFactory = null; } } /** * Initializes and starts the cache. * * @param cacheMode The cache mode. * @param cacheLoaderClass The name of the cache loader class. * @throws Exception Any exception if thrown by the cache. */ private void startCache(Configuration.CacheMode cacheMode, String cacheLoaderClass) throws Exception { cache.getConfiguration().setCacheMode(cacheMode); if (cacheLoaderClass != null) { cache.getConfiguration().setCacheLoaderConfig(getSingleCacheLoaderConfig("", cacheLoaderClass, "", false, false, false)); } cache.getConfiguration().setLockAcquisitionTimeout(600000); cache.start(); } /** * Test for a local cache with cache loader and two modifications per * transaction.
* This test does very often fail with a TimeoutException. * * @throws Exception Any exception if thrown by the cache. */ public void testLocalCacheLoader2Modifications() throws Exception { startCache(Configuration.CacheMode.LOCAL, DummyInMemoryCacheLoader.class.getName()); performTest(2); } /** * Test for a synchronously replicated cache with cache loader and two * modifications per transaction.
* This test fails very often with a TimeoutException. * * @throws Exception Any exception if thrown by the cache. */ public void testReplSyncCacheLoader2Modifications() throws Exception { startCache(Configuration.CacheMode.REPL_SYNC, DummyInMemoryCacheLoader.class.getName()); performTest(2); } /** * Perform a single test, using the pre-configured cache. * * @param modificationsPerTx The number of modifications per transaction. * @throws Exception Any exception if thrown by the cache. */ private void performTest(int modificationsPerTx) throws Exception { for (int i = 0; i < NUM_RUNS; i++) { if (mcl_exception != null) { // terminate the test on the first failed worker throw mcl_exception; } // start several worker threads to work with the same set of FQN's Worker[] t = new Worker[NUM_WORKERS]; CountDownLatch latch = new CountDownLatch(1); for (int j = 0; j < t.length; j++) { t[j] = new Worker(latch, NUM_FQNS_PER_RUN * i, NUM_FQNS_PER_RUN, modificationsPerTx); t[j].start(); } // fire the workers to start processing latch.countDown(); // wait for all workers to complete for (Worker worker : t) { worker.join(); } } } /** * Returns a user transaction to be associated with the calling thread. * * @return A user transaction. * @throws Exception Any exception thrown by the context lookup. */ private UserTransaction getTransaction() throws Exception { return (UserTransaction) new InitialContext(PROPERTIES) .lookup("UserTransaction"); } /** * Log a message. * * @param msg The meessage to be logged. */ private void log(String msg) { System.out.println(System.currentTimeMillis() + " " + Thread.currentThread() + " " + msg); } /** * A worker thread that applies the concurrent modifications. * * @author Marian Nikolov * @author $Author: manik.surtani@jboss.com $ * @version $Date: 2008-01-04 02:58:33 +0100 (Fr, 04. Jan 2008) $ */ private class Worker extends Thread { /** * Used to fire all workers at the same time. */ private final CountDownLatch m_latch; /** * The start id, used as part of the node FQN. */ private final int m_start; /** * The number of nodes to create in a single run. */ private final int m_count; /** * The number of modifications per single transaction. */ private final int m_modificationsPerTx; /** * The state of the thread, used for logging. */ private int m_state; /** * Constructor. * * @param latch Used to synchronize the startup of all worker threads. * @param start The start id. * @param count The number of nodes to create in a single run. * @param modificationsPerTx The number of modifications per * transaction. */ public Worker(CountDownLatch latch, int start, int count, int modificationsPerTx) { m_latch = latch; m_start = start; m_count = count; m_state = -1; m_modificationsPerTx = modificationsPerTx; } /** * {@inheritDoc} */ public void run() { try { // the latch shall fire all workers at the same time m_latch.await(); for (int i = m_start; i < m_start + m_count; i++) { m_state = -1; log("enter " + i); if (checkAndSetState()) { return; } long time = System.currentTimeMillis(); UserTransaction tx = getTransaction(); tx.begin(); if (checkAndSetState()) { try { tx.rollback(); } catch (Exception e) { } return; } // the first worker would create a new node for the FQN // all the others would update the same node Fqn fqn = Fqn.fromString("/NODE/" + i); for (int m = 0; m < m_modificationsPerTx; m++) { cache.put(fqn, m, i); if (checkAndSetState()) { try { tx.rollback(); } catch (Exception e) { } return; } } tx.commit(); if (checkAndSetState()) { return; } time = System.currentTimeMillis() - time; log("leave " + i + " took " + time + " msec"); } } catch (Exception e) { log("caught exception in state " + m_state + ": " + e); e.printStackTrace(); mcl_exception = e; } } /** * Checks the current thread and sets it state. * * @return True if the worker has to terminate, false otherwise. */ private boolean checkAndSetState() { if (mcl_exception != null) { // another worker failed, terminate log("detected failure in state " + m_state); return true; } m_state++; return false; } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/loader/DummyCountingCacheLoader.java0000644000175000017500000001273110777013310031425 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.loader; import org.jboss.cache.Fqn; import org.jboss.cache.Modification; import org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.List; import java.util.Map; import java.util.Set; /** * Dummy cache loader that captures the number of times each method is called. Stores statistics statically, mimicking * a shared cache loader. * * @author Manik Surtani (manik@jboss.org) */ public class DummyCountingCacheLoader extends AbstractCacheLoader { private static int getChildrenNamesCount = 0, getCount = 0, putCount = 0, existsCount = 0, removeCount = 0; public int getGetChildrenNamesCount() { return getChildrenNamesCount; } public int getGetCount() { return getCount; } public int getPutCount() { return putCount; } public int getExistsCount() { return existsCount; } public int getRemoveCount() { return removeCount; } /** * Sets the configuration. Will be called before {@link #create()} and {@link #start()} */ public void setConfig(IndividualCacheLoaderConfig config) { } public IndividualCacheLoaderConfig getConfig() { return null; } /** * Returns a list of children names, all names are relative. Returns null if the parent node is not found. * The returned set must not be modified, e.g. use Collections.unmodifiableSet(s) to return the result * * @param fqn The FQN of the parent * @return Set. A list of children. Returns null if no children nodes are present, or the parent is * not present */ public Set getChildrenNames(Fqn fqn) throws Exception { getChildrenNamesCount++; return null; } /** * Returns the value for a given key. Returns null if the node doesn't exist, or the value is not bound * * @param name * @return * @throws Exception */ public Object get(Fqn name, Object key) throws Exception { getCount++; return null; } /** * Returns all keys and values from the persistent store, given a fully qualified name * * @param name * @return Map of keys and values for the given node. Returns null if the node was not found, or * if the node has no attributes * @throws Exception */ public Map get(Fqn name) throws Exception { getCount++; return null; } /** * Checks whether the CacheLoader has a node with Fqn * * @param name * @return True if node exists, false otherwise */ public boolean exists(Fqn name) throws Exception { existsCount++; return false; } /** * Inserts key and value into the attributes hashmap of the given node. If the node does not exist, all * parent nodes from the root down are created automatically. Returns the old value */ public Object put(Fqn name, Object key, Object value) throws Exception { putCount++; return null; } /** * Inserts all elements of attributes into the attributes hashmap of the given node, overwriting existing * attributes, but not clearing the existing hashmap before insertion (making it a union of existing and * new attributes) * If the node does not exist, all parent nodes from the root down are created automatically * * @param name The fully qualified name of the node * @param attributes A Map of attributes. Can be null */ public void put(Fqn name, Map attributes) throws Exception { putCount++; } /** * Inserts all modifications to the backend store. Overwrite whatever is already in * the datastore. * * @param modifications A List of modifications * @throws Exception */ public void put(List modifications) throws Exception { putCount++; } /** * Removes the given key and value from the attributes of the given node. No-op if node doesn't exist */ public Object remove(Fqn name, Object key) throws Exception { removeCount++; return null; } /** * Removes the given node. If the node is the root of a subtree, this will recursively remove all subnodes, * depth-first */ public void remove(Fqn name) throws Exception { removeCount++; } /** * Removes all attributes from a given node, but doesn't delete the node itself * * @param name * @throws Exception */ public void removeData(Fqn name) throws Exception { removeCount++; } @Override public void loadEntireState(ObjectOutputStream os) throws Exception { //intentional no-op } @Override public void loadState(Fqn subtree, ObjectOutputStream os) throws Exception { // intentional no-op } @Override public void storeEntireState(ObjectInputStream is) throws Exception { // intentional no-op } @Override public void storeState(Fqn subtree, ObjectInputStream is) throws Exception { // intentional no-op } @Override public void destroy() { getChildrenNamesCount = 0; getCount = 0; putCount = 0; existsCount = 0; removeCount = 0; } public void scrubStats() { destroy(); } }jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/loader/SharedCacheLoaderTest.java0000644000175000017500000000567111005354146030676 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.loader; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.interceptors.CacheStoreInterceptor; import org.jboss.cache.interceptors.base.CommandInterceptor; import static org.testng.AssertJUnit.assertEquals; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.Iterator; /** * See http://www.jboss.com/index.html?module=bb&op=viewtopic&p=3919374#3919374 * * @author Manik Surtani (manik@jboss.org) */ @Test(groups = "functional") public class SharedCacheLoaderTest extends AbstractCacheLoaderTestBase { private CacheSPI cache1, cache2; private DummyCountingCacheLoader dummyCacheLoader; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { if (cache1 != null || cache2 != null) tearDown(); // set up 2 instances of CacheImpl with shared CacheLoaders. cache1 = (CacheSPI) new DefaultCacheFactory().createCache(false); cache2 = (CacheSPI) new DefaultCacheFactory().createCache(false); cache1.getConfiguration().setCacheMode("REPL_SYNC"); cache2.getConfiguration().setCacheMode("REPL_SYNC"); cache1.getConfiguration().setCacheLoaderConfig(getSingleCacheLoaderConfig("", DummyCountingCacheLoader.class.getName(), "", false, false, true)); cache2.getConfiguration().setCacheLoaderConfig(getSingleCacheLoaderConfig("", DummyCountingCacheLoader.class.getName(), "", false, false, true)); cache1.start(); cache2.start(); dummyCacheLoader = new DummyCountingCacheLoader(); // statistics are stored statically so this is safe. dummyCacheLoader.scrubStats(); } protected CacheStoreInterceptor findCacheStoreInterceptor(CacheSPI cache) { Iterator ints = cache.getInterceptorChain().iterator(); CacheStoreInterceptor csi = null; while (ints.hasNext()) { CommandInterceptor i = (CommandInterceptor) ints.next(); if (i instanceof CacheStoreInterceptor) { csi = (CacheStoreInterceptor) i; break; } } return csi; } @AfterMethod(alwaysRun = true) protected void tearDown() { if (cache1 != null) cache1.stop(); if (cache2 != null) cache2.stop(); cache1 = null; cache2 = null; } public void testReplicationWithSharedCL() { cache1.put("/test", "one", "two"); // should have replicated assertEquals("two", cache1.get("/test", "one")); assertEquals("two", cache2.get("/test", "one")); // only a single put() should have happened on the cache loader though. assertEquals(1, dummyCacheLoader.getPutCount()); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/loader/BdbjeTest.java0000644000175000017500000007423210777645574026451 0ustar twernertwernerpackage org.jboss.cache.loader; import com.sleepycat.je.Database; import com.sleepycat.je.DatabaseConfig; import com.sleepycat.je.DeadlockException; import com.sleepycat.je.Environment; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.Modification; import org.jboss.cache.loader.bdbje.BdbjeCacheLoaderConfig; import org.jboss.cache.statetransfer.StateTransferManager; import org.jboss.util.stream.MarshalledValueInputStream; import org.jboss.util.stream.MarshalledValueOutputStream; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileFilter; import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; /** * Tests BdbjeCacheLoader directly via the CacheLoader interface. *

*

Run this test case with the current directory set to the JE environment * directory. Any scratch directory will do, but beware that all files in * the directory will be deleted by setUp().

* * @version $Revision: 5530 $ */ @Test(groups = {"functional"}) public class BdbjeTest { private static final int STREAM_HEADER_LENGTH = 4; private static final String envHome = "."; private static final Fqn FQN = Fqn.fromString("/key"); private CacheSPI cache; private CacheLoader loader; /** * Deletes all files in the environment directory. */ @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { File dir = new File(envHome); class MyFilter implements FileFilter { public boolean accept(File file) { return file.getName().endsWith(".jdb"); } } File[] files = dir.listFiles(new MyFilter()); if (files != null) { for (int i = 0; i < files.length; i += 1) { File file = files[i]; if (file.isFile()) { if (!file.delete()) { System.err.println("Unable to delete: " + file); } } } } } /** * Release all resources and ignore exceptions, to shutdown gracefully * when an assertion fires. */ @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { if (loader != null) { try { loader.stop(); } catch (Exception ignored) { } loader = null; } if (cache != null) { try { cache.stop(); } catch (Exception ignored) { } cache = null; } } /** * Creates and starts a loader. * * @param transactional whether to set the TransactionManagerLookupClass * property. * @param dbName a database name, or null to default to the cluster name. */ private void startLoader(boolean transactional, String dbName) throws Exception { /* * Create a dummy CacheSPI object. This is used for setting the cluster * name and TransactionManagerLookupClass (transactional) propertes only. * the CacheSPI object is not used otherwise during testing. */ cache = (CacheSPI) new DefaultCacheFactory().createCache(false); cache.getConfiguration().setClusterName("myCluster"); if (transactional) { cache.getConfiguration().setTransactionManagerLookupClass( "org.jboss.cache.transaction.DummyTransactionManagerLookup"); } cache.start(); /* Derive the config string. */ String configStr; if (dbName != null) { configStr = envHome + '#' + dbName; } else { configStr = envHome; dbName = "myCluster"; } instantiateLoader(); /* Initialize and start the loader. */ loader.setCache(cache); BdbjeCacheLoaderConfig config = new BdbjeCacheLoaderConfig(); config.setLocation(configStr); loader.setConfig(config); loader.create(); loader.start(); /* Verify the database name by trying to open it. */ Environment env = new Environment(new File(envHome), null); DatabaseConfig dbConfig = new DatabaseConfig(); dbConfig.setTransactional(transactional); Database db = env.openDatabase(null, dbName, dbConfig); db.close(); env.close(); } /** * Creates the loader instance. */ private void instantiateLoader() throws Exception { /* Create the cache loader as CacheSPI would. */ Class cls = Class.forName("org.jboss.cache.loader.bdbje.BdbjeCacheLoader"); loader = (CacheLoader) cls.newInstance(); } /** * Stops and destroys the loader. */ private void stopLoader() { loader.stop(); loader.destroy(); } /** * Tests basic operations without a transaction. */ public void testBasicOperations() throws Exception { doTestBasicOperations(false); } /** * Tests basic operations with a transaction. */ public void testBasicOperationsTransactional() throws Exception { doTestBasicOperations(true); } /** * Tests basic operations. */ private void doTestBasicOperations(boolean transactional) throws Exception { startLoader(transactional, null); /* One FQN only. */ doPutTests(FQN); doRemoveTests(FQN); ByteArrayOutputStream baos = new ByteArrayOutputStream(1024); MarshalledValueOutputStream os = new MarshalledValueOutputStream(baos); loader.loadEntireState(os); os.close(); /* Add three FQNs, middle FQN last. */ Fqn k1 = Fqn.fromString("/key1"); Fqn k2 = Fqn.fromString("/key2"); Fqn k3 = Fqn.fromString("/key3"); doPutTests(k1); doPutTests(k3); doPutTests(k2); assertEquals(4, loader.get(k1).size()); assertEquals(4, loader.get(k2).size()); assertEquals(4, loader.get(k3).size()); /* Remove middle FQN first, then the others. */ doRemoveTests(k2); doRemoveTests(k3); doRemoveTests(k1); assertEquals(null, loader.get(k1)); assertEquals(null, loader.get(k2)); assertEquals(null, loader.get(k3)); baos = new ByteArrayOutputStream(1024); os = new MarshalledValueOutputStream(baos); loader.loadEntireState(os); os.close(); stopLoader(); } /** * Do basic put tests for a given FQN. */ private void doPutTests(Fqn fqn) throws Exception { assertTrue(!loader.exists(fqn)); /* put(Fqn,Object,Object) and get(Fqn,Object) */ Object oldVal; oldVal = loader.put(fqn, "one", "two"); assertNull(oldVal); oldVal = loader.put(fqn, "three", "four"); assertNull(oldVal); assertEquals("two", loader.get(fqn).get("one")); assertEquals("four", loader.get(fqn).get("three")); oldVal = loader.put(fqn, "one", "xxx"); assertEquals("two", oldVal); oldVal = loader.put(fqn, "one", "two"); assertEquals("xxx", oldVal); /* get(Fqn) */ Map map = loader.get(fqn); assertEquals(2, map.size()); assertEquals("two", map.get("one")); assertEquals("four", map.get("three")); /* put(Fqn,Map) */ map.put("five", "six"); map.put("seven", "eight"); loader.put(fqn, map); assertEquals("six", loader.get(fqn).get("five")); assertEquals("eight", loader.get(fqn).get("seven")); assertEquals(map, loader.get(fqn)); assertEquals(4, map.size()); assertTrue(loader.exists(fqn)); } /** * Do basic remove tests for a given FQN. */ private void doRemoveTests(Fqn fqn) throws Exception { /* remove(Fqn,Object) */ Object oldVal; oldVal = loader.remove(fqn, "one"); assertEquals("two", oldVal); oldVal = loader.remove(fqn, "five"); assertEquals("six", oldVal); assertEquals(null, loader.get(fqn).get("one")); assertEquals(null, loader.get(fqn).get("five")); assertEquals("four", loader.get(fqn).get("three")); assertEquals("eight", loader.get(fqn).get("seven")); Map map = loader.get(fqn); assertEquals(2, map.size()); assertEquals("four", map.get("three")); assertEquals("eight", map.get("seven")); /* remove(Fqn) */ assertTrue(loader.exists(fqn)); loader.remove(fqn); assertNull("Null expected", loader.get(fqn)); assertTrue(!loader.exists(fqn)); } /** * Tests creating implicit intermediate nodes when a leaf node is created, * and tests removing subtrees. */ public void testMultiLevelTree() throws Exception { startLoader(false, null); /* Create top level node implicitly. */ Fqn k0 = Fqn.fromString("/key0"); Fqn k1 = Fqn.fromString("/key1"); Fqn k2 = Fqn.fromString("/key2"); Fqn k3 = Fqn.fromString("/key3"); assertTrue(!loader.exists(k0)); loader.put(Fqn.fromString("/key0/level1/level2"), null); assertTrue(loader.exists(Fqn.fromString("/key0/level1/level2"))); assertTrue(loader.exists(Fqn.fromString("/key0/level1"))); assertTrue(loader.exists(k0)); /* Remove leaf, leaving implicitly created middle level. */ loader.put(Fqn.fromString("/key0/x/y"), null); assertTrue(loader.exists(Fqn.fromString("/key0/x/y"))); assertTrue(loader.exists(Fqn.fromString("/key0/x"))); loader.remove(Fqn.fromString("/key0/x/y")); assertTrue(!loader.exists(Fqn.fromString("/key0/x/y"))); assertTrue(loader.exists(Fqn.fromString("/key0/x"))); /* Delete top level to delete everything. */ loader.remove(k0); assertTrue(!loader.exists(k0)); assertTrue(!loader.exists(Fqn.fromString("/key0/level1/level2"))); assertTrue(!loader.exists(Fqn.fromString("/key0/level1"))); assertTrue(!loader.exists(Fqn.fromString("/key0/x"))); /* Add three top level nodes as context. */ loader.put(k1, null); loader.put(k2, null); loader.put(k3, null); assertTrue(loader.exists(k1)); assertTrue(loader.exists(k2)); assertTrue(loader.exists(k3)); /* Put /key3/level1/level2. level1 should be implicitly created. */ assertTrue(!loader.exists(Fqn.fromString("/key3/level1"))); assertTrue(!loader.exists(Fqn.fromString("/key3/level1/level2"))); loader.put(Fqn.fromString("/key3/level1/level2"), null); assertTrue(loader.exists(Fqn.fromString("/key3/level1/level2"))); assertTrue(loader.exists(Fqn.fromString("/key3/level1"))); /* Context nodes should still be intact. */ assertTrue(loader.exists(k1)); assertTrue(loader.exists(k2)); assertTrue(loader.exists(k3)); /* Remove middle level only. */ loader.remove(Fqn.fromString("/key3/level1")); assertTrue(!loader.exists(Fqn.fromString("/key3/level1/level2"))); assertTrue(!loader.exists(Fqn.fromString("/key3/level1"))); /* Context nodes should still be intact. */ assertTrue(loader.exists(k1)); assertTrue(loader.exists(k2)); assertTrue(loader.exists(k3)); /* Delete first root, leaving other roots. */ loader.remove(k1); assertTrue(!loader.exists(k1)); assertTrue(loader.exists(k2)); assertTrue(loader.exists(k3)); /* Delete last root, leaving other roots. */ loader.remove(k3); assertTrue(loader.exists(k2)); assertTrue(!loader.exists(k3)); /* Delete final root, leaving none. */ loader.remove(k2); assertTrue(!loader.exists(k0)); assertTrue(!loader.exists(k1)); assertTrue(!loader.exists(k2)); assertTrue(!loader.exists(k3)); /* Repeat all tests above using put(Fqn,Object,Object) and get(Fqn) */ assertNull(loader.get(k0)); loader.put(Fqn.fromString("/key0/level1/level2"), "a", "b"); assertNotNull(loader.get(Fqn.fromString("/key0/level1/level2"))); assertNotNull(loader.get(Fqn.fromString("/key0/level1"))); assertNotNull(loader.get(k0)); assertEquals(0, loader.get(Fqn.fromString("/key0/level1")).size()); assertEquals(0, loader.get(k0).size()); loader.put(Fqn.fromString("/key0/x/y"), "a", "b"); assertNotNull(loader.get(Fqn.fromString("/key0/x/y"))); assertNotNull(loader.get(Fqn.fromString("/key0/x"))); assertEquals(0, loader.get(Fqn.fromString("/key0/x")).size()); loader.remove(Fqn.fromString("/key0/x/y")); assertNull(loader.get(Fqn.fromString("/key0/x/y"))); assertNotNull(loader.get(Fqn.fromString("/key0/x"))); assertEquals(0, loader.get(Fqn.fromString("/key0/x")).size()); loader.remove(k0); assertNull(loader.get(k0)); assertNull(loader.get(Fqn.fromString("/key0/level1/level2"))); assertNull(loader.get(Fqn.fromString("/key0/level1"))); assertNull(loader.get(Fqn.fromString("/key0/x"))); loader.put(k1, "a", "b"); loader.put(k2, "a", "b"); loader.put(k3, "a", "b"); assertNotNull(loader.get(k1)); assertNotNull(loader.get(k2)); assertNotNull(loader.get(k3)); assertNull(loader.get(Fqn.fromString("/key3/level1"))); assertNull(loader.get(Fqn.fromString("/key3/level1/level2"))); loader.put(Fqn.fromString("/key3/level1/level2"), "a", "b"); assertNotNull(loader.get(Fqn.fromString("/key3/level1/level2"))); assertNotNull(loader.get(Fqn.fromString("/key3/level1"))); assertEquals(0, loader.get(Fqn.fromString("/key3/level1")).size()); assertNotNull(loader.get(k1)); assertNotNull(loader.get(k2)); assertNotNull(loader.get(k3)); loader.remove(Fqn.fromString("/key3/level1")); assertNull(loader.get(Fqn.fromString("/key3/level1/level2"))); assertNull(loader.get(Fqn.fromString("/key3/level1"))); assertNotNull(loader.get(k1)); assertNotNull(loader.get(k2)); assertNotNull(loader.get(k3)); loader.remove(k1); assertNull(loader.get(k1)); assertNotNull(loader.get(k2)); assertNotNull(loader.get(k3)); loader.remove(k3); assertNotNull(loader.get(k2)); assertNull(loader.get(k3)); loader.remove(k2); assertNull(loader.get(k0)); assertNull(loader.get(k1)); assertNull(loader.get(k2)); assertNull(loader.get(k3)); stopLoader(); } /** * Tests the getChildrenNames() method. */ public void testGetChildrenNames() throws Exception { startLoader(false, null); checkChildren(Fqn.ROOT, null); checkChildren(Fqn.fromString("/key0"), null); loader.put(Fqn.fromString("/key0"), null); checkChildren(Fqn.ROOT, new String[]{"key0"}); loader.put(Fqn.fromString("/key1/x"), null); checkChildren(Fqn.ROOT, new String[]{"key0", "key1"}); checkChildren(Fqn.fromString("/key1"), new String[]{"x"}); loader.remove(Fqn.fromString("/key1/x")); checkChildren(Fqn.ROOT, new String[]{"key0", "key1"}); checkChildren(Fqn.fromString("/key0"), null); checkChildren(Fqn.fromString("/key1"), null); loader.put(Fqn.fromString("/key0/a"), null); loader.put(Fqn.fromString("/key0/ab"), null); loader.put(Fqn.fromString("/key0/abc"), null); checkChildren(Fqn.fromString("/key0"), new String[]{"a", "ab", "abc"}); loader.put(Fqn.fromString("/key0/xxx"), null); loader.put(Fqn.fromString("/key0/xx"), null); loader.put(Fqn.fromString("/key0/x"), null); checkChildren(Fqn.fromString("/key0"), new String[]{"a", "ab", "abc", "x", "xx", "xxx"}); loader.put(Fqn.fromString("/key0/a/1"), null); loader.put(Fqn.fromString("/key0/a/2"), null); loader.put(Fqn.fromString("/key0/a/2/1"), null); checkChildren(Fqn.fromString("/key0/a/2"), new String[]{"1"}); checkChildren(Fqn.fromString("/key0/a"), new String[]{"1", "2"}); checkChildren(Fqn.fromString("/key0"), new String[]{"a", "ab", "abc", "x", "xx", "xxx"}); loader.put(Fqn.fromString("/key0/\u0000"), null); loader.put(Fqn.fromString("/key0/\u0001"), null); checkChildren(Fqn.fromString("/key0"), new String[]{"a", "ab", "abc", "x", "xx", "xxx", "\u0000", "\u0001"}); loader.put(Fqn.fromString("/\u0001"), null); checkChildren(Fqn.ROOT, new String[]{"key0", "key1", "\u0001"}); loader.put(Fqn.fromString("/\u0001/\u0001"), null); checkChildren(Fqn.fromString("/\u0001"), new String[]{"\u0001"}); loader.put(Fqn.fromString("/\u0001/\uFFFF"), null); checkChildren(Fqn.fromString("/\u0001"), new String[]{"\u0001", "\uFFFF"}); loader.put(Fqn.fromString("/\u0001/\uFFFF/\u0001"), null); checkChildren(Fqn.fromString("/\u0001/\uFFFF"), new String[]{"\u0001"}); stopLoader(); } /** * Checks that the given list of children part names is returned. */ private void checkChildren(Fqn fqn, String[] names) throws Exception { Set set = loader.getChildrenNames(fqn); if (names != null) { assertEquals(names.length, set.size()); for (int i = 0; i < names.length; i += 1) { assertTrue(set.contains(names[i])); } } else { assertNull(set); } } /** * Tests basic operations without a transaction. */ public void testModifications() throws Exception { doTestModifications(false); } /** * Tests basic operations with a transaction. */ public void testModificationsTransactional() throws Exception { doTestModifications(true); } /** * Tests modifications. */ private void doTestModifications(boolean transactional) throws Exception { startLoader(transactional, null); /* PUT_KEY_VALUE, PUT_DATA */ List list = createUpdates(); loader.put(list); checkModifications(list); /* REMOVE_KEY_VALUE */ list = new ArrayList(); Modification mod = new Modification(); mod.setType(Modification.ModificationType.REMOVE_KEY_VALUE); mod.setFqn(FQN); mod.setKey("one"); list.add(mod); loader.put(list); checkModifications(list); /* REMOVE_NODE */ list = new ArrayList(); mod = new Modification(); mod.setType(Modification.ModificationType.REMOVE_NODE); mod.setFqn(FQN); list.add(mod); loader.put(list); checkModifications(list); assertEquals(null, loader.get(FQN)); /* REMOVE_DATA */ loader.put(FQN, "one", "two"); list = new ArrayList(); mod = new Modification(); mod.setType(Modification.ModificationType.REMOVE_DATA); mod.setFqn(FQN); list.add(mod); loader.put(list); checkModifications(list); assertNotNull(loader.get(FQN)); assertEquals(0, loader.get(FQN).size()); stopLoader(); } /** * Tests a one-phase transaction. */ public void testOnePhaseTransaction() throws Exception { startLoader(true, null); List mods = createUpdates(); loader.prepare(null, mods, true); checkModifications(mods); stopLoader(); } /** * Tests a two-phase transaction. */ public void testTwoPhaseTransaction() throws Exception { startLoader(true, null); Object txnKey = new Object(); List mods = createUpdates(); loader.prepare(txnKey, mods, false); try { checkModifications(mods); fail("Expected lock timeout"); } catch (DeadlockException expected) { } loader.commit(txnKey); checkModifications(mods); stopLoader(); } /** * Tests rollback of a two-phase transaction. */ public void testTransactionRollback() throws Exception { startLoader(true, null); Object txnKey = new Object(); List mods = createUpdates(); loader.prepare(txnKey, mods, false); loader.rollback(txnKey); ByteArrayOutputStream baos = new ByteArrayOutputStream(1024); MarshalledValueOutputStream os = new MarshalledValueOutputStream(baos); loader.loadEntireState(os); os.close(); stopLoader(); } /** * Creates a set of update (PUT_KEY_VALUE, PUT_DATA) modifications. */ private List createUpdates() { List list = new ArrayList(); Modification mod = new Modification(); mod.setType(Modification.ModificationType.PUT_KEY_VALUE); mod.setFqn(FQN); mod.setKey("one"); mod.setValue("two"); list.add(mod); mod = new Modification(); mod.setType(Modification.ModificationType.PUT_KEY_VALUE); mod.setFqn(FQN); mod.setKey("three"); mod.setValue("four"); list.add(mod); Map map = new HashMap(); map.put("five", "six"); map.put("seven", "eight"); mod = new Modification(); mod.setType(Modification.ModificationType.PUT_DATA); mod.setFqn(FQN); mod.setData(map); list.add(mod); return list; } /** * Checks that a list of modifications was applied. */ private void checkModifications(List list) throws Exception { for (int i = 0; i < list.size(); i += 1) { Modification mod = list.get(i); Fqn fqn = mod.getFqn(); switch (mod.getType()) { case PUT_KEY_VALUE: assertEquals(mod.getValue(), loader.get(fqn).get(mod.getKey())); break; case PUT_DATA: Map map = mod.getData(); for (Object key : map.keySet()) { assertEquals(map.get(key), loader.get(fqn).get(key)); } break; case REMOVE_KEY_VALUE: assertEquals(null, loader.get(fqn).get(mod.getKey())); break; case REMOVE_DATA: assertTrue(loader.exists(fqn)); assertNotNull(loader.get(fqn)); assertEquals(0, loader.get(fqn).size()); break; case REMOVE_NODE: assertTrue(!loader.exists(fqn)); assertEquals(null, loader.get(fqn)); break; default: fail("unknown type: " + mod); break; } } } /** * Tests a non-transactional prepare. */ public void testTransactionExceptions() throws Exception { List mods = createUpdates(); /* A non-transactional cache loader should not allow prepare(). */ startLoader(false, null); try { loader.prepare(new Object(), mods, false); fail(); } catch (UnsupportedOperationException expected) { } stopLoader(); startLoader(true, null); /* Commit and rollback a non-prepared transaction. */ try { loader.commit(new Object()); fail(); } catch (IllegalArgumentException expected) { } try { loader.rollback(new Object()); fail(); } catch (IllegalArgumentException expected) { } /* Commit and rollback after commit. */ ByteArrayOutputStream baos = new ByteArrayOutputStream(1024); MarshalledValueOutputStream os = new MarshalledValueOutputStream(baos); cache.getMarshaller().objectToObjectStream(StateTransferManager.STREAMING_DELIMITER_NODE, os); os.close(); ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); MarshalledValueInputStream is = new MarshalledValueInputStream(bais); loader.storeEntireState(is); is.close(); Object txnKey = new Object(); loader.prepare(txnKey, mods, false); loader.commit(txnKey); try { loader.commit(txnKey); fail(); } catch (IllegalArgumentException expected) { } try { loader.rollback(txnKey); fail(); } catch (IllegalArgumentException expected) { } /* Commit and rollback after rollback. */ bais = new ByteArrayInputStream(baos.toByteArray()); is = new MarshalledValueInputStream(bais); loader.storeEntireState(is); is.close(); txnKey = new Object(); loader.prepare(txnKey, mods, false); loader.rollback(txnKey); try { loader.rollback(txnKey); fail(); } catch (IllegalArgumentException expected) { } try { loader.rollback(txnKey); fail(); } catch (IllegalArgumentException expected) { } stopLoader(); } /** * Tests that null keys and values work as for a standard Java Map. */ public void testNullKeysAndValues() throws Exception { startLoader(false, null); loader.put(FQN, null, "x"); assertEquals("x", loader.get(FQN).get(null)); Map map = loader.get(FQN); assertEquals(1, map.size()); assertEquals("x", map.get(null)); loader.put(FQN, "y", null); assertEquals(null, loader.get(FQN).get("y")); map = loader.get(FQN); assertEquals(2, map.size()); assertEquals("x", map.get(null)); assertEquals(null, map.get("y")); loader.remove(FQN, null); assertEquals(null, loader.get(FQN).get(null)); assertEquals(1, loader.get(FQN).size()); loader.remove(FQN, "y"); assertNotNull(loader.get(FQN)); assertEquals(0, loader.get(FQN).size()); map = new HashMap(); map.put(null, null); loader.put(FQN, map); assertEquals(map, loader.get(FQN)); loader.remove(FQN); assertNull("Should be null", loader.get(FQN)); map = new HashMap(); map.put("xyz", null); map.put(null, "abc"); loader.put(FQN, map); assertEquals(map, loader.get(FQN)); loader.remove(FQN); assertEquals(null, loader.get(FQN)); stopLoader(); } /** * Test non-default database name. */ public void testDatabaseName() throws Exception { startLoader(false, "nonDefaultDbName"); loader.put(FQN, "one", "two"); assertEquals("two", loader.get(FQN).get("one")); stopLoader(); } /** * Test load/store state. */ public void testLoadAndStore() throws Exception { startLoader(false, null); /* Empty state. */ ByteArrayOutputStream baos = new ByteArrayOutputStream(1024); MarshalledValueOutputStream os = new MarshalledValueOutputStream(baos); loader.loadEntireState(os); cache.getMarshaller().objectToObjectStream(StateTransferManager.STREAMING_DELIMITER_NODE, os); os.close(); ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); MarshalledValueInputStream is = new MarshalledValueInputStream(bais); loader.storeEntireState(is); is.close(); baos = new ByteArrayOutputStream(1024); os = new MarshalledValueOutputStream(baos); loader.loadEntireState(os); cache.getMarshaller().objectToObjectStream(StateTransferManager.STREAMING_DELIMITER_NODE, os); os.close(); bais = new ByteArrayInputStream(baos.toByteArray()); is = new MarshalledValueInputStream(bais); loader.storeEntireState(is); is.close(); baos = new ByteArrayOutputStream(1024); os = new MarshalledValueOutputStream(baos); loader.loadEntireState(os); cache.getMarshaller().objectToObjectStream(StateTransferManager.STREAMING_DELIMITER_NODE, os); os.close(); assertEquals(null, loader.get(FQN)); /* Use a complex object to ensure that the class catalog is used. */ Complex c1 = new Complex(); Complex c2 = new Complex(c1); /* Add objects. */ loader.put(FQN, 1, c1); loader.put(FQN, 2, c2); assertEquals(c1, loader.get(FQN).get(1)); assertEquals(c2, loader.get(FQN).get(2)); assertEquals(2, loader.get(FQN).size()); /* Save state. */ baos = new ByteArrayOutputStream(1024); os = new MarshalledValueOutputStream(baos); loader.loadEntireState(os); cache.getMarshaller().objectToObjectStream(StateTransferManager.STREAMING_DELIMITER_NODE, os); assertTrue(baos.size() > STREAM_HEADER_LENGTH); os.close(); byte[] savedState = baos.toByteArray(); /* Clear state. */ baos = new ByteArrayOutputStream(1024); os = new MarshalledValueOutputStream(baos); cache.getMarshaller().objectToObjectStream(StateTransferManager.STREAMING_DELIMITER_NODE, os); os.close(); bais = new ByteArrayInputStream(baos.toByteArray()); is = new MarshalledValueInputStream(bais); loader.storeEntireState(is); is.close(); assertEquals(null, loader.get(FQN)); /* Restore state. */ bais = new ByteArrayInputStream(savedState); is = new MarshalledValueInputStream(bais); loader.storeEntireState(is); is.close(); assertEquals(c1, loader.get(FQN).get(1)); assertEquals(c2, loader.get(FQN).get(2)); assertEquals(2, loader.get(FQN).size()); stopLoader(); } /** * Complex object whose class description is stored in the class catalog. */ private static class Complex implements Serializable { /** * The serialVersionUID */ private static final long serialVersionUID = -1259096627833244770L; Complex nested; Complex() { this(null); } Complex(Complex nested) { this.nested = nested; } public boolean equals(Object o) { try { Complex x = (Complex) o; return (nested != null) ? nested.equals(x.nested) : (x.nested == null); } catch (ClassCastException e) { return false; } } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/loader/TxCacheLoaderTest.java0000644000175000017500000001320011017455042030046 0ustar twernertwernerpackage org.jboss.cache.loader; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.transaction.TransactionSetup; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.RollbackException; import javax.transaction.SystemException; import javax.transaction.Transaction; import javax.transaction.TransactionManager; import java.util.Set; /** * @author Bela Ban * @version $Id: TxCacheLoaderTest.java 5906 2008-05-29 07:24:18Z mircea.markus $ */ @Test(groups = {"functional", "transaction"}) public class TxCacheLoaderTest extends AbstractCacheLoaderTestBase { CacheSPI cache1, cache2; private Fqn fqn = Fqn.fromString("/one/two/three"); @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { cache1 = (CacheSPI) new DefaultCacheFactory().createCache(false); cache1.getConfiguration().setCacheMode("repl_sync"); cache1.getConfiguration().setTransactionManagerLookupClass(TransactionSetup.getManagerLookup()); cache1.getConfiguration().setCacheLoaderConfig(getSingleCacheLoaderConfig("", DummyInMemoryCacheLoader.class.getName(), "", false, false, false)); // cache1.setReplQueueInterval(3000); cache1.create(); cache1.start(); cache2 = (CacheSPI) new DefaultCacheFactory().createCache(false); cache2.getConfiguration().setCacheMode("repl_sync"); cache2.getConfiguration().setTransactionManagerLookupClass(TransactionSetup.getManagerLookup()); cache2.getConfiguration().setCacheLoaderConfig(getSingleCacheLoaderConfig("", DummyInMemoryCacheLoader.class.getName(), "", false, false, false)); cache2.getConfiguration().setLockAcquisitionTimeout(2000); // cache2.setReplQueueInterval(3000); cache2.create(); cache2.start(); } @AfterMethod(alwaysRun = false) public void tearDown() throws Exception { // clean up cache loaders!! cache1.removeNode(Fqn.ROOT); cache1.stop(); cache1.destroy(); cache2.stop(); cache2.destroy(); } public void testTxPutCommit() throws Exception { TransactionManager mgr = cache1.getConfiguration().getRuntimeConfig().getTransactionManager(); mgr.begin(); cache1.put(fqn, "key1", "val1"); cache1.put("/one/two/three/four", "key2", "val2"); assertNull(cache2.get(fqn, "key1")); assertNull(cache2.get("/one/two/three/four", "key2")); mgr.commit(); assertNotNull(cache1.getNode(fqn).getKeys()); Set children = cache1.getNode("/one").getChildrenNames(); assertEquals(1, children.size()); TestingUtil.sleepThread(2000); assertEquals("val1", cache2.get(fqn, "key1")); assertEquals("val2", cache2.get("/one/two/three/four", "key2")); } public void testTxPrepareAndRollback() throws Exception { final TransactionManager mgr = cache1.getConfiguration().getRuntimeConfig().getTransactionManager(); mgr.begin(); cache1.getConfiguration().setLockAcquisitionTimeout(1500); cache2.getConfiguration().setLockAcquisitionTimeout(1500); Thread locker = new Thread() { Transaction tx2 = null; public void run() { try { mgr.begin(); tx2 = mgr.getTransaction(); cache2.put(fqn, "block-key1", "block-val1");// acquires a lock on cache2./one/two/three TestingUtil.sleepThread(5000); } catch (Exception e) { e.printStackTrace(); } finally { if (tx2 != null) { try { mgr.rollback(); } catch (SystemException e) { e.printStackTrace(); } } } } }; locker.start(); TestingUtil.sleepThread(1000); cache1.put(fqn, "key1", "val1"); cache1.put("/one/two/three/four", "key2", "val2"); try { mgr.commit();// prepare() on cache2 will fail due to lock held by locker thread fail("commit() should fail because we cannot acquire the lock on cache2"); } catch (RollbackException rollback) { System.out.println("--- TX was rolled back (as expected)"); assertTrue(true); } assertNull(cache1.get(fqn, "key1")); assertNull(cache1.get("/one/two/three/four", "key1")); } public void testPutAfterTxCommit() throws Exception { TransactionManager mgr = cache1.getConfiguration().getRuntimeConfig().getTransactionManager(); mgr.begin(); cache1.put(fqn, "key1", "val1"); assertTrue(cache1.exists(fqn)); mgr.commit(); assertTrue(cache1.exists(fqn)); cache1.put("/a/b/c", null);// should be run outside a TX ! assertTrue(cache1.exists("/a/b/c")); } public void testPutAfterTxRollback() throws Exception { TransactionManager mgr = cache1.getConfiguration().getRuntimeConfig().getTransactionManager(); mgr.begin(); cache1.put(fqn, "key1", "val1"); assertTrue(cache1.exists(fqn)); mgr.rollback(); assertFalse(cache1.getCacheLoaderManager().getCacheLoader().exists(fqn)); assertFalse(cache1.exists(fqn)); cache1.put("/a/b/c", null);// should be run outside a TX ! assertTrue(cache1.exists("/a/b/c")); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/loader/ClusteredCacheLoaderTest.java0000644000175000017500000002771011017455042031420 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.loader; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.Region; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.config.Configuration; import org.jboss.cache.lock.TimeoutException; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Random; import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.CountDownLatch; /** * Tests ClusteredCacheLoader * * @author Manik Surtani (manik@jboss.org) */ @Test(groups = {"functional"}) public class ClusteredCacheLoaderTest extends AbstractCacheLoaderTestBase { private static Log log = LogFactory.getLog(ClusteredCacheLoaderTest.class); private CacheSPI cache1, cache2; private CacheLoader loader1, loader2; private Fqn fqn = Fqn.fromString("/a"); private String key = "key"; protected boolean useRegionBasedMarshalling = false; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { if (cache1 != null || cache2 != null) tearDown(); cache1 = (CacheSPI) new DefaultCacheFactory().createCache(false); cache2 = (CacheSPI) new DefaultCacheFactory().createCache(false); cache1.getConfiguration().setClusterName("CCL-Test"); cache1.getConfiguration().setStateRetrievalTimeout(2000); cache2.getConfiguration().setClusterName("CCL-Test"); cache2.getConfiguration().setStateRetrievalTimeout(2000); cache1.getConfiguration().setCacheMode(Configuration.CacheMode.REPL_SYNC); cache2.getConfiguration().setCacheMode(Configuration.CacheMode.REPL_SYNC); cache1.getConfiguration().setCacheLoaderConfig(getSingleCacheLoaderConfig("", "org.jboss.cache.loader.ClusteredCacheLoader", "timeout=5000", false, false, false)); cache2.getConfiguration().setCacheLoaderConfig(getSingleCacheLoaderConfig("", "org.jboss.cache.loader.ClusteredCacheLoader", "timeout=5000", false, false, false)); cache1.getConfiguration().setUseRegionBasedMarshalling(useRegionBasedMarshalling); cache2.getConfiguration().setUseRegionBasedMarshalling(useRegionBasedMarshalling); if (useRegionBasedMarshalling) { cache1.getRegionManager().getRegion(fqn, Region.Type.MARSHALLING, true).registerContextClassLoader(this.getClass().getClassLoader()); cache2.getRegionManager().getRegion(fqn, Region.Type.MARSHALLING, true).registerContextClassLoader(this.getClass().getClassLoader()); } cache1.start(); cache2.start(); loader1 = cache1.getCacheLoaderManager().getCacheLoader(); loader2 = cache2.getCacheLoaderManager().getCacheLoader(); } @AfterMethod(alwaysRun = true) public void tearDown() { System.out.println("***** TEARING DOWN ***** "); if (cache1 != null) { cache1.stop(); cache1 = null; loader1 = null; } if (cache2 != null) { cache2.stop(); cache2 = null; loader2 = null; } } public void testGetKeyValue() throws Exception { cache1.put(fqn, key, "value"); log.info("Finished put"); // test that this has propagated. assertEquals("value", loader1.get(fqn).get(key)); assertEquals("value", loader2.get(fqn).get(key)); cache1.evict(fqn); // now cache 1 should not have this but cache 2 should. // loader1 looks at cache2 while loader2 looks at cache1 assertEquals("value", loader1.get(fqn).get(key)); assertNull("Expecting null", loader2.get(fqn)); // // calling a get on cache1 should cause the loader to retrieve the node from cache2 assertEquals("value", cache1.get(fqn, key)); // // and now loader2 should see it // assertEquals("value", loader2.get(fqn).get(key)); } public void testGet() throws Exception { cache1.put(fqn, key, "value"); // test that this has propagated. Map map = loader1.get(fqn); assertTrue("Should contain key", map.containsKey(key)); assertEquals("value", map.get(key)); assertEquals(1, map.size()); map = loader2.get(fqn); assertTrue("Should contain key", map.containsKey(key)); assertEquals("value", map.get(key)); assertEquals(1, map.size()); cache1.evict(fqn); // now cache 1 should not have this but cache 2 should. // loader1 looks at cache2 while loader2 looks at cache1 map = loader1.get(fqn); assertTrue(map.containsKey(key)); assertEquals("value", map.get(key)); assertEquals(1, map.size()); assertNull("Expecting null", loader2.get(fqn)); map = loader2.get(fqn); assertNull("Should be null", map); // calling a get on cache1 should cause the loader to retrieve the node from cache2 assertEquals("value", cache1.get(fqn, key)); // and now loader2 should see it map = loader2.get(fqn); assertTrue(map.containsKey(key)); assertEquals("value", map.get(key)); assertEquals(1, map.size()); } public void testGetChildrenNames() throws Exception { cache1.put(fqn, key, "value"); Fqn child1 = Fqn.fromRelativeElements(fqn, "child1"); Fqn child2 = Fqn.fromRelativeElements(fqn, "child2"); Fqn child3 = Fqn.fromRelativeElements(fqn, "child3"); cache1.put(child1, key, "value"); cache1.put(child2, key, "value"); cache1.put(child3, key, "value"); // test that this has propagated. Set childNames = loader1.getChildrenNames(fqn); assertEquals(3, childNames.size()); childNames = loader2.getChildrenNames(fqn); assertEquals(3, childNames.size()); cache1.evict(child1); cache1.evict(child2); cache1.evict(child3); cache1.evict(fqn); // now cache 1 should not have this but cache 2 should. // loader1 looks at cache2 while loader2 looks at cache1 assert cache1.peek(fqn, false) == null; childNames = loader1.getChildrenNames(fqn); assert cache1.peek(fqn, false) == null; assertEquals(3, childNames.size()); childNames = loader2.getChildrenNames(fqn); assertNull("should be null", childNames); // calling a get on cache1 should cause the loader to retrieve the node from cache2 assertEquals("value", cache1.get(fqn, key)); // load up children assertEquals("value", cache1.get(child1, key)); assertEquals("value", cache1.get(child2, key)); assertEquals("value", cache1.get(child3, key)); // and now loader2 should see it childNames = loader2.getChildrenNames(fqn); assertEquals(3, childNames.size()); } public void testExists() throws Exception { cache1.put(fqn, key, "value"); // test that this has propagated. assertTrue("should exist", loader1.exists(fqn)); assertTrue("should exist", loader2.exists(fqn)); cache1.evict(fqn); // now cache 1 should not have this but cache 2 should. // loader1 looks at cache2 while loader2 looks at cache1 assertTrue("should exist", loader1.exists(fqn)); assertTrue("should not exist", !loader2.exists(fqn)); // calling a get on cache1 should cause the loader to retrieve the node from cache2 assertEquals("value", cache1.get(fqn, key)); // and now loader2 should see it assertTrue("should exist", loader2.exists(fqn)); } public void testCacheLoaderThreadSafety() throws Exception { threadSafetyTest(true); } public void testCacheLoaderThreadSafetyMultipleFqns() throws Exception { threadSafetyTest(false); } protected void threadSafetyTest(final boolean singleFqn) throws Exception { final CountDownLatch latch = new CountDownLatch(1); final Fqn fqn = Fqn.fromString("/a/b/c"); final List fqns = new ArrayList(30); final Random r = new Random(); if (!singleFqn) { for (int i = 0; i < 30; i++) { Fqn f = Fqn.fromString("/a/b/c/" + i); fqns.add(f); cache2.put(f, "k", "v"); cache1.evict(f); } } else { cache2.put(fqn, "k", "v"); cache1.evict(fqn); } final int loops = 300; // was 1000 final Set exceptions = new CopyOnWriteArraySet(); Thread evictor = new Thread("Evictor") { public void run() { try { latch.await(); for (int i = 0; i < loops; i++) { System.out.println("Evict in loop " + i); Fqn f = singleFqn ? fqn : fqns.get(r.nextInt(fqns.size())); cache1.evict(f); TestingUtil.sleepRandom(50); } } catch (TimeoutException te) { // doesn't matter if we hit these on occasion } catch (Exception e) { exceptions.add(e); } } }; evictor.start(); Thread writer = new Thread("Writer") { public void run() { try { latch.await(); for (int i = 0; i < loops; i++) { System.out.println("Write in loop " + i); Fqn f = singleFqn ? fqn : fqns.get(r.nextInt(fqns.size())); cache2.put(f, "k", "v"); TestingUtil.sleepRandom(50); } } catch (Exception e) { exceptions.add(e); } } }; writer.start(); Thread reader1 = new Thread("Reader-1") { public void run() { try { latch.await(); for (int i = 0; i < loops; i++) { System.out.println("R1 in loop " + i); loader1.get(singleFqn ? fqn : fqns.get(r.nextInt(fqns.size()))); TestingUtil.sleepRandom(50); } } catch (Exception e) { exceptions.add(e); } } }; reader1.start(); Thread reader2 = new Thread("Reader-2") { public void run() { try { latch.await(); for (int i = 0; i < loops; i++) { System.out.println("R2 in loop " + i); loader1.getChildrenNames(singleFqn ? fqn.getParent() : fqns.get(r.nextInt(fqns.size())).getParent()); TestingUtil.sleepRandom(50); } } catch (Exception e) { exceptions.add(e); } } }; reader2.start(); Thread reader3 = new Thread("Reader-3") { public void run() { try { latch.await(); for (int i = 0; i < loops; i++) { System.out.println("R3 in loop " + i); loader1.getChildrenNames(singleFqn ? fqn : fqns.get(r.nextInt(fqns.size()))); TestingUtil.sleepRandom(50); } } catch (Exception e) { exceptions.add(e); } } }; reader3.start(); latch.countDown(); reader1.join(); reader2.join(); reader3.join(); evictor.join(); writer.join(); for (Exception e : exceptions) throw e; } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/loader/ChainingCacheLoaderFullTest.java0000644000175000017500000002415510732232543032033 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.loader; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.XmlConfigurationParser; import org.jboss.cache.transaction.DummyTransactionManagerLookup; import org.jboss.cache.xml.XmlHelper; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertNull; import org.testng.annotations.Test; import org.w3c.dom.Element; import javax.transaction.TransactionManager; /** * Tests ignoreModifications and tests contents of individual loaders * * @author Manik Surtani (manik@jboss.org) */ @Test(groups = {"functional"}) public class ChainingCacheLoaderFullTest { private CacheSPI cache; private ChainingCacheLoader chainingCacheLoader; private CacheLoader loader1, loader2; private Fqn fqn = Fqn.fromString("/a/b"); private String key = "key"; private String value = "value"; protected void startCache(boolean ignoreMods1, boolean ignoreMods2) throws Exception { cache = (CacheSPI) new DefaultCacheFactory().createCache(false); cache.getConfiguration().setCacheLoaderConfig(getCacheLoaderConfig(ignoreMods1, ignoreMods2)); cache.getConfiguration().setCacheMode(Configuration.CacheMode.LOCAL); cache.getConfiguration().setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName()); cache.start(); chainingCacheLoader = (ChainingCacheLoader) cache.getCacheLoaderManager().getCacheLoader(); loader1 = chainingCacheLoader.getCacheLoaders().get(0); loader2 = chainingCacheLoader.getCacheLoaders().get(1); // we need to make sure we have the raw loaders - not the "ReadOnly.." wrapped versions. while (loader1 instanceof AbstractDelegatingCacheLoader) loader1 = ((AbstractDelegatingCacheLoader) loader1).getCacheLoader(); while (loader2 instanceof AbstractDelegatingCacheLoader) loader2 = ((AbstractDelegatingCacheLoader) loader2).getCacheLoader(); } protected void cleanup() throws Exception { cache.removeNode(Fqn.ROOT); cache.stop(); cache = null; } protected CacheLoaderConfig getCacheLoaderConfig(boolean ignoreMods1, boolean ignoreMods2) throws Exception { String xml = "\n" + "false\n" + "\n" + "\n" + "" + DummyInMemoryCacheLoader.class.getName() + "\n" + "" + "\n" + "false\n" + "true\n" + "" + ignoreMods1 + "\n" + "\n" + "\n" + "" + DummyInMemoryCacheLoader.class.getName() + "\n" + "" + "\n" + "false\n" + "false\n" + "" + ignoreMods2 + "\n" + "\n" + ""; Element element = XmlHelper.stringToElement(xml); return XmlConfigurationParser.parseCacheLoaderConfig(element); } public void testCruds() throws Exception { startCache(false, false); // put something in the cache. cache.put(fqn, key, value); assertEquals(value, cache.get(fqn, key)); assertEquals(value, chainingCacheLoader.get(fqn).get(key)); assertEquals(value, loader1.get(fqn).get(key)); assertEquals(value, loader2.get(fqn).get(key)); // evict cache.evict(fqn); assertEquals(value, chainingCacheLoader.get(fqn).get(key)); assertEquals(value, loader1.get(fqn).get(key)); assertEquals(value, loader2.get(fqn).get(key)); assertEquals(value, cache.get(fqn, key)); // remove cache.removeNode(fqn); assertNull(value, cache.get(fqn, key)); assertNull(value, chainingCacheLoader.get(fqn)); assertNull(value, loader1.get(fqn)); assertNull(value, loader2.get(fqn)); cleanup(); } public void testGets() throws Exception { startCache(false, false); cache.put(fqn, key, value); // test that loader1 is always looked up first. cache.evict(fqn); loader1.put(fqn, key, value + 2); assertEquals(value, loader2.get(fqn).get(key)); assertEquals(value + 2, loader1.get(fqn).get(key)); assertEquals(value + 2, chainingCacheLoader.get(fqn).get(key)); assertEquals(value + 2, cache.get(fqn, key)); cache.removeNode(Fqn.ROOT); cache.put(fqn, key, value); // test that loader2 is NOT checked if loader1 has the value cache.evict(fqn); loader2.put(fqn, key, value + 2); assertEquals(value + 2, loader2.get(fqn).get(key)); assertEquals(value, loader1.get(fqn).get(key)); assertEquals(value, chainingCacheLoader.get(fqn).get(key)); assertEquals(value, cache.get(fqn, key)); cache.removeNode(Fqn.ROOT); cache.put(fqn, key, value); // test that loader2 is checked if loader1 returns a null cache.evict(fqn); loader1.remove(fqn); assertNull(loader1.get(fqn)); assertEquals(value, loader2.get(fqn).get(key)); assertEquals(value, chainingCacheLoader.get(fqn).get(key)); assertEquals(value, cache.get(fqn, key)); cleanup(); } public void testIgnoreMods() throws Exception { startCache(false, true); // initialise the loaders loader1.put(fqn, key, value); loader2.put(fqn, key, value); // check contents assertEquals(value, cache.get(fqn, key)); assertEquals(value, chainingCacheLoader.get(fqn).get(key)); assertEquals(value, loader1.get(fqn).get(key)); assertEquals(value, loader2.get(fqn).get(key)); // do a put cache.put(fqn, key, value + 2); assertEquals(value + 2, cache.get(fqn, key)); assertEquals(value + 2, chainingCacheLoader.get(fqn).get(key)); assertEquals(value + 2, loader1.get(fqn).get(key)); assertEquals(value, loader2.get(fqn).get(key)); // remove cache.removeNode(fqn); assertEquals(value, chainingCacheLoader.get(fqn).get(key)); assertNull(loader1.get(fqn)); assertEquals(value, loader2.get(fqn).get(key)); assertEquals(value, cache.get(fqn, key)); cleanup(); } public void testIgnoreModsTransactional() throws Exception { startCache(false, true); TransactionManager mgr = cache.getTransactionManager(); // initialise the loaders loader1.put(fqn, key, value); loader2.put(fqn, key, value); // check contents assertEquals(value, cache.get(fqn, key)); assertEquals(value, chainingCacheLoader.get(fqn).get(key)); assertEquals(value, loader1.get(fqn).get(key)); assertEquals(value, loader2.get(fqn).get(key)); // do a put mgr.begin(); cache.put(fqn, key, value + 2); assertEquals(value + 2, cache.get(fqn, key)); assertEquals(value, chainingCacheLoader.get(fqn).get(key)); assertEquals(value, loader1.get(fqn).get(key)); assertEquals(value, loader2.get(fqn).get(key)); mgr.commit(); assertEquals(value + 2, cache.get(fqn, key)); assertEquals(value + 2, chainingCacheLoader.get(fqn).get(key)); assertEquals(value + 2, loader1.get(fqn).get(key)); assertEquals(value, loader2.get(fqn).get(key)); // remove - not in a tx, see http://jira.jboss.com/jira/browse/JBCACHE-352 // mgr.begin(); cache.removeNode(fqn); // assertNull(cache.get(fqn, key)); // assertEquals(value + 2, chainingCacheLoader.get(fqn).get(key)); // assertEquals(value + 2, loader1.get(fqn).get(key)); // assertEquals(value, loader2.get(fqn).get(key)); // mgr.commit(); assertEquals(value, chainingCacheLoader.get(fqn).get(key)); assertNull(loader1.get(fqn)); assertEquals(value, loader2.get(fqn).get(key)); assertEquals(value, cache.get(fqn, key)); cleanup(); } public void testCrudsTransactional() throws Exception { startCache(false, false); TransactionManager mgr = cache.getTransactionManager(); // assert that the loaders ae empty chainingCacheLoader.remove(cache.getRoot().getFqn()); assertNull(loader1.get(fqn)); assertNull(loader2.get(fqn)); assertNull(chainingCacheLoader.get(fqn)); // put something in the cache. mgr.begin(); cache.put(fqn, key, value); assertEquals(value, cache.get(fqn, key)); assertNull(chainingCacheLoader.get(fqn)); assertNull(loader1.get(fqn)); assertNull(loader2.get(fqn)); mgr.commit(); assertEquals(value, cache.get(fqn, key)); assertEquals(value, chainingCacheLoader.get(fqn).get(key)); assertEquals(value, loader1.get(fqn).get(key)); assertEquals(value, loader2.get(fqn).get(key)); // evict cache.evict(fqn); assertEquals(value, chainingCacheLoader.get(fqn).get(key)); assertEquals(value, loader1.get(fqn).get(key)); assertEquals(value, loader2.get(fqn).get(key)); assertEquals(value, cache.get(fqn, key)); // remove - not in a tx, see http://jira.jboss.com/jira/browse/JBCACHE-352 // mgr.begin(); cache.removeNode(fqn); // assertEquals(value, chainingCacheLoader.get(fqn).get(key)); // assertEquals(value, loader1.get(fqn).get(key)); // assertEquals(value, loader2.get(fqn).get(key)); // assertNull(value, cache.get(fqn, key)); // mgr.commit(); assertNull(value, cache.get(fqn, key)); assertNull(value, chainingCacheLoader.get(fqn)); assertNull(value, loader1.get(fqn)); assertNull(value, loader2.get(fqn)); cleanup(); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/loader/CacheLoaderWithReplicationTest.java0000644000175000017500000002252511020007335032562 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.loader; import org.jboss.cache.Cache; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.commands.tx.CommitCommand; import org.jboss.cache.commands.tx.PrepareCommand; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.util.internals.ReplicationListener; import org.jboss.cache.config.Configuration; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertNull; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.TransactionManager; /** * Tests using cache loaders with replicating data * * @author Manik Surtani (manik@jboss.org) */ @Test(groups = "functional") public class CacheLoaderWithReplicationTest extends AbstractCacheLoaderTestBase { private Cache cache1, cache2; private Fqn fqn = Fqn.fromString("/a"); private String key = "key"; private CacheLoader loader1, loader2; private TransactionManager mgr1, mgr2; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { cache1 = new DefaultCacheFactory().createCache(false); cache1.getConfiguration().setCacheLoaderConfig(getSingleCacheLoaderConfig("", "org.jboss.cache.loader.DummyInMemoryCacheLoader", null, false, true, false)); cache1.getConfiguration().setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); cache2 = new DefaultCacheFactory().createCache(false); cache2.getConfiguration().setCacheLoaderConfig(getSingleCacheLoaderConfig("", "org.jboss.cache.loader.DummyInMemoryCacheLoader", null, false, true, false)); cache2.getConfiguration().setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); loader1 = loader2 = null; mgr1 = mgr2 = null; } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { if (cache1 != null) { try { try { if (mgr1 != null) mgr1.rollback(); } catch (Exception e) { } try { loader1.remove(fqn); cache1.removeNode(fqn); } catch (Exception e) { } cache1.stop(); cache1.destroy(); } finally { cache1 = null; mgr1 = null; loader1 = null; } } if (cache2 != null) { try { try { if (mgr2 != null) mgr2.rollback(); } catch (Exception e) { } try { loader2.remove(fqn); cache2.removeNode(fqn); } catch (Exception e) { } cache2.stop(); cache2.destroy(); } finally { cache2 = null; mgr2 = null; loader2 = null; } } } private void createCaches(boolean sync, boolean optimistic) throws Exception { cache1.getConfiguration().setCacheMode(sync ? Configuration.CacheMode.REPL_SYNC : Configuration.CacheMode.REPL_ASYNC); cache2.getConfiguration().setCacheMode(sync ? Configuration.CacheMode.REPL_SYNC : Configuration.CacheMode.REPL_ASYNC); if (sync) { cache1.getConfiguration().setSyncCommitPhase(true); cache2.getConfiguration().setSyncCommitPhase(true); cache1.getConfiguration().setSyncRollbackPhase(true); cache2.getConfiguration().setSyncRollbackPhase(true); } if (optimistic) { cache1.getConfiguration().setNodeLockingScheme(Configuration.NodeLockingScheme.OPTIMISTIC); cache2.getConfiguration().setNodeLockingScheme(Configuration.NodeLockingScheme.OPTIMISTIC); } cache1.start(); cache2.start(); TestingUtil.blockUntilViewsReceived(10000, cache1, cache2); loader1 = ((CacheSPI) cache1).getCacheLoaderManager().getCacheLoader(); loader2 = ((CacheSPI) cache2).getCacheLoaderManager().getCacheLoader(); // make sure everything is empty... assertNull(loader1.get(fqn)); assertNull(loader2.get(fqn)); assertNull(cache1.getRoot().getChild(fqn)); assertNull(cache2.getRoot().getChild(fqn)); mgr1 = cache1.getConfiguration().getRuntimeConfig().getTransactionManager(); mgr2 = cache2.getConfiguration().getRuntimeConfig().getTransactionManager(); } public void testPessSyncRepl() throws Exception { createCaches(true, false); mgr1.begin(); cache1.put(fqn, key, "value"); assertEquals("value", cache1.get(fqn, key)); assertNull(cache2.get(fqn, key)); assertNull(loader1.get(fqn)); assertNull(loader2.get(fqn)); mgr1.commit(); assertEquals("value", cache1.get(fqn, key)); assertEquals("value", cache2.get(fqn, key)); assertEquals("value", loader1.get(fqn).get(key)); assertEquals("value", loader2.get(fqn).get(key)); mgr1.begin(); cache1.put(fqn, key, "value2"); assertEquals("value2", cache1.get(fqn, key)); assertEquals("value", cache2.get(fqn, key)); assertEquals("value", loader1.get(fqn).get(key)); assertEquals("value", loader2.get(fqn).get(key)); mgr1.rollback(); assertEquals("value", cache1.get(fqn, key)); assertEquals("value", cache2.get(fqn, key)); assertEquals("value", loader1.get(fqn).get(key)); assertEquals("value", loader2.get(fqn).get(key)); } public void testPessAsyncRepl() throws Exception { createCaches(false, false); ReplicationListener replListener = new ReplicationListener(cache2); mgr1.begin(); cache1.put(fqn, key, "value"); assertEquals("value", cache1.get(fqn, key)); assertNull(cache2.get(fqn, key)); assertNull(loader1.get(fqn)); assertNull(loader2.get(fqn)); replListener.expect(PrepareCommand.class); mgr1.commit(); replListener.waitForReplicationToOccur(500); assertEquals("value", cache1.get(fqn, key)); assertEquals("value", cache2.get(fqn, key)); assertEquals("value", loader1.get(fqn).get(key)); assertEquals("value", loader2.get(fqn).get(key)); mgr1.begin(); cache1.put(fqn, key, "value2"); assertEquals("value2", cache1.get(fqn, key)); assertEquals("value", cache2.get(fqn, key)); assertEquals("value", loader1.get(fqn).get(key)); assertEquals("value", loader2.get(fqn).get(key)); mgr1.rollback(); assertEquals("value", cache1.get(fqn, key)); assertEquals("value", cache2.get(fqn, key)); assertEquals("value", loader1.get(fqn).get(key)); assertEquals("value", loader2.get(fqn).get(key)); } public void testOptSyncRepl() throws Exception { createCaches(true, true); mgr1.begin(); cache1.put(fqn, key, "value"); assertEquals("value", cache1.get(fqn, key)); assertNull(cache2.get(fqn, key)); assertNull(loader1.get(fqn)); assertNull(loader2.get(fqn)); mgr1.commit(); assertEquals("value", cache1.get(fqn, key)); assertEquals("value", cache2.get(fqn, key)); assertEquals("value", loader1.get(fqn).get(key)); assertEquals("value", loader2.get(fqn).get(key)); mgr1.begin(); cache1.put(fqn, key, "value2"); assertEquals("value2", cache1.get(fqn, key)); assertEquals("value", cache2.get(fqn, key)); assertEquals("value", loader1.get(fqn).get(key)); assertEquals("value", loader2.get(fqn).get(key)); mgr1.rollback(); assertEquals("value", cache1.get(fqn, key)); assertEquals("value", cache2.get(fqn, key)); assertEquals("value", loader1.get(fqn).get(key)); assertEquals("value", loader2.get(fqn).get(key)); } public void testOptAsyncRepl() throws Exception { createCaches(false, true); ReplicationListener replListener = new ReplicationListener(cache2); mgr1.begin(); replListener.expect(CommitCommand.class); cache1.put(fqn, key, "value"); assertEquals("value", cache1.get(fqn, key)); assertNull(cache2.get(fqn, key)); assertNull(loader1.get(fqn)); assertNull(loader2.get(fqn)); mgr1.commit(); replListener.waitForReplicationToOccur(500); assertEquals("value", cache1.get(fqn, key)); assertEquals("value", cache2.get(fqn, key)); assertEquals("value", loader1.get(fqn).get(key)); assertEquals("value", loader2.get(fqn).get(key)); mgr1.begin(); cache1.put(fqn, key, "value2"); assertEquals("value2", cache1.get(fqn, key)); assertEquals("value", cache2.get(fqn, key)); assertEquals("value", loader1.get(fqn).get(key)); assertEquals("value", loader2.get(fqn).get(key)); mgr1.rollback(); assertEquals("value", cache1.get(fqn, key)); assertEquals("value", cache2.get(fqn, key)); assertEquals("value", loader1.get(fqn).get(key)); assertEquals("value", loader2.get(fqn).get(key)); } }jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/loader/DummySharedInMemoryCacheLoader.java0000644000175000017500000000315411001114151032506 0ustar twernertwernerpackage org.jboss.cache.loader; import org.jboss.cache.Fqn; import org.jboss.cache.config.CacheLoaderConfig; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * An extension of the {@link org.jboss.cache.loader.DummyInMemoryCacheLoader} that uses static maps for data, children, * etc. so it can be shared across instances, emulating a shared database or filesystem cache loader. *

* Since 2.1.0, this dummy cache loader will take an optional parameter, "bin", which contains the name of the "bin" to use * in the static field to store the content. This allows for tests to mimic multiple shared cache loaders in the same cache. * * @author Manik Surtani * @since 2.0.0 */ public class DummySharedInMemoryCacheLoader extends DummyInMemoryCacheLoader { protected static final Map> BINS = new ConcurrentHashMap>(); private String bin = "_default_bin_"; @Override public void setConfig(CacheLoaderConfig.IndividualCacheLoaderConfig cfg) { super.setConfig(cfg); if (config != null && config.getProperties() != null) { bin = config.getProperties().getProperty("bin", "_default_bin_"); } if (!BINS.containsKey(bin)) BINS.put(bin, new ConcurrentHashMap()); nodes = null; // set this to null so any method in superclass that uses this directly will barf with an NPE } @Override protected Map getNodesMap() { return BINS.get(bin); } @Override public void wipe() { BINS.clear(); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/loader/DummyInMemoryCacheLoader.java0000644000175000017500000002041311001114151031354 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.loader; import net.jcip.annotations.ThreadSafe; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.Fqn; import org.jboss.cache.Modification; import org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; /** * Dummy cache loader that stores data in memory * * @author Manik Surtani (manik@jboss.org) */ @ThreadSafe public class DummyInMemoryCacheLoader extends AbstractCacheLoader { // Do NOT access this map directly. always use getNodesMap() since it may be overridden. protected Map nodes = new ConcurrentHashMap(); protected Log log = LogFactory.getLog(DummyInMemoryCacheLoader.class); protected Map> transactions = new ConcurrentHashMap>(); protected boolean debug; // whether to dump System.out messages as well as log messages or not protected final Object NULL = new Object() { @Override public String toString() { return "NULL placeholder"; } }; protected IndividualCacheLoaderConfig config; public void setConfig(IndividualCacheLoaderConfig config) { this.config = config; if (config != null && config.getProperties() != null) { debug = Boolean.parseBoolean(config.getProperties().getProperty("debug", "false")); } } public IndividualCacheLoaderConfig getConfig() { return config; } public Set getChildrenNames(Fqn fqn) throws Exception { if (log.isDebugEnabled()) log.debug("Calling getChildrenNames on Fqn " + fqn + ". Data map = " + getNodesMap()); debugMessage("Calling getChildrenNames on Fqn " + fqn + ". Data map = " + getNodesMap()); if (!getNodesMap().containsKey(fqn)) { log.debug("node not in loader"); debugMessage("node not in loader"); return null; } Set children = findChildren(fqn); log.debug("Fqn " + fqn + " has children " + children); debugMessage("Fqn " + fqn + " has children " + children); // to keep in line with the CacheLoader interface contract for this method. return children.size() == 0 ? null : children; } @SuppressWarnings("unchecked") private Set findChildren(Fqn p) { Set c = new HashSet(); for (Fqn f : getNodesMap().keySet()) { if (!f.isRoot() && f.getParent().equals(p)) { c.add(f.getLastElement()); } } return c; } public Map get(Fqn name) throws Exception { DummyNode dn = getNodesMap().get(name); Map d = dn != null ? dn.data : null; if (log.isDebugEnabled()) log.debug("Getting data for fqn " + name + " = " + d); debugMessage("Getting data for fqn " + name + " = " + d); return stripNULLs(d); } private Map stripNULLs(Map data) { if (data == null) return null; // otherwise make sure we replace NULL placeholders with nulls. Map d = new HashMap(data); if (d.containsKey(NULL)) { Object v = d.remove(NULL); d.put(null, v); } Set keys = new HashSet(); for (Map.Entry e : d.entrySet()) { if (e.getValue() == NULL) { keys.add(e.getKey()); } } for (Object k : keys) { d.put(k, null); } return d; } private Map injectNULLs(Map data) { if (data == null) return null; // otherwise make sure we replace NULL placeholders with nulls. Map d = new HashMap(data); if (d.containsKey(null)) { Object v = d.remove(null); d.put(NULL, v); } Set keys = new HashSet(); for (Map.Entry e : d.entrySet()) { if (e.getValue() == null) { keys.add(e.getKey()); } } for (Object k : keys) { d.put(k, NULL); } return d; } public boolean exists(Fqn name) throws Exception { debugMessage("Performing exists() on " + name); return getNodesMap().containsKey(name); } public Object put(Fqn name, Object key, Object value) throws Exception { DummyNode n = getNodesMap().get(name); if (n == null) { n = new DummyNode(name); } Object k = key == null ? NULL : key; Object v = value == null ? NULL : value; Object old = n.data.put(k, v); getNodesMap().put(name, n); // we need to make sure parents get put in as well. recursivelyPutParentsIfNeeded(name); if (log.isDebugEnabled()) log.debug("Did a put on " + name + ", data is " + n.data); debugMessage("Did a put on " + name + ", data is " + n.data); return old == NULL ? null : old; } public void put(Fqn name, Map attributes) throws Exception { DummyNode n = getNodesMap().get(name); if (n == null) { n = new DummyNode(name); } if (attributes != null) n.data.putAll(injectNULLs(attributes)); getNodesMap().put(name, n); // we need to make sure parents get put in as well. recursivelyPutParentsIfNeeded(name); if (log.isDebugEnabled()) log.debug("Did a put on " + name + ", data is " + n.data); debugMessage("Did a put on " + name + ", data is " + n.data); } private void recursivelyPutParentsIfNeeded(Fqn node) { Fqn parent = node.getParent(); if (getNodesMap().containsKey(parent)) return; // nothing to do. // else put the parent in. getNodesMap().put(parent, new DummyNode(parent)); recursivelyPutParentsIfNeeded(parent); } public Object remove(Fqn fqn, Object key) throws Exception { log.debug("Removing data from " + fqn); debugMessage("Removing data from " + fqn); DummyNode n = getNodesMap().get(fqn); if (n == null) n = new DummyNode(fqn); Object old = n.data.remove(key == null ? NULL : key); getNodesMap().put(fqn, n); return old == NULL ? null : old; } public void remove(Fqn fqn) throws Exception { log.debug("Removing fqn " + fqn); debugMessage("Removing fqn " + fqn); getNodesMap().remove(fqn); // remove children. recursivelyRemoveChildren(fqn); } private void recursivelyRemoveChildren(Fqn removedParent) { for (Fqn f : getNodesMap().keySet()) { if (f.getParent().equals(removedParent)) { // remove the child node too getNodesMap().remove(f); // and it's children. Depth first. recursivelyRemoveChildren(f); } } } public void removeData(Fqn fqn) throws Exception { log.debug("Removing data from " + fqn); debugMessage("Removing data from " + fqn); DummyNode n = getNodesMap().get(fqn); if (n == null) n = new DummyNode(fqn); n.data.clear(); getNodesMap().put(fqn, n); } public class DummyNode { Map data = new ConcurrentHashMap(); Fqn fqn; public DummyNode(Fqn fqn) { this.fqn = fqn; } @Override public String toString() { return "Node{" + "data=" + data + ", fqn=" + fqn + '}'; } } @Override public String toString() { return "DummyInMemoryCacheLoader{" + "getNodesMap()=" + getNodesMap() + '}'; } protected void debugMessage(String msg) { if (debug) System.out.println(" DummyInMemoryCacheLoader debug: " + msg); } /** * ALWAYS use this method instead of accessing the node map directly as it may be overridden. */ protected Map getNodesMap() { return nodes; } public void wipe() { nodes.clear(); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/loader/DataSourceIntegrationTest.java0000644000175000017500000001201110732356553031647 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.loader; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.transaction.DummyTransactionManager; import static org.testng.AssertJUnit.assertNotNull; import static org.testng.AssertJUnit.assertNull; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.NameNotFoundException; import javax.sql.DataSource; import java.io.PrintWriter; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.util.Properties; @Test(groups = "functional") public class DataSourceIntegrationTest extends AbstractCacheLoaderTestBase { private String old_factory = null; private final String FACTORY = "org.jboss.cache.transaction.DummyContextFactory"; private final String JNDI_NAME = "java:/MockDS"; private CacheSPI cache; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { old_factory = System.getProperty(Context.INITIAL_CONTEXT_FACTORY); System.setProperty(Context.INITIAL_CONTEXT_FACTORY, FACTORY); DummyTransactionManager.getInstance(); } protected CacheLoaderConfig getCacheLoaderConfig(String jndi) throws Exception { String props = "cache.jdbc.datasource=" + jndi + "\ncache.jdbc.table.create=true\ncache.jdbc.table.drop=true"; CacheLoaderConfig cacheLoaderConfig = getSingleCacheLoaderConfig("", "org.jboss.cache.loader.JDBCCacheLoader", props, false, false, false); return cacheLoaderConfig; } /** * Tests fix for JBCACHE-303, ensuring that JDBCCacheLoader works if * its DataSource is not in JNDI until start is called. * * @throws Exception */ public void testDataSourceIntegration() throws Exception { Context context = new InitialContext(); try { Object obj = context.lookup(JNDI_NAME); assertNull(JNDI_NAME + " not bound", obj); } catch (NameNotFoundException n) { // expected } cache = (CacheSPI) new DefaultCacheFactory().createCache(false); cache.getConfiguration().setCacheMode("local"); cache.getConfiguration().setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); cache.getConfiguration().setCacheLoaderConfig(getCacheLoaderConfig(JNDI_NAME)); cache.create(); context.bind(JNDI_NAME, new MockDataSource()); assertNotNull(JNDI_NAME + " bound", context.lookup(JNDI_NAME)); cache.start(); assertNotNull("Cache has a cache loader", cache.getCacheLoaderManager().getCacheLoader()); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { Context ctx = new InitialContext(); ctx.unbind(JNDI_NAME); if (old_factory != null) { System.setProperty(Context.INITIAL_CONTEXT_FACTORY, old_factory); } else { System.getProperties().remove(Context.INITIAL_CONTEXT_FACTORY); } if (cache != null) { cache.stop(); cache.destroy(); } } private static class MockDataSource implements DataSource { private String userName; private String jdbcUrl; private String jdbcPassword; public MockDataSource() { Properties properties = new Properties(); try { properties.load(this.getClass().getClassLoader().getResourceAsStream("cache-jdbc.properties")); Class.forName(properties.getProperty("cache.jdbc.driver")); } catch (Exception e) { throw new IllegalStateException("Error loading jdbc properties ", e); } userName = properties.getProperty("cache.jdbc.user"); jdbcUrl = properties.getProperty("cache.jdbc.url"); jdbcPassword = properties.getProperty("cache.jdbc.password"); } public Connection getConnection() throws SQLException { return DriverManager.getConnection(jdbcUrl, userName, jdbcPassword); } public Connection getConnection(String user, String password) throws SQLException { return DriverManager.getConnection(jdbcUrl, userName, jdbcPassword); } public int getLoginTimeout() throws SQLException { return 0; } public PrintWriter getLogWriter() throws SQLException { return null; } public void setLoginTimeout(int seconds) throws SQLException { } public void setLogWriter(PrintWriter printWriter) throws SQLException { } // preliminary JDK6 support - just so it compiles!!! public boolean isWrapperFor(Class ifc) { return false; } public T unwrap(Class iface) { return null; } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/loader/SingletonStoreCacheLoaderTest.java0000644000175000017500000003546211017455042032450 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.loader; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.util.internals.ViewChangeListener; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.factories.UnitTestCacheConfigurationFactory; import org.jboss.cache.factories.XmlConfigurationParser; import org.jboss.cache.xml.XmlHelper; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import org.w3c.dom.Element; import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; /** * Unit test class for SingletonStoreCacheLoader * * @author Galder Zamarreno */ @Test(groups = "functional") public class SingletonStoreCacheLoaderTest extends AbstractCacheLoaderTestBase { private static final Log log = LogFactory.getLog(SingletonStoreCacheLoaderTest.class); private CacheSPI cache1, cache2, cache3; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { cache1 = (CacheSPI) new DefaultCacheFactory().createCache(UnitTestCacheConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC), false); cache2 = (CacheSPI) new DefaultCacheFactory().createCache(UnitTestCacheConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC), false); cache3 = (CacheSPI) new DefaultCacheFactory().createCache(UnitTestCacheConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC), false); } public void testPutCacheLoaderWithNoPush() throws Exception { initSingletonNonPushCache(cache1); initSingletonNonPushCache(cache2); initSingletonNonPushCache(cache3); createCaches(); statCaches(); cache1.put(fqn("/test1"), "key", "value"); cache2.put(fqn("/test2"), "key", "value"); cache3.put(fqn("/test3"), "key", "value"); CacheLoader cl1 = getDelegatingCacheLoader(cache1); CacheLoader cl2 = getDelegatingCacheLoader(cache2); CacheLoader cl3 = getDelegatingCacheLoader(cache3); assertTrue("/test1 should have been entered in cl1", cl1.exists(fqn("/test1"))); assertTrue("/test2 should have been entered in cl1", cl1.exists(fqn("/test2"))); assertTrue("/test3 should have been entered in cl1", cl1.exists(fqn("/test3"))); assertFalse("/test1 should not be in cl2", cl2.exists(fqn("/test1"))); assertFalse("/test2 should not be in cl2", cl2.exists(fqn("/test2"))); assertFalse("/test3 should not be in cl2", cl2.exists(fqn("/test3"))); assertFalse("/test1 should not be in cl3", cl3.exists(fqn("/test1"))); assertFalse("/test2 should not be in cl3", cl3.exists(fqn("/test2"))); assertFalse("/test3 should not be in cl3", cl3.exists(fqn("/test3"))); stopCache1(); cache2.put(fqn("/test4"), "key", "value"); cache3.put(fqn("/test5"), "key", "value"); assertTrue("/test4 should have been entered in cl2", cl2.exists(fqn("/test4"))); assertTrue("/test5 should have been entered in cl2", cl2.exists(fqn("/test5"))); assertFalse("/test4 should not be in cl3", cl3.exists(fqn("/test4"))); assertFalse("/test5 should not be in cl3", cl3.exists(fqn("/test5"))); stopCache2(); cache3.put(fqn("/test6"), "key", "value"); assertTrue("/test5 should have been entered in cl3", cl3.exists(Fqn.fromString("/test6"))); } public void testPutCacheLoaderWithPush() throws Exception { initSingletonWithPushCache(cache1); initSingletonWithPushCache(cache2); initSingletonWithPushCache(cache3); createCaches(); statCaches(); cache1.put(fqn("/a"), "a-key", "a-value"); cache1.put(fqn("/a"), "aa-key", "aa-value"); cache1.put(fqn("/a/b"), "b-key", "b-value"); cache1.put(fqn("/a/b"), "bb-key", "bb-value"); cache1.put(fqn("/a/b/c"), "c-key", "c-value"); cache1.put(fqn("/a/b/d"), "d-key", "d-value"); cache1.put(fqn("/e"), "e-key", "e-value"); cache1.put(fqn("/e/f/g"), "g-key", "g-value"); CacheLoader cl1 = getDelegatingCacheLoader(cache1); CacheLoader cl2 = getDelegatingCacheLoader(cache2); CacheLoader cl3 = getDelegatingCacheLoader(cache3); assertTrue(cl1.get(fqn("/a")).containsKey("a-key")); assertTrue(cl1.get(fqn("/a")).containsKey("aa-key")); assertTrue(cl1.get(fqn("/a/b")).containsKey("b-key")); assertTrue(cl1.get(fqn("/a/b")).containsKey("bb-key")); assertTrue(cl1.get(fqn("/a/b/c")).containsKey("c-key")); assertTrue(cl1.get(fqn("/a/b/d")).containsKey("d-key")); assertTrue(cl1.get(fqn("/e")).containsKey("e-key")); assertTrue(cl1.get(fqn("/e/f/g")).containsKey("g-key")); assertFalse(cl2.exists(fqn("/a"))); assertFalse(cl2.exists(fqn("/a"))); assertFalse(cl2.exists(fqn("/a/b"))); assertFalse(cl2.exists(fqn("/a/b"))); assertFalse(cl2.exists(fqn("/a/b/c"))); assertFalse(cl2.exists(fqn("/a/b/d"))); assertFalse(cl2.exists(fqn("/e"))); assertFalse(cl2.exists(fqn("/e/f/g"))); assertFalse(cl3.exists(fqn("/a"))); assertFalse(cl3.exists(fqn("/a"))); assertFalse(cl3.exists(fqn("/a/b"))); assertFalse(cl3.exists(fqn("/a/b"))); assertFalse(cl3.exists(fqn("/a/b/c"))); assertFalse(cl3.exists(fqn("/a/b/d"))); assertFalse(cl3.exists(fqn("/e"))); assertFalse(cl3.exists(fqn("/e/f/g"))); ViewChangeListener viewChangeListener = new ViewChangeListener(cache2); stopCache1(); viewChangeListener.waitForViewChange(60, TimeUnit.SECONDS); SingletonStoreCacheLoader scl2 = (SingletonStoreCacheLoader) cache2.getCacheLoaderManager().getCacheLoader(); waitForPushStateCompletion(scl2.getPushStateFuture()); assertTrue(cl2.get(fqn("/a")).containsKey("a-key")); assertTrue(cl2.get(fqn("/a")).containsKey("aa-key")); assertTrue(cl2.get(fqn("/a/b")).containsKey("b-key")); assertTrue(cl2.get(fqn("/a/b")).containsKey("bb-key")); assertTrue(cl2.get(fqn("/a/b/c")).containsKey("c-key")); assertTrue(cl2.get(fqn("/a/b/d")).containsKey("d-key")); assertTrue(cl2.get(fqn("/e")).containsKey("e-key")); assertTrue(cl2.get(fqn("/e/f/g")).containsKey("g-key")); cache2.put(fqn("/e/f/h"), "h-key", "h-value"); cache3.put(fqn("/i"), "i-key", "i-value"); assertTrue(cl2.get(fqn("/e/f/h")).containsKey("h-key")); assertTrue(cl2.get(fqn("/i")).containsKey("i-key")); assertFalse(cl3.exists(fqn("/a"))); assertFalse(cl3.exists(fqn("/a"))); assertFalse(cl3.exists(fqn("/a/b"))); assertFalse(cl3.exists(fqn("/a/b"))); assertFalse(cl3.exists(fqn("/a/b/c"))); assertFalse(cl3.exists(fqn("/a/b/d"))); assertFalse(cl3.exists(fqn("/e"))); assertFalse(cl3.exists(fqn("/e/f/g"))); assertFalse(cl3.exists(fqn("/e/f/h"))); assertFalse(cl3.exists(fqn("/i"))); viewChangeListener = new ViewChangeListener(cache3); stopCache2(); viewChangeListener.waitForViewChange(60, TimeUnit.SECONDS); SingletonStoreCacheLoader scl3 = (SingletonStoreCacheLoader) cache3.getCacheLoaderManager().getCacheLoader(); waitForPushStateCompletion(scl3.getPushStateFuture()); assertTrue(cl3.get(fqn("/a")).containsKey("a-key")); assertTrue(cl3.get(fqn("/a")).containsKey("aa-key")); assertTrue(cl3.get(fqn("/a/b")).containsKey("b-key")); assertTrue(cl3.get(fqn("/a/b")).containsKey("bb-key")); assertTrue(cl3.get(fqn("/a/b/c")).containsKey("c-key")); assertTrue(cl3.get(fqn("/a/b/d")).containsKey("d-key")); assertTrue(cl3.get(fqn("/e")).containsKey("e-key")); assertTrue(cl3.get(fqn("/e/f/g")).containsKey("g-key")); assertTrue(cl3.get(fqn("/e/f/h")).containsKey("h-key")); assertTrue(cl3.get(fqn("/i")).containsKey("i-key")); cache3.put(fqn("/a"), "aaa-key", "aaa-value"); assertTrue(cl3.get(fqn("/a")).containsKey("aaa-key")); stopCache3(); } public void testAvoidConcurrentStatePush() throws Exception { final ExecutorService executor = Executors.newFixedThreadPool(2); final CountDownLatch pushStateCanFinish = new CountDownLatch(1); final CountDownLatch secondActiveStatusChangerCanStart = new CountDownLatch(1); final MockSingletonStoreCacheLoader mscl = new MockSingletonStoreCacheLoader(pushStateCanFinish, secondActiveStatusChangerCanStart, new SingletonStoreDefaultConfig()); Future f1 = executor.submit(createActiveStatusChanger(mscl)); secondActiveStatusChangerCanStart.await(); Future f2 = executor.submit(createActiveStatusChanger(mscl)); f1.get(); f2.get(); assertEquals(1, mscl.getNumberCreatedTasks()); } public void testPushStateTimedOut() throws Exception { final CountDownLatch pushStateCanFinish = new CountDownLatch(1); SingletonStoreDefaultConfig ssdc = new SingletonStoreDefaultConfig(); ssdc.setPushStateWhenCoordinatorTimeout(1000); final MockSingletonStoreCacheLoader mscl = new MockSingletonStoreCacheLoader(pushStateCanFinish, null, ssdc); Future f = Executors.newSingleThreadExecutor().submit(createActiveStatusChanger(mscl)); pushStateCanFinish.await(2000, TimeUnit.MILLISECONDS); pushStateCanFinish.countDown(); try { f.get(); fail("Should have timed out"); } catch (ExecutionException e) { Throwable t = e.getCause().getCause().getCause(); assertTrue(t + " should have been TimeoutException", t instanceof TimeoutException); } } private void createCaches() { cache1.create(); cache2.create(); cache3.create(); } private void statCaches() { cache1.start(); cache2.start(); cache3.start(); } private void waitForPushStateCompletion(Future pushThreadFuture) throws Exception { if (pushThreadFuture != null) { pushThreadFuture.get(); } } private Callable createActiveStatusChanger(SingletonStoreCacheLoader mscl) { return new ActiveStatusModifier(mscl); } protected CacheLoaderConfig getSingletonStoreCacheLoaderConfig(String cacheloaderClass) throws Exception { String xml = "\n" + "false\n" + "\n" + "\n" + " " + cacheloaderClass + "\n" + " \n" + " " + " true" + " " + " pushStateWhenCoordinator = true\n" + " pushStateWhenCoordinatorTimeout = 5000\n" + " " + " " + "\n" + ""; Element element = XmlHelper.stringToElement(xml); return XmlConfigurationParser.parseCacheLoaderConfig(element); } private void initSingletonNonPushCache(CacheSPI cache) throws Exception { cache.getConfiguration().setCacheLoaderConfig(getSingletonStoreCacheLoaderConfig( DummyInMemoryCacheLoader.class.getName())); } private void initSingletonWithPushCache(CacheSPI cache) throws Exception { cache.getConfiguration().setCacheLoaderConfig(getSingletonStoreCacheLoaderConfig( DummyInMemoryCacheLoader.class.getName())); } private CacheLoader getDelegatingCacheLoader(CacheSPI cache) { AbstractDelegatingCacheLoader acl = (AbstractDelegatingCacheLoader) cache.getCacheLoaderManager().getCacheLoader(); return acl.getCacheLoader(); } private Fqn fqn(String fqn) { return Fqn.fromString(fqn); } private void stopCache1() { if (cache1 != null) { cache1.stop(); } cache1 = null; } private void stopCache2() { if (cache2 != null) { cache2.stop(); } cache2 = null; } private void stopCache3() { if (cache3 != null) { cache3.stop(); } cache3 = null; } @AfterMethod(alwaysRun = true) public void tearDown() { stopCache1(); stopCache2(); stopCache3(); } class MockSingletonStoreCacheLoader extends SingletonStoreCacheLoader { private int numberCreatedTasks = 0; private CountDownLatch pushStateCanFinish; private CountDownLatch secondActiveStatusChangerCanStart; public MockSingletonStoreCacheLoader(CountDownLatch pushStateCanFinish, CountDownLatch secondActiveStatusChangerCanStart, SingletonStoreDefaultConfig config) { super(config); this.pushStateCanFinish = pushStateCanFinish; this.secondActiveStatusChangerCanStart = secondActiveStatusChangerCanStart; } public int getNumberCreatedTasks() { return numberCreatedTasks; } public void setNumberCreatedTasks(int numberCreatedTasks) { this.numberCreatedTasks = numberCreatedTasks; } @Override protected Callable createPushStateTask() { return new Callable() { public Object call() throws Exception { numberCreatedTasks++; try { if (secondActiveStatusChangerCanStart != null) { secondActiveStatusChangerCanStart.countDown(); } pushStateCanFinish.await(); } catch (InterruptedException e) { fail("ActiveStatusModifier interrupted"); } return null; } }; } @Override protected void awaitForPushToFinish(Future future, int timeout, TimeUnit unit) { pushStateCanFinish.countDown(); super.awaitForPushToFinish(future, timeout, unit); } } class ActiveStatusModifier implements Callable { private SingletonStoreCacheLoader scl; public ActiveStatusModifier(SingletonStoreCacheLoader singleton) { scl = singleton; } public Object call() throws Exception { log.debug("active status modifier started"); scl.activeStatusChanged(true); scl.getPushStateFuture().get(); return null; } } } ././@LongLink0000000000000000000000000000014700000000000011567 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/loader/ClusteredCacheLoaderRegionBasedTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/loader/ClusteredCacheLoaderRegionBasedTest.ja0000644000175000017500000000043510737160437033200 0ustar twernertwernerpackage org.jboss.cache.loader; import org.testng.annotations.Test; @Test(groups = {"functional"}) public class ClusteredCacheLoaderRegionBasedTest extends ClusteredCacheLoaderTest { public ClusteredCacheLoaderRegionBasedTest() { useRegionBasedMarshalling = true; } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/loader/DummyInMemoryCacheLoaderTest.java0000644000175000017500000000151710701315057032236 0ustar twernertwernerpackage org.jboss.cache.loader; import org.jboss.cache.config.CacheLoaderConfig; import org.testng.annotations.Test; /** * Odd that we need a test for a test class, but if we intend to use the {@link org.jboss.cache.loader.DummyInMemoryCacheLoader} as a cache * loader stub then we need to make sure it behaves as a valid cache loader. */ @Test(groups = {"functional"}) public class DummyInMemoryCacheLoaderTest extends CacheLoaderTestsBase { protected void configureCache() throws Exception { // use the shared variation of the DIMCL so that state is persisted in a static variable in memory rather than an // instance one. CacheLoaderConfig clc = getSingleCacheLoaderConfig("", DummySharedInMemoryCacheLoader.class.getName(), "", false, true, false); cache.getConfiguration().setCacheLoaderConfig(clc); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/loader/LocalDelegatingCacheLoaderTest.java0000644000175000017500000000475510741132571032512 0ustar twernertwernerpackage org.jboss.cache.loader; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.config.Configuration; import org.testng.annotations.AfterMethod; import org.testng.annotations.Test; @Test(groups = {"functional"}) public class LocalDelegatingCacheLoaderTest extends CacheLoaderTestsBase { CacheSPI delegating_cache; protected void configureCache() throws Exception { delegating_cache = (CacheSPI) new DefaultCacheFactory().createCache(false); delegating_cache.getConfiguration().setCacheMode(Configuration.CacheMode.LOCAL); delegating_cache.create(); delegating_cache.start(); LocalDelegatingCacheLoaderConfig cfg = new LocalDelegatingCacheLoaderConfig(); cfg.setDelegate(delegating_cache); cfg.setAsync(false); cfg.setFetchPersistentState(false); CacheLoaderConfig cacheLoaderConfig = new CacheLoaderConfig(); cacheLoaderConfig.addIndividualCacheLoaderConfig(cfg); cache.getConfiguration().setCacheLoaderConfig(cacheLoaderConfig); } protected void postConfigure() { CacheLoader ldr = loader; LocalDelegatingCacheLoader ldcl = null; do { if (ldr instanceof LocalDelegatingCacheLoader) { ldcl = (LocalDelegatingCacheLoader) ldr; } else { if (ldr instanceof AbstractDelegatingCacheLoader) { // look deeper in the delegate chain ldr = ((AbstractDelegatingCacheLoader) ldr).getCacheLoader(); } else { // can't dig any deeper! break; } } } while (ldcl == null); ldcl.setDelegateCache(delegating_cache); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { super.tearDown(); delegating_cache.stop(); delegating_cache.destroy(); } @Test(groups = {"functional"}, enabled = false) public void testLoadAndStore() throws Exception { //TODO intentional overload since this test does not pass //http://jira.jboss.com/jira/browse/JBCACHE-851 } public void testPartialLoadAndStore() { // do nothing } public void testBuddyBackupStore() { // do nothing } public void testCacheLoaderThreadSafety() { // do nothing } public void testCacheLoaderThreadSafetyMultipleFqns() { // do nothing } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/loader/InterceptorSynchronizationTest.java0000644000175000017500000001420510741132571033030 0ustar twernertwernerpackage org.jboss.cache.loader; import org.jboss.cache.CacheException; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.Modification; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig; import static org.testng.AssertJUnit.assertTrue; import org.testng.annotations.Test; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; /** * Test case that proves that the CacheImpl is serialized inside the CacheLoaderInterceptor * when the node is empty. ANY call to retrieve a node that is not loaded will lock * up inside CacheLoaderInterceptor while the node is loaded and any other thread * that wants a node out of the cache will wait for the first one to finish before it can even _start_ loading. * * @author paulsmith */ @Test(groups = {"functional"}) public class InterceptorSynchronizationTest { final int CACHELOADER_WAITTIME = 2000;// lets say loading a node takes 2 seconds final int numThreadsPerTopLevelNode = 5;// we'll have 5 requests for nodes within a branch @SuppressWarnings("deprecation") public void testBlockingProblem() throws Exception { CacheSPI cache = (CacheSPI) new DefaultCacheFactory().createCache(false); //setCacheLoader(new TestSlowCacheLoader()); CacheLoaderConfig clc = new CacheLoaderConfig(); IndividualCacheLoaderConfig iclc = new IndividualCacheLoaderConfig(); //iclc.setClassName(TestSlowCacheLoader.class.getName()); iclc.setCacheLoader(new TestSlowCacheLoader()); clc.addIndividualCacheLoaderConfig(iclc); cache.getConfiguration().setCacheLoaderConfig(clc); cache.start(); long begin = System.currentTimeMillis(); Collection threads = new ArrayList(); /* * Create lots of threads all trying to load DIFFERENT fqn's, as well as a set with different top level nodes. */ for (int i = 0; i < numThreadsPerTopLevelNode; i++) { Thread thread = new Thread(new Retriever(cache, "/Moo/" + i)); threads.add(thread); Thread thread2 = new Thread(new Retriever(cache, "/Meow/" + i)); threads.add(thread2); } for (Thread thread : threads) { thread.start(); } for (Thread thread : threads) { thread.join(); } long end = System.currentTimeMillis(); long timeTaken = (end - begin); /* * My expectation is that if there are 2 top level nodes they should be loaded in parallel at the very least, * but even bottom level nodes that are different should be able to be loaded concurrently. * * In this test, NONE of the threads operate in parallel once entered into CacheLoaderInterceptor. */ int totalTimeExpectedToWaitIfNotSerialized = 3 * CACHELOADER_WAITTIME;// i'm being very generous here. 3 times the wait time for each node is more than enough if it was in parallel. assertTrue("If it was parallel, it should have finished quicker than this:" + timeTaken, timeTaken < totalTimeExpectedToWaitIfNotSerialized); } /** * Dummy cache loader that emulates a slow loading of any node from a virtual backing store. * * @author paulsmith */ public class TestSlowCacheLoader extends AbstractCacheLoader { public void setConfig(IndividualCacheLoaderConfig config) { } public IndividualCacheLoaderConfig getConfig() { return null; } public Set getChildrenNames(Fqn arg0) throws Exception { return null; } public Object get(Fqn arg0, Object arg1) { return null; } public Map get(Fqn arg0) throws Exception { Thread.sleep(CACHELOADER_WAITTIME); return Collections.singletonMap((Object) "foo", (Object) "bar"); } public boolean exists(Fqn arg0) throws Exception { return true; } public Object put(Fqn arg0, Object arg1, Object arg2) throws Exception { return null; } public void put(Fqn arg0, Map arg1) throws Exception { } public void put(List modifications) throws Exception { } public Object remove(Fqn arg0, Object arg1) throws Exception { return null; } public void remove(Fqn arg0) throws Exception { } public void removeData(Fqn arg0) throws Exception { } public void prepare(Object tx, List modifications, boolean one_phase) throws Exception { } public void commit(Object arg0) throws Exception { } public void rollback(Object arg0) { } public void loadEntireState(ObjectOutputStream os) throws Exception { // nothing to do here } public void loadState(Fqn subtree, ObjectOutputStream os) throws Exception { // nothing to do here } public void storeEntireState(ObjectInputStream is) throws Exception { // nothing to do here } public void storeState(Fqn subtree, ObjectInputStream is) throws Exception { // nothing to do here } public void create() throws Exception { } public void start() throws Exception { } public void stop() { } public void destroy() { } } private static class Retriever implements Runnable { private final String fqn; private CacheSPI cache; private Retriever(CacheSPI cache, String fqn) { this.fqn = fqn; this.cache = cache; } public void run() { try { cache.get(fqn, "foo"); } catch (CacheException e) { throw new RuntimeException("Unexpected", e); } } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/loader/AbstractCacheLoaderTestBase.java0000644000175000017500000000464710732356553032042 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.loader; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.factories.XmlConfigurationParser; import org.jboss.cache.xml.XmlHelper; import org.testng.annotations.Test; import org.w3c.dom.Element; /** * Very basic test case that provides methods to create a cache loader config. * * @author Manik Surtani (manik@jboss.org) */ @Test(groups = "functional") public abstract class AbstractCacheLoaderTestBase { protected final Log log = LogFactory.getLog(getClass()); protected CacheLoaderConfig getSingleCacheLoaderConfig(String preload, String cacheloaderClass, String properties, boolean async, boolean fetchPersistentState, boolean shared) throws Exception { return getSingleCacheLoaderConfig(preload, cacheloaderClass, properties, async, fetchPersistentState, shared, false); } protected CacheLoaderConfig getSingleCacheLoaderConfig(String preload, String cacheloaderClass, String properties, boolean async, boolean fetchPersistentState, boolean shared, boolean purgeOnStartup) throws Exception { return getSingleCacheLoaderConfig(false, preload, cacheloaderClass, properties, async, fetchPersistentState, shared, purgeOnStartup); } protected CacheLoaderConfig getSingleCacheLoaderConfig(boolean passivation, String preload, String cacheloaderClass, String properties, boolean async, boolean fetchPersistentState, boolean shared, boolean purgeOnStartup) throws Exception { String xml = "\n" + "" + passivation + "\n" + "" + preload + "\n" + "\n" + "" + cacheloaderClass + "\n" + "" + properties + "\n" + "" + async + "\n" + "" + shared + "\n" + "" + fetchPersistentState + "\n" + "" + purgeOnStartup + "\n" + "\n" + ""; Element element = XmlHelper.stringToElement(xml); return XmlConfigurationParser.parseCacheLoaderConfig(element); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/loader/BdbjeCacheLoaderTest.java0000644000175000017500000000301611017455042030465 0ustar twernertwernerpackage org.jboss.cache.loader; import org.jboss.cache.Fqn; import org.jboss.cache.util.TestingUtil; import static org.testng.AssertJUnit.assertEquals; import org.testng.annotations.Test; /** * Runs the same tests as {@link FileCacheLoaderTest}, but with Berkeley DB instead of a file-based CacheLoader * * @author Bela Ban * @version $Id: BdbjeCacheLoaderTest.java 5906 2008-05-29 07:24:18Z mircea.markus $ */ @Test(groups = "functional") public class BdbjeCacheLoaderTest extends CacheLoaderTestsBase { protected void configureCache() throws Exception { String tmpDir = System.getProperty("java.io.tmpdir", "/tmp"); String tmpCLLoc = tmpDir + "/JBossCache-FileCacheLoaderTest"; cache.getConfiguration().setCacheLoaderConfig(getSingleCacheLoaderConfig("", "org.jboss.cache.loader.bdbje.BdbjeCacheLoader", "location=" + tmpCLLoc, false, true, false)); TestingUtil.recursiveFileRemove(tmpCLLoc); } public void testTransaction() throws Exception { // to help recreate the issue in // http://www.jboss.com/index.html?module=bb&op=viewtopic&p=4048003#4048003 Fqn fqn = Fqn.fromString("/a/b/c"); String key = "key", value = "value"; cache.put(fqn, key, value); cache.getTransactionManager().begin(); assertEquals(value, cache.get(fqn, key)); cache.getTransactionManager().commit(); // now repeat this. cache.getTransactionManager().begin(); assertEquals(value, cache.get(fqn, key)); cache.getTransactionManager().commit(); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/loader/JdbmCacheLoaderTest.java0000644000175000017500000000146111017455042030335 0ustar twernertwernerpackage org.jboss.cache.loader; import org.jboss.cache.util.TestingUtil; import org.testng.annotations.Test; /** * Tests {@link org.jboss.cache.loader.jdbm.JdbmCacheLoader}. * * @author Elias Ross * @version $Id: JdbmCacheLoaderTest.java 5906 2008-05-29 07:24:18Z mircea.markus $ */ @Test (groups = {"functional"}) public class JdbmCacheLoaderTest extends CacheLoaderTestsBase { protected void configureCache() throws Exception { String tmpDir = System.getProperty("java.io.tmpdir", "/tmp"); String tmpCLLoc = tmpDir + "/JBossCache-JdbmCacheLoaderTest"; cache.getConfiguration().setCacheLoaderConfig(getSingleCacheLoaderConfig("", "org.jboss.cache.loader.jdbm.JdbmCacheLoader", "location=" + tmpCLLoc, false, true, false)); TestingUtil.recursiveFileRemove(tmpCLLoc); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/loader/JDBCCacheLoaderConfigTest.java0000644000175000017500000000274210665031725031322 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.loader; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertFalse; import static org.testng.AssertJUnit.assertTrue; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * Unit test for JDBCCacheLoaderConfigTest * * @author Galder Zamarreno */ @Test(groups = {"functional"}) public class JDBCCacheLoaderConfigTest { private AdjListJDBCCacheLoaderConfig cacheLoaderConfig; @BeforeMethod(alwaysRun = true) protected void setUp() throws Exception { cacheLoaderConfig = new AdjListJDBCCacheLoaderConfig(); } public void testSetGetConnectionFactory() { cacheLoaderConfig.setConnectionFactoryClass("com.acme.Paradise"); assertEquals("com.acme.Paradise", cacheLoaderConfig.getConnectionFactoryClass()); } public void testEqualsHashCode() { cacheLoaderConfig.setConnectionFactoryClass("com.acme.Paradise"); AdjListJDBCCacheLoaderConfig other = new AdjListJDBCCacheLoaderConfig(); other.setConnectionFactoryClass("com.acme.Paradise"); assertTrue(cacheLoaderConfig.equals(other)); assertEquals(cacheLoaderConfig.hashCode(), other.hashCode()); other.setConnectionFactoryClass("com.ibm.flaming.Gala"); assertFalse(cacheLoaderConfig.equals(other)); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/loader/ChainingCacheLoaderBasicTest.java0000644000175000017500000000507411017455042032147 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.loader; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.factories.XmlConfigurationParser; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.xml.XmlHelper; import org.testng.annotations.Test; import org.w3c.dom.Element; import java.io.File; /** * Tests basic functionality of a chaining cache loader with 2 different loaders * * @author Manik Surtani (manik@jboss.org) */ @Test(groups = {"functional"}) public class ChainingCacheLoaderBasicTest extends CacheLoaderTestsBase { private String loc1 = System.getProperty("java.io.tmpdir", ".") + File.separator + "JBossCache-ChainingCacheLoaderBasicTest-1"; private String loc2 = System.getProperty("java.io.tmpdir", ".") + File.separator + "JBossCache-ChainingCacheLoaderBasicTest-2"; public ChainingCacheLoaderBasicTest() { File dir1 = new File(loc1); File dir2 = new File(loc2); if (!dir1.exists()) dir1.mkdirs(); if (!dir2.exists()) dir2.mkdirs(); } protected void configureCache() throws Exception { cache.getConfiguration().setCacheLoaderConfig(getCacheLoaderConfig(loc1, loc2)); TestingUtil.recursiveFileRemove(loc1); TestingUtil.recursiveFileRemove(loc2); } protected CacheLoaderConfig getCacheLoaderConfig(String loc1, String loc2) throws Exception { String xml = "\n" + "false\n" + "\n" + "\n" + "org.jboss.cache.loader.FileCacheLoader\n" + "" + " location=" + loc1 + "\n" + "\n" + "false\n" + "true\n" + "\n" + "\n" + "org.jboss.cache.loader.FileCacheLoader\n" + "" + " location=" + loc2 + "\n" + "\n" + "false\n" + "false\n" + "\n" + ""; Element element = XmlHelper.stringToElement(xml); return XmlConfigurationParser.parseCacheLoaderConfig(element); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/loader/CacheLoaderTestsBase.java0000755000175000017500000021650511075111056030526 0ustar twernertwernerpackage org.jboss.cache.loader; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.CacheException; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.Modification; import org.jboss.cache.Node; import org.jboss.cache.NodeSPI; import org.jboss.cache.buddyreplication.BuddyManager; import org.jboss.cache.config.Configuration; import org.jboss.cache.statetransfer.StateTransferManager; import org.jboss.cache.transaction.TransactionSetup; import org.jboss.util.stream.MarshalledValueInputStream; import org.jboss.util.stream.MarshalledValueOutputStream; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.Transaction; import javax.transaction.TransactionManager; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Random; import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.CountDownLatch; /** * Commons tests for all CacheLoaders * * @author Bela Ban * @version $Id: CacheLoaderTestsBase.java 6932 2008-10-14 12:35:58Z manik.surtani@jboss.com $ */ @Test(groups = {"functional"}) abstract public class CacheLoaderTestsBase extends AbstractCacheLoaderTestBase { private static final Log log = LogFactory.getLog(CacheLoaderTestsBase.class); CacheSPI cache; CacheLoader loader = null; static final Fqn FQN = Fqn.fromString("/key"); private static final Fqn SUBTREE_FQN = Fqn.fromRelativeElements(FQN, "subtree"); private static final Fqn BUDDY_BASE = Fqn.fromRelativeElements(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN, "test"); private static final Fqn BUDDY_PLUS_FQN = Fqn.fromRelativeFqn(BUDDY_BASE, FQN); private static final Fqn BUDDY_PLUS_SUBTREE_FQN = Fqn.fromRelativeFqn(BUDDY_BASE, SUBTREE_FQN); @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { try { cache = (CacheSPI) new DefaultCacheFactory().createCache(false); Configuration c = cache.getConfiguration(); c.setCacheMode(Configuration.CacheMode.LOCAL); c.setTransactionManagerLookupClass(TransactionSetup.getManagerLookup()); configureCache(); cache.start(); loader = cache.getCacheLoaderManager().getCacheLoader(); postConfigure(); } catch (Exception e) { // e.printStackTrace(); throw e; } } protected void postConfigure() { // no op. Subclass if you need any further cfg after the cache starts. } abstract protected void configureCache() throws Exception; @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { log.warn("********* Starting tearDown()"); cleanup(); try { cache.removeNode(Fqn.ROOT); } catch (Exception e) { // do nothing } try { loader.remove(Fqn.ROOT); } catch (Exception e) { // do nothing } cache.stop(); cache.destroy(); } protected void cleanup() { // to be overridden } protected void addDelay() { // returns immediately in this case. Subclasses may override where a delay is needed. } /** * Helper method to test the existence of a key * * @param fqn * @param key */ protected boolean exists(String fqn, String key) { NodeSPI n = cache.peek(Fqn.fromString(fqn), false, false); if (key == null) return n != null; return n != null && n.getKeysDirect().contains(key); } public void testPrint() throws CacheException { final Fqn NODE = Fqn.fromString("/test"); final String KEY = "key"; cache.put(NODE, KEY, 10); cache.evict(NODE, false); addDelay(); Node ret = cache.getNode(NODE); assertNotNull(ret); } public void testPut() throws CacheException { final String NODE = "/test"; final String KEY = "key"; Object retval; cache.removeNode(NODE); addDelay(); retval = cache.put(NODE, KEY, 10); assertEquals(null, retval); retval = cache.put(NODE, KEY, 20); addDelay(); assertEquals(10, retval); cache.evict(Fqn.fromString(NODE), false);// evicts from memory, but *not* from store addDelay(); log.debug("put 30, expect 20 back"); retval = cache.put(NODE, KEY, 30); assertEquals(20, retval); } public void testPut2() throws Exception { final String NODE = "/a/b/c"; assertNull(loader.get(Fqn.fromString(NODE))); final String KEY = "key"; Object retval; cache.removeNode(NODE); assertNull(loader.get(Fqn.fromString(NODE))); addDelay(); retval = cache.put(NODE, KEY, 10); assertNull(retval); addDelay(); retval = cache.put(NODE, KEY, 20); assertEquals(10, retval); cache.evict(Fqn.fromString(NODE), false);// evicts from memory, but *not* from store cache.evict(Fqn.fromString("/a/b"), false); cache.evict(Fqn.fromString("/a"), false); addDelay(); log.debug("replace KEY with 30, expect 20"); retval = cache.put(NODE, KEY, 30); assertEquals(20, retval); } /** * Tests various Map puts. */ public void testPut3() throws Exception { final Fqn NODE = Fqn.fromString("/a/b/c"); cache.removeNode(NODE); addDelay(); Map m = new HashMap(); m.put("a", "b"); m.put("c", "d"); Map m2 = new HashMap(); m2.put("e", "f"); m2.put("g", "h"); cache.put(NODE, m); addDelay(); cache.get(NODE, "X"); assertEquals(m, loader.get(NODE)); assertEquals(m, cache.getNode(NODE).getData()); cache.evict(NODE, false); addDelay(); cache.get(NODE, "X"); assertEquals(m, cache.getNode(NODE).getData()); cache.evict(NODE, false); cache.get(NODE, "X"); cache.put(NODE, m2); assertEquals("combined", 4, cache.getNode(NODE).getData().size()); } public void testShallowMove() throws Exception { Fqn a = Fqn.fromString("/a"); Fqn b = Fqn.fromString("/b"); Fqn a_b = Fqn.fromString("/a/b"); String key = "key", valueA = "A", valueB = "B"; cache.put(a, key, valueA); cache.put(b, key, valueB); addDelay(); CacheLoader loader = cache.getCacheLoaderManager().getCacheLoader(); assertEquals(valueA, loader.get(a).get(key)); assertEquals(valueB, loader.get(b).get(key)); assertTrue(loader.getChildrenNames(Fqn.ROOT).contains("a")); assertTrue(loader.getChildrenNames(Fqn.ROOT).contains("b")); // now move cache.move(b, a); addDelay(); assertEquals(valueA, loader.get(a).get(key)); assertNull(loader.get(b)); assertEquals(valueB, loader.get(a_b).get(key)); } public void testDeepMove() throws Exception { Fqn a = Fqn.fromString("/a"); Fqn b = Fqn.fromString("/b"); Fqn a_b = Fqn.fromString("/a/b"); Fqn b_c = Fqn.fromString("/b/c"); Fqn a_b_c = Fqn.fromString("/a/b/c"); String key = "key", valueA = "A", valueB = "B", valueC = "C"; cache.put(a, key, valueA); cache.put(b, key, valueB); cache.put(b_c, key, valueC); addDelay(); assertEquals(valueA, cache.getCacheLoaderManager().getCacheLoader().get(a).get(key)); assertEquals(valueB, cache.getCacheLoaderManager().getCacheLoader().get(b).get(key)); assertEquals(valueC, cache.getCacheLoaderManager().getCacheLoader().get(b_c).get(key)); // now move cache.move(b, a); addDelay(); assertEquals(valueA, cache.getCacheLoaderManager().getCacheLoader().get(a).get(key)); assertNull(cache.getCacheLoaderManager().getCacheLoader().get(b)); assertEquals(valueB, cache.getCacheLoaderManager().getCacheLoader().get(a_b).get(key)); assertNull(cache.getCacheLoaderManager().getCacheLoader().get(b_c)); assertEquals(valueC, cache.getCacheLoaderManager().getCacheLoader().get(a_b_c).get(key)); } /** * Tests various put combos which should exercise the CacheLoaderInterceptor. */ public void testPutRemoveCombos() throws Exception { final String NODE = "/a/b/c"; cache.removeNode(NODE); Fqn fqn = Fqn.fromString(NODE); addDelay(); Map m = new HashMap(); m.put("a", "b"); m.put("c", "d"); loader.put(fqn, m); cache.put(NODE, "e", "f"); addDelay(); cache.get(NODE, "X"); assertEquals(3, cache.getNode(NODE).getData().size()); cache.evict(fqn, false); cache.get(NODE, "X"); cache.remove(NODE, "e"); assertEquals(2, cache.getNode(NODE).getData().size()); } public void testGet() throws CacheException { final String NODE = "/a/b/c"; Object retval; cache.removeNode(NODE); addDelay(); retval = cache.put(NODE, "1", 10); assertNull(retval); addDelay(); cache.put(NODE, "2", 20); cache.evict(Fqn.fromString("/a/b/c"), false); assertNull("DataNode should not exisit ", cache.peek(Fqn.fromString("/a/b/c"), false, false)); addDelay(); retval = cache.get(NODE, "1"); assertEquals(10, retval); retval = cache.get(NODE, "2"); assertEquals(20, retval); } @SuppressWarnings("unchecked") public void testGetNode() throws CacheException { final String NODE = "/a/b/c"; Object retval; cache.removeNode(NODE); addDelay(); cache.put(NODE, "1", 10); cache.evict(Fqn.fromString(NODE), false); assertNull("DataNode should not exisit ", cache.peek(Fqn.fromString("/a/b/c"), false, false)); addDelay(); retval = cache.getNode(NODE); assertNotNull("Should not be null", retval); Node node = (Node) retval; assertEquals(10, node.get("1")); } public void testSerialization() throws CacheException { SamplePojo pojo = new SamplePojo(39, "Bela"); pojo.getHobbies().add("Running"); pojo.getHobbies().add("Beerathlon"); pojo.getHobbies().add("Triathlon"); cache.put("/mypojo", 322649, pojo); addDelay(); assertNotNull(cache.get("/mypojo", 322649)); cache.evict(Fqn.fromString("/mypojo"), false); assertNull(cache.peek(Fqn.fromString("/mypojo"), false, false)); SamplePojo pojo2 = (SamplePojo) cache.get("/mypojo", 322649);// should fetch from CacheLoader assertNotNull(pojo2); assertEquals(39, pojo2.getAge()); assertEquals("Bela", pojo2.getName()); assertEquals(3, pojo2.getHobbies().size()); } /** * Just adds some data that wil be later retrieved. This test has to be run first */ public void testPopulate() { try { Map m = new HashMap(); for (int i = 0; i < 10; i++) { m.put("key" + i, "val" + i); } cache.put("/a/b/c", m); // force preload on /1/2/3/4/5 cache.getCacheLoaderManager().preload(Fqn.fromString("/1/2/3/4/5"), true, true); cache.put("/1/2/3/4/5", null); cache.put("/1/2/3/4/5/a", null); cache.put("/1/2/3/4/5/b", null); cache.put("/1/2/3/4/5/c", null); cache.put("/1/2/3/4/5/d", null); cache.put("/1/2/3/4/5/e", null); cache.put("/1/2/3/4/5/d/one", null); cache.put("/1/2/3/4/5/d/two", null); cache.put("/1/2/3/4/5/d/three", null); // cache.put("/a/b/c", "newKey", "newValue"); System.out.println("cache: " + cache); assertTrue(cache.exists("/1/2/3/4")); assertTrue(cache.exists("/a/b/c")); assert (!exists("/a/b/c/d", null)); } catch (Exception e) { fail(e.toString()); } } public void testPreloading() throws CacheException { cache.removeNode(Fqn.ROOT); cache.put("1/2/3/4/5/d", "key", "val"); cache.evict(Fqn.fromString("1/2/3/4/5/d")); System.out.println("-- checking for 1/2/3/4/5/d"); addDelay(); assert (!exists("1/2/3/4/5/d", null));// exists() doesn't load cache.getNode("1/2/3/4/5/d");// get *does* load assertTrue(cache.exists("1/2/3/4/5/d")); System.out.println("-- 1/2/3/4/5/d exists"); } @SuppressWarnings("unchecked") public void testCacheLoading2() throws Exception { Set keys = null; cache.put("/a/b/c", "key", "val"); keys = cache.getNode("/a/b/c").getKeys(); assertNotNull(keys); assertEquals(1, keys.size()); } public void testExists() { cache.put("/eins/zwei/drei", "key1", "val1"); assertTrue(cache.exists("/eins/zwei/drei")); assert (exists("/eins/zwei/drei", "key1")); assert (!exists("/eins/zwei/drei", "key2")); assert (!exists("/uno/due/tre", null)); assert (!exists("/une/due/tre", "key1")); } public void testGetChildren() { try { cache.put("/1/2/3/4/5/d/one", null); cache.put("/1/2/3/4/5/d/two", null); cache.put("/1/2/3/4/5/d/three", null); Set children = cache.getNode("/1/2/3/4/5/d").getChildrenNames(); assertNotNull(children); assertEquals(3, children.size()); assertTrue(children.contains("one")); assertTrue(children.contains("two")); assertTrue(children.contains("three")); } catch (Exception e) { fail(e.toString()); } } public void testGetChildrenWithEviction() throws CacheException { cache.put("/a/b/c/1", null); cache.put("/a/b/c/2", null); cache.put("/a/b/c/3", null); cache.evict(Fqn.fromString("/a/b/c/1")); cache.evict(Fqn.fromString("/a/b/c/2")); cache.evict(Fqn.fromString("/a/b/c/3")); cache.evict(Fqn.fromString("/a/b/c")); cache.evict(Fqn.fromString("/a/b")); cache.evict(Fqn.fromString("/a")); cache.evict(Fqn.fromString("/")); addDelay(); Set children = cache.getNode("/a/b/c").getChildrenNames(); assertNotNull(children); assertEquals(3, children.size()); assertTrue(children.contains("1")); assertTrue(children.contains("2")); assertTrue(children.contains("3")); } public void testGetChildren2() { try { cache.put("/1", null); cache.put("a", null); Set children = cache.getRoot().getChildrenNames(); assertNotNull(children); assertEquals(2, children.size()); assertTrue(children.contains("1")); assertTrue(children.contains("a")); } catch (Exception e) { fail(e.toString()); } } public void testGetChildren3() { try { cache.put("/1", null); cache.put("a", null); Set children = cache.getRoot().getChildrenNames(); assertNotNull(children); assertEquals(2, children.size()); assertTrue(children.contains("1")); assertTrue(children.contains("a")); } catch (Exception e) { fail(e.toString()); } } public void testGetChildren4() { try { if (!cache.exists("/a/b/c")) { cache.put("/a/b/c", null); } Set children = cache.getChildrenNames((Fqn) null); assertTrue(children.isEmpty()); } catch (Exception e) { fail(e.toString()); } } public void testGetChildren5() { try { cache.put("/a/1", null); cache.put("/a/2", null); cache.put("/a/3", null); Node n = cache.getNode("/a"); assertNotNull(n); Set children = n.getChildrenNames(); assertNotNull(children); assertEquals(3, children.size()); } catch (Exception e) { fail(e.toString()); } } public void testGetChildren6() { try { cache.put("/a/1", null); cache.put("/a/2", null); cache.put("/a/3", null); cache.evict(Fqn.fromString("/a/1")); cache.evict(Fqn.fromString("/a/2")); cache.evict(Fqn.fromString("/a/3")); cache.evict(Fqn.fromString("/a")); addDelay(); assertNotNull(cache.getNode("/a")); Set children = cache.getNode("/a").getChildrenNames(); assertNotNull("No children were loaded", children); System.out.println("children: " + children); assertEquals("3 children weren't loaded", 3, children.size()); } catch (Exception e) { fail(e.toString()); } } public void testGetChildren7() { try { cache.put("/a/1", null); cache.put("/a/2", null); cache.put("/a/3", null); cache.put("/a", "test", "test"); cache.evict(Fqn.fromString("/a/1")); cache.evict(Fqn.fromString("/a/2")); cache.evict(Fqn.fromString("/a/3")); cache.evict(Fqn.fromString("/a")); addDelay(); Object val = cache.get("/a", "test"); assertEquals("attributes weren't loaded", "test", val); Set children = cache.getNode("/a").getChildrenNames(); assertNotNull("No children were loaded", children); System.out.println("children: " + children); assertEquals("3 children weren't loaded", 3, children.size()); } catch (Exception e) { fail(e.toString()); } } public void testGetChildren8() { cache.put("/a/1", null); cache.put("/a/2", null); cache.put("/a/3", null); cache.evict(Fqn.fromString("/a/1")); cache.evict(Fqn.fromString("/a/2")); cache.evict(Fqn.fromString("/a/3")); cache.evict(Fqn.fromString("/a")); addDelay(); assertNull(cache.get("/a", "test")); cache.getNode("/a/1"); Set children = cache.getNode("/a").getChildrenNames(); assertNotNull("No children were loaded", children); System.out.println("children: " + children); assertEquals("3 children weren't loaded", 3, children.size()); } public void testGetChildren9() { try { cache.put("/a/1", null); cache.put("/a/2", null); cache.put("/a/3", null); cache.evict(Fqn.fromString("/a/1")); cache.evict(Fqn.fromString("/a/2")); cache.evict(Fqn.fromString("/a/3")); cache.evict(Fqn.fromString("/a")); addDelay(); assertNull(cache.get("/a", "test")); cache.getNode("/a/1"); Set children = cache.getNode("/a").getChildrenNames(); assertNotNull("No children were loaded", children); System.out.println("children: " + children); assertEquals("3 children weren't loaded", 3, children.size()); cache.evict(Fqn.fromString("/a/1")); cache.evict(Fqn.fromString("/a/2")); cache.evict(Fqn.fromString("/a/3")); cache.evict(Fqn.fromString("/a")); assertNull(cache.get("/a", "test")); cache.getNode("/a/1"); children = cache.getNode("/a").getChildrenNames(); assertNotNull("No children were loaded", children); System.out.println("children: " + children); assertEquals("3 children weren't loaded", 3, children.size()); } catch (Exception e) { fail(e.toString()); } } public void testGetChildren10() { try { cache.put("/a/1", null); cache.put("/a/2", null); cache.put("/a/3", null); cache.evict(Fqn.fromString("/a/1")); cache.evict(Fqn.fromString("/a/2")); cache.evict(Fqn.fromString("/a/3")); cache.evict(Fqn.fromString("/a")); addDelay(); assertNull(cache.get("/a", "test")); cache.getNode("/a/1"); Set children = cache.getNode("/a").getChildrenNames(); assertNotNull("No children were loaded", children); System.out.println("children: " + children); assertEquals("3 children weren't loaded", 3, children.size()); children = cache.getNode("/a").getChildrenNames(); assertNotNull("No children were loaded", children); System.out.println("children: " + children); assertEquals("3 children weren't loaded", 3, children.size()); } catch (Exception e) { fail(e.toString()); } } public void testGetChildren11() { Set children; try { cache.put("/a/b", "key", "val"); cache.put("/a/b/1", "key", "val"); cache.put("/a/b/2", "key", "val"); cache.put("/a/b/3", "key", "val"); cache.put("/a/b/1/tmp", "key", "val"); cache.put("/a/b/2/tmp", "key", "val"); cache.put("/a/b/3/tmp", "key", "val"); cache.evict(Fqn.fromString("/a")); cache.evict(Fqn.fromString("/a/b")); cache.evict(Fqn.fromString("/a/b/1")); cache.evict(Fqn.fromString("/a/b/2")); cache.evict(Fqn.fromString("/a/b/3")); // now load the children - this set childrenLoaded in /a/b to true children = cache.getNode("/a/b").getChildrenNames(); assertEquals(3, children.size()); cache.evict(Fqn.fromString("/a/b")); cache.evict(Fqn.fromString(("/a/b/1/tmp"))); cache.evict(Fqn.fromString(("/a/b/2/tmp"))); cache.evict(Fqn.fromString(("/a/b/3/tmp"))); cache.evict(Fqn.fromString(("/a/b/1"))); cache.evict(Fqn.fromString(("/a/b/2"))); cache.evict(Fqn.fromString(("/a/b/3"))); cache.evict(Fqn.fromString("/a")); children = cache.getNode("/a/b").getChildrenNames(); assertEquals(3, children.size()); } catch (Exception e) { fail(e.toString()); } } public void testGetChildren12() { Set children; try { cache.put("/a/b", "key", "val"); cache.put("/a/b/1", "key", "val"); cache.put("/a/b/2", "key", "val"); cache.put("/a/b/3", "key", "val"); children = cache.getNode("/a/b").getChildrenNames(); assertEquals(3, children.size()); cache.evict(Fqn.fromString("/a/b/3")); cache.evict(Fqn.fromString("/a/b/2")); // cache.evict(Fqn.fromString("/a/b/1")); cache.evict(Fqn.fromString("/a/b")); cache.evict(Fqn.fromString("/a")); NodeSPI n = (NodeSPI) cache.getNode("/a/b"); assert !n.isChildrenLoaded(); children = cache.getNode("/a/b").getChildrenNames(); assertEquals(3, children.size()); cache.evict(Fqn.fromString("/a/b/3")); cache.evict(Fqn.fromString("/a/b/2")); // cache.evict(Fqn.fromString("/a/b/1")); cache.evict(Fqn.fromString("/a/b")); cache.evict(Fqn.fromString("/a")); children = cache.getNode("/a/b").getChildrenNames(); assertEquals(3, children.size()); } catch (Exception e) { fail(e.toString()); } } public void testLoaderGetChildrenNames() throws Exception { Fqn f = Fqn.fromString("/a"); cache.put(f, "k", "v"); assertEquals("v", loader.get(f).get("k")); assertNull(loader.getChildrenNames(f)); } public void testGetKeys() throws Exception { Fqn f = Fqn.fromString("/a"); cache.put(f, "one", "one"); cache.put(f, "two", "two"); cache.evict(f); Set keys = cache.getRoot().getChild(f).getKeys(); assertEquals("Correct # of keys", 2, keys.size()); assertTrue("Has key 'one", keys.contains("one")); assertTrue("Has key 'two", keys.contains("two")); } public void testGetData() throws Exception { Fqn f = Fqn.fromString("/a"); assert !cache.exists(f); assert !loader.exists(f); cache.put(f, "one", "one"); cache.put(f, "two", "two"); cache.evict(f); Map data = cache.getRoot().getChild(f).getData(); assertEquals("incorrect # of entries", 2, data.size()); assertEquals("Has key 'one", "one", data.get("one")); assertEquals("Has key 'two", "two", data.get("two")); } public void testPutDataMap() throws Exception { Fqn f = Fqn.fromString("/a"); assert !cache.exists(f); assert !loader.exists(f); Map input = new HashMap(); input.put("one", "one"); input.put("two", "two"); cache.put(f, input); cache.evict(f); Object three = "three"; // avoid some weird generics complaint cache.put(f, Collections.singletonMap(three, three)); Map data = cache.getRoot().getChild(f).getData(); assertEquals("incorrect # of entries", 3, data.size()); assertEquals("Has key 'one", "one", data.get("one")); assertEquals("Has key 'two", "two", data.get("two")); assertEquals("Has key 'three", "three", data.get("three")); } public void testPutNullDataMap() throws Exception { Fqn f = Fqn.fromString("/a"); assert !cache.exists(f); assert !loader.exists(f); cache.put(f, null); Map fromLoader = loader.get(f); assert fromLoader != null : "Node should exist in the loader"; assert fromLoader.isEmpty() : "Should not contain any data"; } public void testPutNullDataMapNodeHasData() throws Exception { Fqn f = Fqn.fromString("/a"); cache.put(f, "key", "value"); assert cache.exists(f); assert loader.exists(f); cache.put(f, null); Map fromLoader = loader.get(f); assert fromLoader != null : "Node should exist in the loader"; assert fromLoader.size() == 1 : "Should contain original data"; } public void testRemoveData() { String key = "/x/y/z/"; cache.put(key, "keyA", "valA"); cache.put(key, "keyB", "valB"); cache.put(key, "keyC", "valC"); assertEquals(3, cache.getNode(key).getKeys().size()); cache.getNode(key).clearData(); Set keys = cache.getNode(key).getKeys(); assertEquals(0, keys.size()); cache.removeNode("/x"); Object val = cache.get(key, "keyA"); assertNull(val); } public void testRemoveData2() { Set keys; Fqn key = Fqn.fromString("/x/y/z/"); cache.put(key, "keyA", "valA"); cache.put(key, "keyB", "valB"); cache.put(key, "keyC", "valC"); addDelay(); keys = cache.getNode(key).getKeys(); assertEquals(3, keys.size()); cache.getNode(key).clearData(); cache.evict(key); addDelay(); keys = cache.getNode(key).getKeys(); assertNotNull(keys); assertEquals(0, keys.size()); } public void testRemoveData3() { Set keys; Fqn key = Fqn.fromString("/x/y/z/"); cache.put(key, "keyA", "valA"); cache.put(key, "keyB", "valB"); cache.put(key, "keyC", "valC"); keys = cache.getNode(key).getKeys(); assertEquals(3, keys.size()); cache.evict(key); cache.getNode(key).clearData(); keys = cache.getNode(key).getKeys(); assertEquals("no more keys", 0, keys.size()); } public void testRemoveData4() throws Exception { Set keys; Fqn key = Fqn.fromString("/x/y/z/"); cache.put(key, "keyA", "valA"); cache.put(key, "keyB", "valB"); cache.put(key, "keyC", "valC"); keys = cache.getNode(key).getKeys(); assertEquals(3, keys.size()); cache.evict(key); Map map = new HashMap(); map.put("keyA", "valA"); map.put("keyB", "valB"); map.put("keyC", "valC"); assertEquals(map, loader.get(key)); Node n = cache.getRoot().getChild(key); n.clearData(); assertEquals(Collections.emptyMap(), loader.get(key)); } public void testReplaceAll() throws Exception { Set keys; Fqn key = Fqn.fromString("/x/y/z/"); cache.put(key, "keyA", "valA"); cache.put(key, "keyB", "valB"); cache.put(key, "keyC", "valC"); keys = cache.getNode(key).getKeys(); assertEquals(3, keys.size()); cache.evict(key); Map map = new HashMap(); map.put("keyA", "valA"); map.put("keyB", "valB"); map.put("keyC", "valC"); Map newMap = new HashMap(); newMap.put("keyD", "valD"); newMap.put("keyE", "valE"); assertEquals(map, loader.get(key)); Node n = cache.getRoot().getChild(key); n.replaceAll(newMap); assertEquals(newMap, loader.get(key)); } public void testRemoveKey() { String key = "/x/y/z/"; cache.put(key, "keyA", "valA"); assertEquals(1, cache.getNode(key).getKeys().size()); cache.put(key, "keyB", "valB"); assertEquals(2, cache.getNode(key).getKeys().size()); cache.put(key, "keyC", "valC"); assertEquals(3, cache.getNode(key).getKeys().size()); cache.remove(key, "keyA"); assertEquals(2, cache.getNode(key).getKeys().size()); cache.removeNode("/x"); } public void testRemoveKey2() throws CacheException { final String NODE = "/test"; final String KEY = "key"; Object retval; cache.removeNode(NODE); retval = cache.put(NODE, KEY, 10); assertNull(retval); addDelay(); retval = cache.remove(NODE, KEY); assertEquals(10, retval); addDelay(); retval = cache.remove(NODE, KEY); assertNull(retval); } public void testRemoveKey3() throws CacheException { final String NODE = "/test"; final String KEY = "key"; Object retval; cache.removeNode(NODE); retval = cache.put(NODE, KEY, 10); assertNull(retval); cache.evict(Fqn.fromString(NODE));// evicts from memory, but *not* from store addDelay(); retval = cache.remove(NODE, KEY); assertEquals(10, retval); cache.evict(Fqn.fromString(NODE));// evicts from memory, but *not* from store addDelay(); retval = cache.remove(NODE, KEY); assertNull(retval); } public void testRemove() { String key = "/x/y/z/"; cache.put(key, "keyA", "valA"); cache.put(key, "keyB", "valB"); cache.put(key, "keyC", "valC"); cache.removeNode("/x"); assertNull(cache.get(key, "keyA")); addDelay(); Set keys = cache.getKeys(key); assertNull("got keys " + keys, keys); cache.removeNode("/x"); } public void testRemoveRoot() { assertEquals(0, cache.getRoot().getKeys().size()); cache.put("/1/2/3/4/5", null); cache.put("uno/due/tre", null); cache.put("1/2/3/a", null); cache.put("/eins/zwei/drei", null); cache.put("/one/two/three", null); cache.removeNode(Fqn.ROOT); assertEquals(0, cache.getRoot().getKeys().size()); } public void testLoaderRemoveRoot() throws Exception { Fqn f = Fqn.fromString("/a"); loader.put(f, "k", "v"); assertTrue(loader.get(f).containsKey("k")); loader.remove(f); assertNull(loader.get(f)); loader.put(f, "k", "v"); assertTrue(loader.get(f).containsKey("k")); loader.remove(Fqn.ROOT); assertNull("Removing Fqn.ROOT should cause all children to be removed as well", loader.get(f)); } public void testEvictionWithCacheLoader() { cache.put("/first/second", "key1", "val1");// stored in cache loader cache.put("/first/second/third", "key2", "val2");// stored in cache loader cache.evict(Fqn.fromString("/first/second"));// doesn't remove node, just data ! addDelay(); assertTrue(cache.exists("/first/second/third")); assertTrue(cache.exists("/first/second")); assertTrue(cache.exists("/first")); String val = (String) cache.get("/first/second", "key1");// should be loaded from cache loader assertEquals("val1", val); assertTrue(cache.exists("/first/second/third")); assertTrue(cache.exists("/first/second")); assertTrue(cache.exists("/first")); } public void testEvictionWithCacheLoader2() { cache.put("/first/second/third", "key1", "val1");// stored in cache loader cache.evict(Fqn.fromString("/first/second/third"));// removes node, because there are no children addDelay(); assert (!exists("/first/second/third", null)); assertTrue(cache.exists("/first/second")); assertTrue(cache.exists("/first")); String val = (String) cache.get("/first/second/third", "key1");// should be loaded from cache loader assertEquals("val1", val); assertTrue(cache.exists("/first/second/third")); assertTrue(cache.exists("/first/second")); assertTrue(cache.exists("/first")); } public void testEvictionWithGetChildrenNames() throws Exception { cache.put("/a/1", null); cache.put("/a/2", null); cache.put("/a/3", null); // cache.put("/a/1/tmp", null); cache.evict(Fqn.fromString("/a/1")); cache.evict(Fqn.fromString("/a/2")); cache.evict(Fqn.fromString("/a/3")); cache.evict(Fqn.fromString("/a")); addDelay(); TransactionManager mgr = getTransactionManager(); mgr.begin(); Set children = cache.getNode("/a").getChildrenNames(); System.out.println("**** " + cache.getTransactionManager()); assertEquals(3, children.size()); assertTrue(children.contains("1")); assertTrue(children.contains("2")); assertTrue(children.contains("3")); assertEquals(5, cache.getNumberOfLocksHeld()); mgr.commit(); } public void testTxPutCommit() throws Exception { TransactionManager mgr = getTransactionManager(); mgr.begin(); cache.put("/one/two/three", "key1", "val1"); cache.put("/one/two/three/four", "key2", "val2"); mgr.commit(); assertNotNull("Cache has node /one/two/three", cache.getNode("/one/two/three").getKeys()); assertNotNull("Loader has node /one/two/three", loader.get(Fqn.fromString("/one/two/three"))); Set children = cache.getNode("/one").getChildrenNames(); assertEquals("Cache has correct number of children", 1, children.size()); children = loader.getChildrenNames(Fqn.fromString("/one")); assertEquals("Loader has correct number of children", 1, children.size()); cache.removeNode(Fqn.ROOT); } public void testTxPutRollback() throws Exception { TransactionManager mgr = getTransactionManager(); cache.removeNode("/one"); addDelay(); mgr.begin(); cache.put("/one/two/three", "key1", "val1"); cache.put("/one/two/three/four", "key2", "val2"); log.debug("NODE1 " + cache.getNode("/one/two/three").getData()); mgr.rollback(); log.debug("NODE2 " + cache.getNode("/one/two/three")); assertEquals(null, cache.get("/one/two/three", "key1")); assertEquals(null, cache.get("/one/two/three/four", "key2")); addDelay(); assertNull("Loader should not have node /one/two/three", loader.get(Fqn.fromString("/one/two/three"))); assertNull("Cache should not have node /one/two/three", cache.getKeys("/one/two/three")); Set children = cache.getChildrenNames("/one"); assertEquals("Cache has no children under /one", 0, children.size()); children = loader.getChildrenNames(Fqn.fromString("/one")); assertEquals("Loader has no children under /one", null, children); } /** * Tests basic operations without a transaction. */ public void testBasicOperations() throws Exception { doTestBasicOperations(); } /** * Tests basic operations with a transaction. */ public void testBasicOperationsTransactional() throws Exception { TransactionManager mgr = getTransactionManager(); mgr.begin(); doTestBasicOperations(); mgr.commit(); } /** * Tests basic operations. */ private void doTestBasicOperations() throws Exception { /* One FQN only. */ doPutTests(Fqn.fromString("/key")); doRemoveTests(Fqn.fromString("/key")); // assertEquals(0, loader.loadEntireState().length); /* Add three FQNs, middle FQN last. */ doPutTests(Fqn.fromString("/key1")); doPutTests(Fqn.fromString("/key3")); doPutTests(Fqn.fromString("/key2")); assertEquals(4, loader.get(Fqn.fromString("/key1")).size()); assertEquals(4, loader.get(Fqn.fromString("/key2")).size()); assertEquals(4, loader.get(Fqn.fromString("/key3")).size()); /* Remove middle FQN first, then the others. */ doRemoveTests(Fqn.fromString("/key2")); doRemoveTests(Fqn.fromString("/key3")); doRemoveTests(Fqn.fromString("/key1")); assertNull(loader.get(Fqn.fromString("/key1"))); assertNull(loader.get(Fqn.fromString("/key2"))); assertNull(loader.get(Fqn.fromString("/key3"))); // assertEquals(0, loader.loadEntireState().length); } /** * Do basic put tests for a given FQN. */ private void doPutTests(Fqn fqn) throws Exception { assertTrue(!loader.exists(fqn)); /* put(Fqn,Object,Object) and get(Fqn,Object) */ Object oldVal; oldVal = loader.put(fqn, "one", "two"); assertNull(oldVal); addDelay(); oldVal = loader.put(fqn, "three", "four"); assertNull(oldVal); addDelay(); assertEquals("two", loader.get(fqn).get("one")); assertEquals("four", loader.get(fqn).get("three")); addDelay(); oldVal = loader.put(fqn, "one", "xxx"); assertEquals("two", oldVal); addDelay(); oldVal = loader.put(fqn, "one", "two"); assertEquals("xxx", oldVal); /* get(Fqn) */ addDelay(); Map map = new HashMap(loader.get(fqn)); assertEquals(2, map.size()); assertEquals("two", map.get("one")); assertEquals("four", map.get("three")); /* put(Fqn,Map) */ map.put("five", "six"); map.put("seven", "eight"); loader.put(fqn, map); addDelay(); assertEquals("six", loader.get(fqn).get("five")); assertEquals("eight", loader.get(fqn).get("seven")); assertEquals(map, loader.get(fqn)); assertEquals(4, map.size()); assertTrue(loader.exists(fqn)); } /** * Do basic remove tests for a given FQN. */ private void doRemoveTests(Fqn fqn) throws Exception { /* remove(Fqn,Object) */ Object oldVal; oldVal = loader.remove(fqn, "one"); assertEquals("two", oldVal); addDelay(); oldVal = loader.remove(fqn, "five"); assertEquals("six", oldVal); addDelay(); assertNull(loader.get(fqn).get("one")); assertNull(loader.get(fqn).get("five")); assertEquals("four", loader.get(fqn).get("three")); assertEquals("eight", loader.get(fqn).get("seven")); Map map = loader.get(fqn); assertEquals(2, map.size()); assertEquals("four", map.get("three")); assertEquals("eight", map.get("seven")); /* remove(Fqn) */ assertTrue(loader.exists(fqn)); loader.remove(fqn); addDelay(); map = loader.get(fqn); assertNull("Should be null", map); assertTrue(!loader.exists(fqn)); } /** * Tests creating implicit intermediate nodes when a leaf node is created, * and tests removing subtrees. */ public void testMultiLevelTree() throws Exception { /* Create top level node implicitly. */ assertTrue(!loader.exists(Fqn.fromString("/key0"))); loader.put(Fqn.fromString("/key0/level1/level2"), null); addDelay(); assertTrue(loader.exists(Fqn.fromString("/key0/level1/level2"))); assertTrue(loader.exists(Fqn.fromString("/key0/level1"))); assertTrue(loader.exists(Fqn.fromString("/key0"))); /* Remove leaf, leaving implicitly created middle level. */ loader.put(Fqn.fromString("/key0/x/y"), null); addDelay(); assertTrue(loader.exists(Fqn.fromString("/key0/x/y"))); assertTrue(loader.exists(Fqn.fromString("/key0/x"))); loader.remove(Fqn.fromString("/key0/x/y")); addDelay(); assertTrue(!loader.exists(Fqn.fromString("/key0/x/y"))); assertTrue(loader.exists(Fqn.fromString("/key0/x"))); /* Delete top level to delete everything. */ loader.remove(Fqn.fromString("/key0")); addDelay(); assertTrue(!loader.exists(Fqn.fromString("/key0"))); assertTrue(!loader.exists(Fqn.fromString("/key0/level1/level2"))); assertTrue(!loader.exists(Fqn.fromString("/key0/level1"))); assertTrue(!loader.exists(Fqn.fromString("/key0/x"))); /* Add three top level nodes as context. */ loader.put(Fqn.fromString("/key1"), null); loader.put(Fqn.fromString("/key2"), null); loader.put(Fqn.fromString("/key3"), null); addDelay(); assertTrue(loader.exists(Fqn.fromString("/key1"))); assertTrue(loader.exists(Fqn.fromString("/key2"))); assertTrue(loader.exists(Fqn.fromString("/key3"))); /* Put /key3/level1/level2. level1 should be implicitly created. */ assertTrue(!loader.exists(Fqn.fromString("/key3/level1"))); assertTrue(!loader.exists(Fqn.fromString("/key3/level1/level2"))); loader.put(Fqn.fromString("/key3/level1/level2"), null); addDelay(); assertTrue(loader.exists(Fqn.fromString("/key3/level1/level2"))); assertTrue(loader.exists(Fqn.fromString("/key3/level1"))); /* Context nodes should still be intact. */ assertTrue(loader.exists(Fqn.fromString("/key1"))); assertTrue(loader.exists(Fqn.fromString("/key2"))); assertTrue(loader.exists(Fqn.fromString("/key3"))); /* Remove middle level only. */ loader.remove(Fqn.fromString("/key3/level1")); addDelay(); assertTrue(!loader.exists(Fqn.fromString("/key3/level1/level2"))); assertTrue(!loader.exists(Fqn.fromString("/key3/level1"))); /* Context nodes should still be intact. */ assertTrue(loader.exists(Fqn.fromString("/key1"))); assertTrue(loader.exists(Fqn.fromString("/key2"))); assertTrue(loader.exists(Fqn.fromString("/key3"))); /* Delete first root, leaving other roots. */ loader.remove(Fqn.fromString("/key1")); addDelay(); assertTrue(!loader.exists(Fqn.fromString("/key1"))); assertTrue(loader.exists(Fqn.fromString("/key2"))); assertTrue(loader.exists(Fqn.fromString("/key3"))); /* Delete last root, leaving other roots. */ loader.remove(Fqn.fromString("/key3")); addDelay(); assertTrue(loader.exists(Fqn.fromString("/key2"))); assertTrue(!loader.exists(Fqn.fromString("/key3"))); /* Delete final root, leaving none. */ loader.remove(Fqn.fromString("/key2")); addDelay(); assertTrue(!loader.exists(Fqn.fromString("/key0"))); assertTrue(!loader.exists(Fqn.fromString("/key1"))); assertTrue(!loader.exists(Fqn.fromString("/key2"))); assertTrue(!loader.exists(Fqn.fromString("/key3"))); /* Repeat all tests above using put(Fqn,Object,Object) and get(Fqn) */ assertNull(loader.get(Fqn.fromString("/key0"))); loader.put(Fqn.fromString("/key0/level1/level2"), "a", "b"); addDelay(); assertNotNull(loader.get(Fqn.fromString("/key0/level1/level2"))); assertNotNull(loader.get(Fqn.fromString("/key0/level1"))); assertEquals(0, loader.get(Fqn.fromString("/key0/level1")).size()); assertEquals(0, loader.get(Fqn.fromString("/key0")).size()); loader.put(Fqn.fromString("/key0/x/y"), "a", "b"); addDelay(); assertNotNull(loader.get(Fqn.fromString("/key0/x/y"))); assertNotNull(loader.get(Fqn.fromString("/key0/x"))); assertEquals(0, loader.get(Fqn.fromString("/key0/x")).size()); loader.remove(Fqn.fromString("/key0/x/y")); addDelay(); assertNull(loader.get(Fqn.fromString("/key0/x/y"))); assertNotNull(loader.get(Fqn.fromString("/key0/x"))); assertEquals(0, loader.get(Fqn.fromString("/key0/x")).size()); loader.remove(Fqn.fromString("/key0")); addDelay(); assertNull(loader.get(Fqn.fromString("/key0"))); assertNull(loader.get(Fqn.fromString("/key0/level1/level2"))); assertNull(loader.get(Fqn.fromString("/key0/level1"))); assertNull(loader.get(Fqn.fromString("/key0/x"))); loader.put(Fqn.fromString("/key1"), "a", "b"); loader.put(Fqn.fromString("/key2"), "a", "b"); loader.put(Fqn.fromString("/key3"), "a", "b"); addDelay(); assertNotNull(loader.get(Fqn.fromString("/key1"))); assertNotNull(loader.get(Fqn.fromString("/key2"))); assertNotNull(loader.get(Fqn.fromString("/key3"))); assertNull(loader.get(Fqn.fromString("/key3/level1"))); assertNull(loader.get(Fqn.fromString("/key3/level1/level2"))); loader.put(Fqn.fromString("/key3/level1/level2"), "a", "b"); addDelay(); assertNotNull(loader.get(Fqn.fromString("/key3/level1/level2"))); assertNotNull(loader.get(Fqn.fromString("/key3/level1"))); assertEquals(0, loader.get(Fqn.fromString("/key3/level1")).size()); assertNotNull(loader.get(Fqn.fromString("/key1"))); assertNotNull(loader.get(Fqn.fromString("/key2"))); assertNotNull(loader.get(Fqn.fromString("/key3"))); loader.remove(Fqn.fromString("/key3/level1")); addDelay(); assertNull(loader.get(Fqn.fromString("/key3/level1/level2"))); assertNull(loader.get(Fqn.fromString("/key3/level1"))); assertNotNull(loader.get(Fqn.fromString("/key1"))); assertNotNull(loader.get(Fqn.fromString("/key2"))); assertNotNull(loader.get(Fqn.fromString("/key3"))); loader.remove(Fqn.fromString("/key1")); addDelay(); assertNull(loader.get(Fqn.fromString("/key1"))); assertNotNull(loader.get(Fqn.fromString("/key2"))); assertNotNull(loader.get(Fqn.fromString("/key3"))); loader.remove(Fqn.fromString("/key3")); addDelay(); assertNotNull(loader.get(Fqn.fromString("/key2"))); assertNull(loader.get(Fqn.fromString("/key3"))); loader.remove(Fqn.fromString("/key2")); addDelay(); assertNull(loader.get(Fqn.fromString("/key0"))); assertNull(loader.get(Fqn.fromString("/key1"))); assertNull(loader.get(Fqn.fromString("/key2"))); assertNull(loader.get(Fqn.fromString("/key3"))); } /** * Tests the getChildrenNames() method. */ public void testGetChildrenNames() throws Exception { checkChildren(Fqn.ROOT, null); checkChildren(Fqn.fromString("/key0"), null); loader.put(Fqn.fromString("/key0"), null); addDelay(); checkChildren(Fqn.ROOT, new String[]{"key0"}); loader.put(Fqn.fromString("/key1/x"), null); addDelay(); checkChildren(Fqn.ROOT, new String[]{"key0", "key1"}); checkChildren(Fqn.fromString("/key1"), new String[]{"x"}); loader.remove(Fqn.fromString("/key1/x")); addDelay(); checkChildren(Fqn.ROOT, new String[]{"key0", "key1"}); checkChildren(Fqn.fromString("/key0"), null); checkChildren(Fqn.fromString("/key1"), null); loader.put(Fqn.fromString("/key0/a"), null); loader.put(Fqn.fromString("/key0/ab"), null); loader.put(Fqn.fromString("/key0/abc"), null); addDelay(); checkChildren(Fqn.fromString("/key0"), new String[]{"a", "ab", "abc"}); loader.put(Fqn.fromString("/key0/xxx"), null); loader.put(Fqn.fromString("/key0/xx"), null); loader.put(Fqn.fromString("/key0/x"), null); addDelay(); checkChildren(Fqn.fromString("/key0"), new String[]{"a", "ab", "abc", "x", "xx", "xxx"}); loader.put(Fqn.fromString("/key0/a/1"), null); loader.put(Fqn.fromString("/key0/a/2"), null); loader.put(Fqn.fromString("/key0/a/2/1"), null); addDelay(); checkChildren(Fqn.fromString("/key0/a/2"), new String[]{"1"}); checkChildren(Fqn.fromString("/key0/a"), new String[]{"1", "2"}); checkChildren(Fqn.fromString("/key0"), new String[]{"a", "ab", "abc", "x", "xx", "xxx"}); // // loader.put(Fqn.fromString("/key0/\u0000"), null); // loader.put(Fqn.fromString("/key0/\u0001"), null); // checkChildren(Fqn.fromString("/key0"), // new String[] { "a", "ab", "abc", "x", "xx", "xxx", // "\u0000", "\u0001"}); // // loader.put(Fqn.fromString("/\u0001"), null); // checkChildren(Fqn.ROOT, new String[] { "key0", "key1", "\u0001" }); // // loader.put(Fqn.fromString("/\u0001/\u0001"), null); // checkChildren(Fqn.fromString("/\u0001"), new String[] { "\u0001" }); // // loader.put(Fqn.fromString("/\u0001/\uFFFF"), null); // checkChildren(Fqn.fromString("/\u0001"), // new String[] { "\u0001", "\uFFFF" }); // // loader.put(Fqn.fromString("/\u0001/\uFFFF/\u0001"), null); // checkChildren(Fqn.fromString("/\u0001/\uFFFF"), // new String[] { "\u0001" }); } /** * Checks that the given list of children part names is returned. */ private void checkChildren(Fqn fqn, String[] names) throws Exception { Set set = loader.getChildrenNames(fqn); if (names != null) { assertEquals(names.length, set.size()); for (int i = 0; i < names.length; i += 1) { assertTrue(set.contains(names[i])); } } else { assertNull(set); } } /** * Tests basic operations without a transaction. */ public void testModifications() throws Exception { doTestModifications(); } /** * Tests basic operations with a transaction. */ public void testModificationsTransactional() throws Exception { TransactionManager mgr = getTransactionManager(); mgr.begin(); doTestModifications(); mgr.commit(); } /** * Tests modifications. */ private void doTestModifications() throws Exception { /* PUT_KEY_VALUE, PUT_DATA */ List list = createUpdates(); loader.put(list); addDelay(); checkModifications(list); /* REMOVE_KEY_VALUE */ list = new ArrayList(); Modification mod = new Modification(); mod.setType(Modification.ModificationType.REMOVE_KEY_VALUE); mod.setFqn(FQN); mod.setKey("one"); list.add(mod); loader.put(list); addDelay(); checkModifications(list); /* REMOVE_NODE */ list = new ArrayList(); mod = new Modification(); mod.setType(Modification.ModificationType.REMOVE_NODE); mod.setFqn(FQN); list.add(mod); loader.put(list); addDelay(); checkModifications(list); assertNull(loader.get(FQN)); /* REMOVE_DATA */ loader.put(FQN, "one", "two"); list = new ArrayList(); mod = new Modification(); mod.setType(Modification.ModificationType.REMOVE_DATA); mod.setFqn(FQN); list.add(mod); loader.put(list); addDelay(); checkModifications(list); } /** * Tests a one-phase transaction. */ public void testOnePhaseTransaction() throws Exception { List mods = createUpdates(); loader.prepare(null, mods, true); checkModifications(mods); } /** * Tests a two-phase transaction. */ public void testTwoPhaseTransaction() throws Exception { // Object txnKey = new Object(); TransactionManager mgr = getTransactionManager(); mgr.begin(); Transaction tx = mgr.getTransaction(); List mods = createUpdates(); loader.prepare(tx, mods, false); loader.commit(tx); addDelay(); checkModifications(mods); mgr.commit(); } /** * Tests rollback of a two-phase transaction. */ public void testTransactionRollback() throws Exception { loader.remove(Fqn.fromString("/")); int num; try { ByteArrayOutputStream baos = new ByteArrayOutputStream(1024); MarshalledValueOutputStream os = new MarshalledValueOutputStream(baos); loader.loadEntireState(os); num = baos.size(); } catch (UnsupportedOperationException ex) { log.info("caught unsupported operation exception that's okay: ", ex); return; } Object txnKey = new Object(); List mods = createUpdates(); loader.prepare(txnKey, mods, false); loader.rollback(txnKey); ByteArrayOutputStream baos = new ByteArrayOutputStream(1024); MarshalledValueOutputStream os = new MarshalledValueOutputStream(baos); loader.loadEntireState(os); assertEquals(num, baos.size()); } /** * Tests rollback of a two-phase transaction that is mediated by the cache. */ public void testIntegratedTransactionRollback() throws Exception { loader.remove(Fqn.fromString("/")); int num = 0; try { ByteArrayOutputStream baos = new ByteArrayOutputStream(1024); MarshalledValueOutputStream os = new MarshalledValueOutputStream(baos); loader.loadEntireState(os); num = baos.size(); } catch (UnsupportedOperationException ex) { System.out.println("caught unsupported operation exception that's okay: " + ex); return; } Object txnKey = new Object(); List mods = createUpdates(); loader.prepare(txnKey, mods, false); loader.rollback(txnKey); ByteArrayOutputStream baos = new ByteArrayOutputStream(1024); MarshalledValueOutputStream os = new MarshalledValueOutputStream(baos); loader.loadEntireState(os); assertEquals(num, baos.size()); } /** * Creates a set of update (PUT_KEY_VALUE, PUT_DATA) modifications. */ private List createUpdates() { List list = new ArrayList(); Modification mod = new Modification(); mod.setType(Modification.ModificationType.PUT_KEY_VALUE); mod.setFqn(FQN); mod.setKey("one"); mod.setValue("two"); list.add(mod); mod = new Modification(); mod.setType(Modification.ModificationType.PUT_KEY_VALUE); mod.setFqn(FQN); mod.setKey("three"); mod.setValue("four"); list.add(mod); Map map = new HashMap(); map.put("five", "six"); map.put("seven", "eight"); mod = new Modification(); mod.setType(Modification.ModificationType.PUT_DATA); mod.setFqn(FQN); mod.setData(map); list.add(mod); return list; } /** * Checks that a list of modifications was applied. */ private void checkModifications(List list) throws Exception { for (Modification mod : list) { Fqn fqn = mod.getFqn(); switch (mod.getType()) { case PUT_KEY_VALUE: assertEquals(mod.getValue(), loader.get(fqn).get(mod.getKey())); break; case PUT_DATA: Map map = mod.getData(); for (Object key : map.keySet()) { assertEquals(map.get(key), loader.get(fqn).get(key)); } break; case REMOVE_KEY_VALUE: assertNull(loader.get(fqn).get(mod.getKey())); break; case REMOVE_DATA: map = loader.get(fqn); assertNotNull(map); assertEquals(0, map.size()); break; case REMOVE_NODE: assertNull(loader.get(fqn)); break; default: fail("unknown type: " + mod); break; } } } /** * Tests that null keys and values work as for a standard Java Map. */ public void testNullKeysAndValues() throws Exception { loader.put(FQN, null, "x"); addDelay(); assertEquals("x", loader.get(FQN).get(null)); Map map = loader.get(FQN); assertEquals(1, map.size()); assertEquals("x", map.get(null)); loader.put(FQN, "y", null); addDelay(); assertNull(loader.get(FQN).get("y")); map = loader.get(FQN); assertEquals(2, map.size()); assertEquals("x", map.get(null)); assertNull(map.get("y")); loader.remove(FQN, null); addDelay(); assertNull(loader.get(FQN).get(null)); assertEquals(1, loader.get(FQN).size()); loader.remove(FQN, "y"); addDelay(); assertNotNull(loader.get(FQN)); assertEquals(0, loader.get(FQN).size()); map = new HashMap(); map.put(null, null); loader.put(FQN, map); addDelay(); Map m = loader.get(FQN); m.toString(); //throw new RuntimeException("MAP " + loader.get(FQN).getClass()); /* assertEquals(map, loader.get(FQN)); loader.remove(FQN); addDelay(); assertNull(loader.get(FQN)); map = new HashMap(); map.put("xyz", null); map.put(null, "abc"); loader.put(FQN, map); addDelay(); assertEquals(map, loader.get(FQN)); loader.remove(FQN); addDelay(); assertNull(loader.get(FQN))*/ } /** * Test non-default database name. */ public void testDatabaseName() throws Exception { loader.put(FQN, "one", "two"); addDelay(); assertEquals("two", loader.get(FQN).get("one")); } /** * Test load/store state. */ public void testLoadAndStore() throws Exception { // Empty state loader.remove(Fqn.fromString("/")); // Use a complex object to ensure that the class catalog is used. Complex c1 = new Complex(); Complex c2 = new Complex(c1); // Add objects loader.put(FQN, 1, c1); loader.put(FQN, 2, c2); addDelay(); assertEquals(c1, loader.get(FQN).get(1)); assertEquals(c2, loader.get(FQN).get(2)); assertEquals(2, loader.get(FQN).size()); // Save state byte[] state; ByteArrayOutputStream baos = new ByteArrayOutputStream(1024); MarshalledValueOutputStream os = new MarshalledValueOutputStream(baos); try { loader.loadEntireState(os); } catch (UnsupportedOperationException ex) { System.out.println("caught unsupported operation exception (this is expected): " + ex); } finally { cache.getMarshaller().objectToObjectStream(StateTransferManager.STREAMING_DELIMITER_NODE, os); os.close(); assertTrue(baos.size() > 0); state = baos.toByteArray(); } /* Restore state. */ ByteArrayInputStream bais = new ByteArrayInputStream(state); MarshalledValueInputStream is = new MarshalledValueInputStream(bais); try { loader.storeEntireState(is); } catch (UnsupportedOperationException ex) { System.out.println("caught unsupported operation exception (this is expected): " + ex); } finally { is.close(); } addDelay(); assertEquals(c1, loader.get(FQN).get(1)); assertEquals(c2, loader.get(FQN).get(2)); assertEquals(2, loader.get(FQN).size()); } /** * Complex object whose class description is stored in the class catalog. */ public static class Complex implements Serializable { /** * The serialVersionUID */ private static final long serialVersionUID = -6810871775584708565L; Complex nested; Complex() { this(null); } Complex(Complex nested) { this.nested = nested; } public boolean equals(Object o) { if (!(o instanceof Complex)) { return false; } Complex x = (Complex) o; return (nested != null) ? nested.equals(x.nested) : (x.nested == null); } public int hashCode() { if (nested == null) { return super.hashCode(); } else { return 13 + nested.hashCode(); } } } public void testRemoveInTransactionCommit() throws Exception { Fqn fqn = Fqn.fromString("/a/b"); loader.remove(fqn); String key = "key"; String value = "value"; cache.put(fqn, key, value); loader = cache.getCacheLoaderManager().getCacheLoader(); assertEquals(value, cache.get(fqn, key)); assertEquals(value, loader.get(fqn).get(key)); cache.getTransactionManager().begin(); cache.removeNode(fqn); cache.getNode(fqn);// forces the node to be loaded from cache loader again cache.getTransactionManager().commit(); log.debug("expect the cache and the loader to be null here."); assertEquals(null, cache.get(fqn, key)); assertEquals(null, loader.get(fqn)); } public void testRemoveInTransactionRollback() throws Exception { Fqn fqn = Fqn.fromString("/a/b"); loader.remove(fqn); String key = "key"; String value = "value"; cache.put(fqn, key, value); loader = cache.getCacheLoaderManager().getCacheLoader(); assertEquals(value, cache.get(fqn, key)); assertEquals(value, loader.get(fqn).get(key)); cache.getTransactionManager().begin(); cache.removeNode(fqn); cache.getNode(fqn);// forces a reload from cloader cache.getTransactionManager().rollback(); assertEquals(value, cache.get(fqn, key)); assertEquals(value, loader.get(fqn).get(key)); } /** * See http://jira.jboss.com/jira/browse/JBCACHE-352 */ public void testRemoveAndGetInTransaction() throws Exception { Fqn fqn = Fqn.fromString("/a/b"); String key = "key"; String value = "value"; cache.put(fqn, key, value); CacheLoader loader = cache.getCacheLoaderManager().getCacheLoader(); assertEquals(value, cache.get(fqn, key)); assertEquals(value, loader.get(fqn).get(key)); cache.getTransactionManager().begin(); cache.removeNode(fqn); assertNull("Expecting a null since I have already called a remove() - see JBCACHE-352", cache.get(fqn, key)); cache.getTransactionManager().rollback(); assertEquals(value, cache.get(fqn, key)); assertEquals(value, loader.get(fqn).get(key)); } /** * Test load/store state. */ public void testPartialLoadAndStore() throws Exception { /* Empty state. */ loader.remove(Fqn.fromString("/")); // assertEquals(0, loader.loadEntireState().length); // loader.storeEntireState(new byte[0]); // assertEquals(0, loader.loadEntireState().length); // loader.storeEntireState(null); // assertEquals(0, loader.loadEntireState().length); // assertEquals(null, loader.get(FQN)); /* Use a complex object to ensure that the class catalog is used. */ Complex c1 = new Complex(); Complex c2 = new Complex(c1); Complex c3 = new Complex(); Complex c4 = new Complex(c3); /* Add objects. */ loader.put(FQN, 1, c1); loader.put(FQN, 2, c2); loader.put(SUBTREE_FQN, 1, c3); loader.put(SUBTREE_FQN, 2, c4); addDelay(); assertEquals(c1, loader.get(FQN).get(1)); assertEquals(c2, loader.get(FQN).get(2)); assertEquals(2, loader.get(FQN).size()); assertEquals(c3, loader.get(SUBTREE_FQN).get(1)); assertEquals(c4, loader.get(SUBTREE_FQN).get(2)); assertEquals(2, loader.get(SUBTREE_FQN).size()); /* Save state. */ ByteArrayOutputStream baos = new ByteArrayOutputStream(1024); MarshalledValueOutputStream os = new MarshalledValueOutputStream(baos); loader.loadState(SUBTREE_FQN, os); cache.getMarshaller().objectToObjectStream(StateTransferManager.STREAMING_DELIMITER_NODE, os); os.close(); assertTrue(baos.size() > 0); loader.remove(SUBTREE_FQN); /* Restore state. */ ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); MarshalledValueInputStream is = new MarshalledValueInputStream(bais); loader.storeState(SUBTREE_FQN, is); is.close(); addDelay(); assertEquals(c1, loader.get(FQN).get(1)); assertEquals(c2, loader.get(FQN).get(2)); assertEquals(2, loader.get(FQN).size()); assertEquals(c3, loader.get(SUBTREE_FQN).get(1)); assertEquals(c4, loader.get(SUBTREE_FQN).get(2)); assertEquals(2, loader.get(SUBTREE_FQN).size()); } public void testBuddyBackupStore() throws Exception { /* Empty state. */ loader.remove(Fqn.ROOT); /* Use a complex object to ensure that the class catalog is used. */ Complex c1 = new Complex(); Complex c2 = new Complex(c1); Complex c3 = new Complex(); Complex c4 = new Complex(c3); /* Add objects. */ loader.put(FQN, 1, c1); loader.put(FQN, 2, c2); loader.put(SUBTREE_FQN, 1, c3); loader.put(SUBTREE_FQN, 2, c4); addDelay(); assertEquals(c1, loader.get(FQN).get(1)); assertEquals(c2, loader.get(FQN).get(2)); assertEquals(2, loader.get(FQN).size()); assertEquals(c3, loader.get(SUBTREE_FQN).get(1)); assertEquals(c4, loader.get(SUBTREE_FQN).get(2)); assertEquals(2, loader.get(SUBTREE_FQN).size()); /* Save state. */ ByteArrayOutputStream baos = new ByteArrayOutputStream(1024); MarshalledValueOutputStream os = new MarshalledValueOutputStream(baos); loader.loadState(FQN, os); cache.getMarshaller().objectToObjectStream(StateTransferManager.STREAMING_DELIMITER_NODE, os); os.close(); assertTrue(baos.size() > 0); /* Restore state. */ ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); MarshalledValueInputStream is = new MarshalledValueInputStream(bais); loader.storeState(BUDDY_BASE, is); is.close(); addDelay(); assertEquals(c1, loader.get(BUDDY_PLUS_FQN).get(1)); assertEquals(c2, loader.get(BUDDY_PLUS_FQN).get(2)); assertEquals(2, loader.get(BUDDY_PLUS_FQN).size()); assertEquals(c3, loader.get(BUDDY_PLUS_SUBTREE_FQN).get(1)); assertEquals(c4, loader.get(BUDDY_PLUS_SUBTREE_FQN).get(2)); assertEquals(2, loader.get(BUDDY_PLUS_SUBTREE_FQN).size()); } public void testIgnoreModifications() throws Exception { Fqn fqn = Fqn.fromString("/a"); cache.put(fqn, "k", "v"); assert "v".equals(cache.get(fqn, "k")); assert "v".equals(loader.get(fqn).get("k")); // Some loaders cannot deal with being called when outside of a running cache. assert "v".equals(loader.get(fqn).get("k")); // now stop the cache cache.stop(); cache.destroy(); assert cache.getCacheLoaderManager() == null : "Should nullify cache loader manager in the cache"; cache.getConfiguration().getCacheLoaderConfig().getIndividualCacheLoaderConfigs().get(0).setIgnoreModifications(true); cache.start(); loader = cache.getCacheLoaderManager().getCacheLoader(); postConfigure(); // CCL uses it's own mechanisms to ensure read-only behaviour if (!(loader instanceof ChainingCacheLoader)) { // test that the cache loader is wrapped by a read-only delegate assert loader instanceof ReadOnlyDelegatingCacheLoader; } // old state should be persisted. assert "v".equals(loader.get(fqn).get("k")); assert "v".equals(cache.get(fqn, "k")); // the loader should now be read-only cache.put(fqn, "k", "v2"); assert "v2".equals(cache.get(fqn, "k")); assert "v".equals(loader.get(fqn).get("k")); } public void testCacheLoaderThreadSafety() throws Exception { threadSafetyTest(true); } public void testCacheLoaderThreadSafetyMultipleFqns() throws Exception { threadSafetyTest(false); } protected void threadSafetyTest(final boolean singleFqn) throws Exception { final CountDownLatch latch = new CountDownLatch(1); final Fqn fqn = Fqn.fromString("/a/b/c"); final List fqns = new ArrayList(30); final Random r = new Random(); if (!singleFqn) { for (int i = 0; i < 30; i++) { Fqn f = Fqn.fromString("/a/b/c/" + i); fqns.add(f); loader.put(f, "k", "v"); } } else { loader.put(fqn, "k", "v"); } final int loops = 1000; final Set exceptions = new CopyOnWriteArraySet(); Thread remover1 = new Thread("Remover-1") { public void run() { try { latch.await(); for (int i = 0; i < loops; i++) { loader.remove(singleFqn ? fqn : fqns.get(r.nextInt(fqns.size()))); } } catch (Exception e) { exceptions.add(e); } } }; remover1.start(); Thread remover2 = new Thread("Remover-2") { public void run() { try { latch.await(); for (int i = 0; i < loops; i++) { loader.remove(singleFqn ? fqn : fqns.get(r.nextInt(fqns.size())), "k"); } } catch (Exception e) { exceptions.add(e); } } }; remover2.start(); Thread reader1 = new Thread("Reader-1") { public void run() { try { latch.await(); for (int i = 0; i < loops; i++) { loader.get(singleFqn ? fqn : fqns.get(r.nextInt(fqns.size()))); } } catch (Exception e) { exceptions.add(e); } } }; reader1.start(); Thread reader2 = new Thread("Reader-2") { public void run() { try { latch.await(); for (int i = 0; i < loops; i++) { loader.getChildrenNames(singleFqn ? fqn : fqns.get(r.nextInt(fqns.size()))); } } catch (Exception e) { exceptions.add(e); } } }; reader2.start(); Thread writer1 = new Thread("Writer-1") { public void run() { try { latch.await(); for (int i = 0; i < loops; i++) { loader.put(singleFqn ? fqn : fqns.get(r.nextInt(fqns.size())), "k", "v"); } } catch (Exception e) { exceptions.add(e); } } }; writer1.start(); Thread writer2 = new Thread("Writer-2") { public void run() { try { latch.await(); for (int i = 0; i < loops; i++) { loader.put(singleFqn ? fqn : fqns.get(r.nextInt(fqns.size())), new HashMap()); } } catch (Exception e) { exceptions.add(e); } } }; writer2.start(); latch.countDown(); reader1.join(); reader2.join(); remover1.join(); remover2.join(); writer1.join(); writer2.join(); for (Exception e : exceptions) throw e; } protected TransactionManager getTransactionManager() { return cache.getConfiguration().getRuntimeConfig().getTransactionManager(); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/loader/S3CacheLoaderTest.java0000644000175000017500000000540510770417262027757 0ustar twernertwernerpackage org.jboss.cache.loader; import static org.testng.AssertJUnit.assertNotNull; import java.io.IOException; import java.util.Properties; import net.noderunner.amazon.s3.emulator.Server; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.Fqn; import org.jboss.cache.config.CacheLoaderConfig; import org.testng.annotations.Test; /** * Tests {@link org.jboss.cache.loader.s3.S3CacheLoader}. * * This requires a S3 account to truly test; uses an emulator otherwise. * * @author Elias Ross * @version $Id: JdbmCacheLoaderTest.java 4561 2007-10-08 14:02:02Z manik.surtani@jboss.com $ */ @Test(groups = {"functional"}, enabled = true) public class S3CacheLoaderTest extends CacheLoaderTestsBase { private static final Log log = LogFactory.getLog(S3CacheLoaderTest.class); private Server server; @Override protected void configureCache() throws Exception { String accessKey = System.getProperty("accessKey"); String properties; if (accessKey == null) { log.info("Testing using S3CacheLoader using emulator"); server = new Server(); server.start(); properties = "cache.s3.accessKeyId=dummy\n" + "cache.s3.secretAccessKey=dummy\n" + "cache.s3.server=localhost\n" + "cache.s3.port=" + server.getPort() + "\n" + "cache.s3.callingFormat=VANITY" + "\n" + "cache.s3.bucket=localhost" + "\n"; } else { properties = "cache.s3.accessKeyId=" + accessKey + "\n" + "cache.s3.secretAccessKey=" + System.getProperty("secretKey") + "\n" ; } CacheLoaderConfig config = getSingleCacheLoaderConfig("", "org.jboss.cache.loader.s3.S3CacheLoader", properties, false, true, false); // System.out.println(config); Properties p = config.getFirstCacheLoaderConfig().getProperties(); // System.out.println(p); assertNotNull(p.get("cache.s3.accessKeyId")); assertNotNull(p.get("cache.s3.secretAccessKey")); cache.getConfiguration().setCacheLoaderConfig(config); } @Override public void cleanup() { if (server != null) { try { server.close(); } catch (IOException e) { } } } protected void postConfigure() { cache.removeNode(Fqn.root()); } //@Override public void testCacheLoaderThreadSafety() { } //@Override public void testPartialLoadAndStore() { // do nothing } //@Override public void testBuddyBackupStore() { // do nothing } //@Override protected void threadSafetyTest(final boolean singleFqn) throws Exception { } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/loader/C3p0JDBCCacheLoaderTest.java0000644000175000017500000000276210665031725030624 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.loader; import java.util.Properties; /** * Unit test that runs the the tests defined JDBCCacheLoaderTest using a standalone * connection pool factory based on c3p0 library. * * @author Galder Zamarreno */ public class C3p0JDBCCacheLoaderTest extends JDBCCacheLoaderTest { private static final String CF_CLASS = "org.jboss.cache.loader.C3p0ConnectionFactory"; protected void configureCache() throws Exception { Properties prop = getProperties(); String props = "cache.jdbc.driver =" + prop.getProperty("cache.jdbc.driver") + "\n" + "cache.jdbc.url=" + prop.getProperty("cache.jdbc.url") + "\n" + "cache.jdbc.user=" + prop.getProperty("cache.jdbc.user") + "\n" + "cache.jdbc.password=" + prop.getProperty("cache.jdbc.password") + "\n" + "cache.jdbc.node.type=" + prop.getProperty("cache.jdbc.node.type") + "\n" + "cache.jdbc.sql-concat=" + prop.getProperty("cache.jdbc.sql-concat") + "\n" + "cache.jdbc.connection.factory=" + CF_CLASS; cache.getConfiguration().setCacheLoaderConfig(getSingleCacheLoaderConfig("", "org.jboss.cache.loader.JDBCCacheLoader", props, false, true, false)); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/loader/TcpCacheServerTest.java0000644000175000017500000001367511017455042030261 0ustar twernertwernerpackage org.jboss.cache.loader; import org.jboss.cache.Cache; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.UnitTestCacheConfigurationFactory; import org.jboss.cache.factories.XmlConfigurationParser; import org.jboss.cache.jmx.CacheJmxWrapper; import org.jboss.cache.loader.tcp.TcpCacheServer; import org.jboss.cache.xml.XmlHelper; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.Test; import org.w3c.dom.Element; import java.net.UnknownHostException; /** * Tests various ways of setting up the TcpCacheServer * * @author Brian Stansberry * @version $Id: TcpCacheServerTest.java 5906 2008-05-29 07:24:18Z mircea.markus $ */ @Test(groups = {"functional"}) public class TcpCacheServerTest { static TcpCacheServer cache_server = null; private CacheSPI cache; private CacheLoader loader; static { Runtime.getRuntime().addShutdownHook(new Thread() { public void run() { if (cache_server != null) { System.out.println("Stopping TcpCacheServer"); cache_server.stop(); } } }); } private void createCacheAndLoader() throws Exception { Configuration c = new Configuration(); c.setCacheMode(Configuration.CacheMode.LOCAL); c.setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); c.setCacheLoaderConfig(getCacheLoaderConfig()); cache = (CacheSPI) new DefaultCacheFactory().createCache(c); cache.start(); loader = cache.getCacheLoaderManager().getCacheLoader(); } protected CacheLoaderConfig getCacheLoaderConfig() throws Exception { String xml = "\n" + "false\n" + "\n" + "\n" + "org.jboss.cache.loader.TcpDelegatingCacheLoader\n" + "host=127.0.0.1\nport=12121\n" + "false\n" + "true\n" + "true\n" + "false\n" + "\n" + ""; Element element = XmlHelper.stringToElement(xml); return XmlConfigurationParser.parseCacheLoaderConfig(element); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { if (cache != null) { cache.stop(); cache.destroy(); cache = null; } if (cache_server != null) { System.out.println("Stopping TcpCacheServer"); cache_server.stop(); cache_server = null; } } private static void createTcpCacheServer() throws UnknownHostException { cache_server = new TcpCacheServer(); cache_server.setBindAddress("127.0.0.1"); cache_server.setPort(12121); } private void startTcpCacheServer() { Thread runner = new Thread() { public void run() { try { System.out.println("Starting TcpCacheServer"); cache_server.create(); cache_server.start(); } catch (Exception ex) { ex.printStackTrace(); } } }; runner.setDaemon(true); runner.start(); // give the tcp cache server time to start up TestingUtil.sleepThread(2000); } public void testInjectConfigFilePath() throws Exception { createTcpCacheServer(); Configuration conf = UnitTestCacheConfigurationFactory.createConfiguration(Configuration.CacheMode.LOCAL, true); CacheSPI cacheSPI = (CacheSPI) new DefaultCacheFactory().createCache(conf); cache_server.setCache(cacheSPI); startTcpCacheServer(); createCacheAndLoader(); cacheCheck(); usabilityCheck(); } public void testInjectCache() throws Exception { createTcpCacheServer(); Configuration conf = UnitTestCacheConfigurationFactory.createConfiguration(Configuration.CacheMode.LOCAL, true); CacheSPI cacheSPI = (CacheSPI) new DefaultCacheFactory().createCache(conf); cache_server.setCache(cacheSPI); startTcpCacheServer(); createCacheAndLoader(); cacheCheck(); usabilityCheck(); } public void testInjectCacheJmxWrapper() throws Exception { createTcpCacheServer(); Configuration conf = UnitTestCacheConfigurationFactory.createConfiguration(Configuration.CacheMode.LOCAL, true); CacheSPI cacheSPI = (CacheSPI) new DefaultCacheFactory().createCache(conf); CacheJmxWrapper wrapper = new CacheJmxWrapper(cacheSPI); wrapper.start(); cache_server.setCacheJmxWrapper(wrapper); startTcpCacheServer(); createCacheAndLoader(); cacheCheck(); usabilityCheck(); } private void cacheCheck() { Cache c = cache_server.getCache(); assertNotNull("Cache exists", c); Configuration config = c.getConfiguration(); // check a couple properties assertEquals("Correct mode", Configuration.CacheMode.LOCAL, config.getCacheMode()); assertEquals("Correct cluster name", "JBossCache-Cluster", config.getClusterName()); } private void usabilityCheck() throws Exception { Fqn fqn = Fqn.fromString("/key"); assertFalse("Fqn does not exist in loader", loader.exists(fqn)); /* put(Fqn,Object,Object) and get(Fqn,Object) */ Object oldVal; oldVal = loader.put(fqn, "one", "two"); assertNull("oldVal is null", oldVal); assertEquals("Got value from cache", "two", cache.get(fqn, "one")); } }jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/loader/C3p0ConnectionFactoryTest.java0000644000175000017500000000670610701373076031477 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.loader; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.fail; import java.sql.Connection; import java.util.Properties; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import com.mchange.v2.c3p0.PooledDataSource; /** * Unit test for C3p0ConnectionFactory * * @author Galder Zamarreno */ @Test(groups = {"functional"}) public class C3p0ConnectionFactoryTest { private C3p0ConnectionFactory cf; private AdjListJDBCCacheLoaderConfig config; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { Properties prop = load("cache-jdbc.properties"); config = new AdjListJDBCCacheLoaderConfig(); config.setProperties(prop); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { cf.stop(); } public void testSetConfig() throws Exception { config.getProperties().setProperty("c3p0.checkoutTimeout", "10000"); /* We set the maxPoolSize in two different ways. First, via a System property and secondly, emulating XML configuration, as it maxPoolSize had been set in the cache loader properties. The system property should be the one used. Further explanation in C3p0ConnectionFactory */ System.setProperty("c3p0.maxPoolSize", "5"); config.getProperties().setProperty("c3p0.maxPoolSize", "3"); cf = new C3p0ConnectionFactory(); cf.setConfig(config); cf.start(); Connection c1 = cf.getConnection(); Connection c2 = cf.getConnection(); Connection c3 = cf.getConnection(); Connection c4 = cf.getConnection(); Connection c5 = cf.getConnection(); Connection c6 = null; try { c6 = cf.getConnection(); fail("Should have produced an SQLException indicating that it timed out checking out a Connection"); } catch (Exception good) { } finally { cf.close(c1); cf.close(c2); cf.close(c3); cf.close(c4); cf.close(c5); cf.close(c6); } } public void testGetConnection() throws Exception { cf = new C3p0ConnectionFactory(); cf.setConfig(config); cf.start(); PooledDataSource internalDs = (PooledDataSource) cf.getDataSource(); Connection c1 = cf.getConnection(); Connection c2 = cf.getConnection(); assertEquals("There should be two connections checked out", 2, internalDs.getNumBusyConnectionsDefaultUser()); cf.close(c1); Thread.sleep(100); assertEquals("There should be one connection checked out", 1, internalDs.getNumBusyConnectionsDefaultUser()); cf.close(c2); Thread.sleep(100); assertEquals("There should be no connections checked out", 0, internalDs.getNumBusyConnectionsDefaultUser()); } private Properties load(String resource) throws Exception { Properties prop = new Properties(); try { prop.load(this.getClass().getClassLoader().getResourceAsStream(resource)); return prop; } catch (Exception e) { log("Error loading jdbc properties "); throw (e); } } private void log(Object o) { System.out.println(o); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/loader/JDBCCacheLoaderDerbyDSTest.java0000644000175000017500000001075611031017265031404 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.loader; import org.apache.derby.jdbc.EmbeddedXADataSource; import org.jboss.cache.Fqn; import org.jboss.cache.transaction.DummyTransactionManager; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.Test; import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.NameNotFoundException; import java.util.Properties; /** * This test runs cache loader tests using Database as the cache loader store. * The default test is configured using MySQL. * The server and database configuration is read from a properties file located at * /etc/cache-jdbc.properties. *

* The appropriate JDBC driver (i.e mysql-connector-java-3.0.10-stable-bin.jar) * must be in the lib directory for this test to run successfuly * * @author Hany Mesha * @version $Revision: 6072 $ */ @Test(groups = {"functional"}) public class JDBCCacheLoaderDerbyDSTest extends CacheLoaderTestsBase { private String old_factory = null; private final String FACTORY = "org.jboss.cache.transaction.DummyContextFactory"; private final String JNDI_NAME = "java:/DerbyDS"; protected void configureCache() throws Exception { old_factory = System.getProperty(Context.INITIAL_CONTEXT_FACTORY); System.setProperty(Context.INITIAL_CONTEXT_FACTORY, FACTORY); DummyTransactionManager.getInstance(); Context context = new InitialContext(); try { Object obj = context.lookup(JNDI_NAME); assertNull(JNDI_NAME + " not bound", obj); } catch (NameNotFoundException n) { // expected } Properties prop = new Properties(); try { prop.load(this.getClass().getClassLoader().getResourceAsStream("cache-jdbc.properties")); } catch (Exception e) { System.out.println("Error loading jdbc properties "); } //MysqlDataSource ds = new MysqlDataSource(); EmbeddedXADataSource ds = new EmbeddedXADataSource(); ds.setDatabaseName("jbossdb"); ds.setCreateDatabase("create"); ds.setUser(prop.getProperty("cache.jdbc.user")); ds.setPassword(prop.getProperty("cache.jdbc.password")); String props = "cache.jdbc.datasource =" + JNDI_NAME + "\n" + "cache.jdbc.node.type=" + prop.getProperty("cache.jdbc.node.type") + "\n" + "cache.jdbc.sql-concat= 1 || 2"; cache.getConfiguration().setCacheLoaderConfig(getSingleCacheLoaderConfig("", "org.jboss.cache.loader.JDBCCacheLoader", props, false, true, false)); cache.create(); context.bind(JNDI_NAME, ds); assertNotNull(JNDI_NAME + " bound", context.lookup(JNDI_NAME)); } public void testLargeObject() { try { String key = "LargeObj"; // create an object with size bigger than 4k (k=1024 bytes) StringBuilder text = new StringBuilder("LargeObject"); while (text.toString().getBytes().length < (1024 * 100)) { text.append(text); } String initialValue = text.toString(); // insert it into the cache loader loader.remove(Fqn.fromString("/")); Object retVal = loader.put(FQN, key, initialValue); assertNull(retVal); addDelay(); // load the object from the cache loader and validate it assertEquals(initialValue, (String) loader.get(FQN).get(key)); // update the object and validate it String updatedValue = initialValue.concat(("UpdatedValue")); retVal = loader.put(FQN, key, updatedValue); assertEquals(initialValue, (String) retVal); assertEquals(updatedValue, (String) loader.get(FQN).get(key)); } catch (Exception e) { fail(e.toString()); } } public void testTransactionRollback() throws Exception { // no-op } public void testIntegratedTransactionRollback() throws Exception { // no-op } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { super.tearDown(); Context ctx = new InitialContext(); ctx.unbind(JNDI_NAME); if (old_factory != null) { System.setProperty(Context.INITIAL_CONTEXT_FACTORY, old_factory); } else { System.getProperties().remove(Context.INITIAL_CONTEXT_FACTORY); } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/loader/FileCacheLoaderConfigTest.java0000644000175000017500000000271610665031725031500 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.loader; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertFalse; import static org.testng.AssertJUnit.assertNull; import static org.testng.AssertJUnit.assertTrue; import java.util.Properties; import org.testng.annotations.Test; /** * Unit test class for FileCacheLoaderConfig * * @author Galder Zamarreno */ @Test(groups={"functional"}) public class FileCacheLoaderConfigTest { private FileCacheLoaderConfig fclc = new FileCacheLoaderConfig(); public void testSetProperties() { fclc.setProperties((Properties)null); assertTrue(fclc.isCheckCharacterPortability()); assertNull(fclc.getLocation()); Properties p = new Properties(); p.setProperty("location", "any"); fclc.setProperties(p); assertTrue(fclc.isCheckCharacterPortability()); assertEquals("any", fclc.getLocation()); p.clear(); p.setProperty("check.character.portability", "true"); fclc.setProperties(p); assertTrue(fclc.isCheckCharacterPortability()); assertNull(fclc.getLocation()); p.clear(); p.setProperty("check.character.portability", "false"); fclc.setProperties(p); assertFalse(fclc.isCheckCharacterPortability()); assertNull(fclc.getLocation()); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/loader/CacheLoaderPurgingTest.java0000644000175000017500000001142011001142604031056 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.loader; import org.jboss.cache.CacheException; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.XmlConfigurationParser; import org.jboss.cache.xml.XmlHelper; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.Test; import org.w3c.dom.Element; /** * @author Manik Surtani (manik@jboss.org) */ @Test(groups = {"functional"}) public class CacheLoaderPurgingTest extends AbstractCacheLoaderTestBase { private CacheSPI cache; private String key = "key", value = "value"; private Fqn fqn = Fqn.fromString("/a/b/c"); @AfterMethod(alwaysRun = true) public void tearDown() throws CacheException { if (cache != null) { cache.removeNode(Fqn.ROOT); cache.stop(); cache = null; } } public void testSingleLoaderNoPurge() throws Exception { cache = (CacheSPI) new DefaultCacheFactory().createCache(false); Configuration c = cache.getConfiguration(); c.setCacheLoaderConfig(getSingleCacheLoaderConfig("", DummySharedInMemoryCacheLoader.class.getName(), "", false, false, false)); cache.start(); cache.put(fqn, key, value); CacheLoader loader = cache.getCacheLoaderManager().getCacheLoader(); assertEquals(value, cache.get(fqn, key)); assertEquals(value, loader.get(fqn).get(key)); cache.evict(fqn); cache.stop(); assertEquals(value, loader.get(fqn).get(key)); cache.start(); assertEquals(value, cache.get(fqn, key)); assertEquals(value, loader.get(fqn).get(key)); } public void testSingleLoaderPurge() throws Exception { cache = (CacheSPI) new DefaultCacheFactory().createCache(false); Configuration c = cache.getConfiguration(); c.setCacheLoaderConfig(getSingleCacheLoaderConfig("", DummySharedInMemoryCacheLoader.class.getName(), "", false, false, false, true)); cache.start(); cache.put(fqn, key, value); CacheLoader loader = cache.getCacheLoaderManager().getCacheLoader(); assertEquals(value, cache.get(fqn, key)); assertEquals(value, loader.get(fqn).get(key)); cache.evict(fqn); cache.stop(); assertEquals(value, loader.get(fqn).get(key)); cache.start(); assertTrue(cache.getCacheLoaderManager().getCacheLoaderConfig().getFirstCacheLoaderConfig().isPurgeOnStartup()); assertNull(cache.getNode(fqn)); assertNull(loader.get(fqn)); } public void testTwoLoadersPurge() throws Exception { cache = (CacheSPI) new DefaultCacheFactory().createCache(false); String xml = "\n" + "false\n" + "\n" + "\n" + "" + DummySharedInMemoryCacheLoader.class.getName() + "\n" + "" + " bin=bin1\n" + "\n" + "false\n" + "true\n" + "" + true + "\n" + "\n" + "\n" + "" + DummySharedInMemoryCacheLoader.class.getName() + "\n" + "" + " bin=bin2\n" + "\n" + "false\n" + "false\n" + "" + false + "\n" + "\n" + ""; Configuration c = cache.getConfiguration(); Element element = XmlHelper.stringToElement(xml); c.setCacheLoaderConfig(XmlConfigurationParser.parseCacheLoaderConfig(element)); cache.start(); cache.put(fqn, key, value); CacheLoader loader[] = ((ChainingCacheLoader) cache.getCacheLoaderManager().getCacheLoader()).getCacheLoaders().toArray(new CacheLoader[]{}); assertEquals(value, cache.get(fqn, key)); assertEquals(value, loader[0].get(fqn).get(key)); assertEquals(value, loader[1].get(fqn).get(key)); cache.evict(fqn); cache.stop(); assertEquals(value, loader[0].get(fqn).get(key)); assertEquals(value, loader[1].get(fqn).get(key)); cache.start(); assertTrue(!cache.exists(fqn)); assertNull(loader[0].get(fqn)); assertNotNull(loader[1].get(fqn)); assertEquals(value, cache.get(fqn, key)); } } ././@LongLink0000000000000000000000000000015300000000000011564 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/loader/AdjListJDBCCacheLoaderCompatibilityTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/loader/AdjListJDBCCacheLoaderCompatibilityTes0000644000175000017500000002674511017455042033100 0ustar twernertwernerpackage org.jboss.cache.loader; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.factories.XmlConfigurationParser; import org.jboss.cache.marshall.NodeData; import org.jboss.cache.statetransfer.StateTransferManager; import org.jboss.cache.xml.XmlHelper; import org.jboss.util.stream.MarshalledValueInputStream; import org.jboss.util.stream.MarshalledValueOutputStream; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import org.w3c.dom.Element; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.util.ArrayList; import java.util.HashSet; import java.util.Properties; /** * Tests the compatibility between JDBCCacheLoader and JDBCCacheLoaderOld. More exactly, * it tests whether the new JDBCCacheLoader works fine on data previously created * JDBCCacheLoaderOld. * * @author Mircea.Markus@iquestint.com * @version 1.0 */ @Test(groups = {"functional"}) @SuppressWarnings("deprecation") public class AdjListJDBCCacheLoaderCompatibilityTest { @SuppressWarnings("deprecation") private JDBCCacheLoaderOld oldImpl; private JDBCCacheLoader newImpl; private CacheSPI cache, cache2; /** * Note : newImpl is not started here but in each individual test. That's because on start it performs * some backward compatibility logic. * * @see JDBCCacheLoader#start() */ @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { newImpl = getNewCacheLoader(); oldImpl = getOldLoader(); cache = (CacheSPI) new DefaultCacheFactory().createCache(); cache2 = (CacheSPI) new DefaultCacheFactory().createCache(); newImpl.setCache(cache);//this is needed for marshaller oldImpl.setCache(cache2); oldImpl.start(); oldImpl.remove(Fqn.ROOT); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { oldImpl.remove(Fqn.ROOT); oldImpl.stop(); newImpl.stop(); TestingUtil.killCaches(cache, cache2); } public void testCommonOperations() throws Exception { newImpl.start(); oldImpl.put(Fqn.fromString("/a/b/c"), "key1", "value1"); oldImpl.put(Fqn.fromString("/a/b/d"), "key2", "value2"); oldImpl.put(Fqn.fromString("/a/b/e"), "key3", "value3"); assertTrue(newImpl.exists(Fqn.fromString("/a/b/c"))); assertTrue(newImpl.exists(Fqn.fromString("/a/b/d"))); assertTrue(newImpl.exists(Fqn.fromString("/a/b/e"))); assertEquals("value1", newImpl.get(Fqn.fromString("/a/b/c")).get("key1")); assertEquals("value2", newImpl.get(Fqn.fromString("/a/b/d")).get("key2")); assertEquals("value3", newImpl.get(Fqn.fromString("/a/b/e")).get("key3")); } /** * Does the new implementation manage to successfully remove nodes created by old one? */ public void testRemove() throws Exception { oldImpl.put(Fqn.fromString("/a/b/c"), "key1", "value1"); oldImpl.put(Fqn.fromString("/a/b/d"), "key2", "value2"); oldImpl.put(Fqn.fromString("/a/b/e"), "key3", "value3"); oldImpl.put(Fqn.fromString("/a/f/e"), "key4", "value4"); assertTrue(newImpl.exists(Fqn.fromString("/a/b/c"))); assertTrue(newImpl.exists(Fqn.fromString("/a/b/d"))); assertTrue(newImpl.exists(Fqn.fromString("/a/b/e"))); newImpl.start(); newImpl.remove(Fqn.fromString("/a/b")); assertFalse(newImpl.exists(Fqn.fromString("/a/b/c"))); assertFalse(newImpl.exists(Fqn.fromString("/a/b/d"))); assertFalse(newImpl.exists(Fqn.fromString("/a/b/e"))); assertTrue(newImpl.exists(Fqn.fromString("/a/f"))); assertTrue(newImpl.exists(Fqn.fromString("/a/f/e"))); } public void testLoadEntireState() throws Exception { oldImpl.put(Fqn.fromString("/a/b/c"), "key1", "value1"); oldImpl.put(Fqn.fromString("/a/b/d"), "key2", "value2"); oldImpl.put(Fqn.fromString("/a/b/e"), "key3", "value3"); oldImpl.put(Fqn.fromString("/a/f/e"), "key4", "value4"); oldImpl.put(Fqn.ROOT, "root_key", "root_value"); ByteArrayOutputStream newBaos = new ByteArrayOutputStream(1024); MarshalledValueOutputStream newOs = new MarshalledValueOutputStream(newBaos); newImpl.start(); newImpl.loadEntireState(newOs); newImpl.getMarshaller().objectToObjectStream(StateTransferManager.STREAMING_DELIMITER_NODE, newOs); newOs.close(); newImpl.remove(Fqn.ROOT); assertNull(newImpl.get(Fqn.fromString("/a/b/c"))); assertNull(newImpl.get(Fqn.fromString("/a/b/d"))); assertNull(newImpl.get(Fqn.fromString("/a/b/e"))); assertNull(newImpl.get(Fqn.fromString("/a/f/e"))); assertNull(newImpl.get(Fqn.ROOT)); ByteArrayInputStream bais = new ByteArrayInputStream(newBaos.toByteArray()); MarshalledValueInputStream is = new MarshalledValueInputStream(bais); newImpl.storeEntireState(is); assertEquals(newImpl.get(Fqn.fromString("/a/b/c")).get("key1"), "value1"); assertEquals(newImpl.get(Fqn.fromString("/a/b/d")).get("key2"), "value2"); assertEquals(newImpl.get(Fqn.fromString("/a/b/e")).get("key3"), "value3"); assertEquals(newImpl.get(Fqn.fromString("/a/f/e")).get("key4"), "value4"); assertEquals("root_value", newImpl.get(Fqn.ROOT).get("root_key")); assertEquals(newImpl.getNodeCount(), 8); } public void testLoadNodeState() throws Exception { oldImpl.put(Fqn.fromString("/a/b/c"), "key1", "value1"); oldImpl.put(Fqn.fromString("/a/b/d"), "key2", "value2"); oldImpl.put(Fqn.fromString("/a/b/e"), "key3", "value3"); oldImpl.put(Fqn.fromString("/a/f/e"), "key4", "value4"); oldImpl.put(Fqn.ROOT, "root_key", "root_value"); ByteArrayOutputStream newBaos = new ByteArrayOutputStream(1024); MarshalledValueOutputStream newOs = new MarshalledValueOutputStream(newBaos); newImpl.start(); newImpl.loadState(Fqn.fromString("/a/b"), newOs); newImpl.getMarshaller().objectToObjectStream(StateTransferManager.STREAMING_DELIMITER_NODE, newOs); newOs.close(); newImpl.remove(Fqn.fromString("/a/b")); assertNull(newImpl.get(Fqn.fromString("/a/b/c"))); assertNull(newImpl.get(Fqn.fromString("/a/b/d"))); assertNull(newImpl.get(Fqn.fromString("/a/b/e"))); assertNull(newImpl.get(Fqn.fromString("/a/b"))); ByteArrayInputStream bais = new ByteArrayInputStream(newBaos.toByteArray()); MarshalledValueInputStream is = new MarshalledValueInputStream(bais); newImpl.storeState(Fqn.fromString("/a/b"), is); assertEquals(newImpl.get(Fqn.fromString("/a/b/c")).get("key1"), "value1"); assertEquals(newImpl.get(Fqn.fromString("/a/b/d")).get("key2"), "value2"); assertEquals(newImpl.get(Fqn.fromString("/a/b/e")).get("key3"), "value3"); assertEquals(newImpl.get(Fqn.fromString("/a/f/e")).get("key4"), "value4"); assertEquals(newImpl.get(Fqn.ROOT).get("root_key"), "root_value"); assertEquals(newImpl.getNodeCount(), 8); } /** * getNodeDataList is a template method on which the serialisation process relies. We check here that the new * implementation works exactelly as the old one. */ public void testGetNodeData() throws Exception { oldImpl.put(Fqn.fromString("/a/b/c"), "key1", "value1"); oldImpl.put(Fqn.fromString("/a/b/d"), "key2", "value2"); oldImpl.put(Fqn.fromString("/a/b/e"), "key3", "value3"); oldImpl.put(Fqn.fromString("/a/f/e"), "key4", "value4"); oldImpl.put(Fqn.ROOT, "root_key", "root_value"); newImpl.start(); ArrayList oldList = new ArrayList(); oldImpl.getNodeDataList(Fqn.ROOT, oldList); ArrayList newList = new ArrayList(); newImpl.getNodeDataList(Fqn.ROOT, newList); assertEquals(new HashSet(oldList), new HashSet(newList)); } /** * Tests performs some backward copatibility work. See {@link JDBCCacheLoader#start()} for details. */ public void testStartWork() throws Exception { oldImpl.put(Fqn.fromString("/a/b/c"), "key1", "value1"); oldImpl.put(Fqn.fromString("/a/b/d"), "key2", "value2"); assertNull(oldImpl.get(Fqn.ROOT)); newImpl.start(); assertNotNull(newImpl.get(Fqn.ROOT)); } protected CacheLoaderConfig getSingleCacheLoaderConfig(String preload, String cacheloaderClass, String properties) throws Exception { String xml = "\n" + "false\n" + "" + preload + "\n" + "\n" + "" + cacheloaderClass + "\n" + "" + properties + "\n" + "false\n" + "false\n" + "true\n" + "false\n" + "\n" + ""; Element element = XmlHelper.stringToElement(xml); return XmlConfigurationParser.parseCacheLoaderConfig(element); } protected Properties getProperties() throws Exception { Properties prop = new Properties(); try { prop.load(this.getClass().getClassLoader().getResourceAsStream("cache-jdbc.properties")); return prop; } catch (Exception e) { throw new Exception("Error loading jdbc properties ", e); } } private JDBCCacheLoader getNewCacheLoader() throws Exception { Properties prop = getProperties(); String props = "cache.jdbc.driver =" + prop.getProperty("cache.jdbc.driver") + "\n" + "cache.jdbc.url=" + prop.getProperty("cache.jdbc.url") + "\n" + "cache.jdbc.user=" + prop.getProperty("cache.jdbc.user") + "\n" + "cache.jdbc.password=" + prop.getProperty("cache.jdbc.password") + "\n" + "cache.jdbc.node.type=" + prop.getProperty("cache.jdbc.node.type") + "\n" + "cache.jdbc.sql-concat=" + prop.getProperty("cache.jdbc.sql-concat") + "\n"; CacheLoaderConfig.IndividualCacheLoaderConfig base = getSingleCacheLoaderConfig("", "org.jboss.cache.loader.JDBCCacheLoader", props).getFirstCacheLoaderConfig(); JDBCCacheLoader jdbcCacheLoader = new JDBCCacheLoader(); jdbcCacheLoader.setConfig(base); return jdbcCacheLoader; } private JDBCCacheLoaderOld getOldLoader() throws Exception { Properties prop = getProperties(); String props = "cache.jdbc.driver =" + prop.getProperty("cache.jdbc.driver") + "\n" + "cache.jdbc.url=" + prop.getProperty("cache.jdbc.url") + "\n" + "cache.jdbc.user=" + prop.getProperty("cache.jdbc.user") + "\n" + "cache.jdbc.password=" + prop.getProperty("cache.jdbc.password") + "\n" + "cache.jdbc.node.type=" + prop.getProperty("cache.jdbc.node.type") + "\n" + "cache.jdbc.sql-concat=" + prop.getProperty("cache.jdbc.sql-concat");// + "\n" + // "cache.jdbc.connection.factory=org.jboss.cache.manualtests.cacheloader.OneConnectionFactory"; CacheLoaderConfig.IndividualCacheLoaderConfig base = getSingleCacheLoaderConfig("", "org.jboss.cache.loader.JDBCCacheLoader", props).getFirstCacheLoaderConfig(); JDBCCacheLoaderOld jdbcCacheLoader = new JDBCCacheLoaderOld(); jdbcCacheLoader.setConfig(base); return jdbcCacheLoader; } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/loader/AsyncFileCacheLoaderTest.java0000644000175000017500000001221510732752176031350 0ustar twernertwernerpackage org.jboss.cache.loader; import org.jboss.cache.CacheException; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.Modification; import org.jboss.cache.config.Configuration; import org.jboss.cache.statetransfer.StateTransferManager; import org.jboss.util.stream.MarshalledValueInputStream; import org.jboss.util.stream.MarshalledValueOutputStream; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertTrue; import org.testng.annotations.AfterMethod; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.util.Collections; import java.util.HashMap; public class AsyncFileCacheLoaderTest extends AbstractCacheLoaderTestBase { private CacheSPI cache; protected void configureCache() throws Exception { configureCache(""); } protected void configureCache(String props) throws Exception { cache = (CacheSPI) new DefaultCacheFactory().createCache(false); cache.getConfiguration().setCacheMode(Configuration.CacheMode.LOCAL); // cache.setCacheLoaderConfiguration(getSingleCacheLoaderConfig("", "org.jboss.cache.loader.jdbm.JdbmCacheLoader", cache.getConfiguration().setCacheLoaderConfig(getSingleCacheLoaderConfig("", "org.jboss.cache.loader.FileCacheLoader", props, true, false, true)); cache.create(); cache.start(); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { if (cache != null) { cache.stop(); } } public void testRestrictionOnAddingToQueue() throws Exception { configureCache(); CacheLoader loader = cache.getCacheLoaderManager().getCacheLoader(); loader.remove(Fqn.fromString("/blah")); loader.put(Fqn.fromString("/blah"), "one", "two"); loader.put(Fqn.fromString("/blah"), "three", "four"); loader.put(Fqn.fromString("/blah"), "five", "six"); loader.put(Fqn.fromString("/blah"), "seven", "eight"); // stop the cache loader loader.stop(); try { loader.remove(Fqn.fromString("/blah")); assertTrue("Should have restricted this entry from being made", false); } catch (CacheException e) { assertTrue(true); } // clean up loader.start(); loader.remove(Fqn.fromString("/blah")); } public void testPutImmediate() throws Exception { configureCache( "cache.async.put=false\n" + "cache.async.pollWait=10000\n" + ""); CacheLoader loader = cache.getCacheLoaderManager().getCacheLoader(); Fqn fqn = Fqn.fromString("/a/b/c/d"); HashMap map = new HashMap(); map.put("c", "d"); // Three kinds of puts! Modification mod = new Modification(Modification.ModificationType.PUT_KEY_VALUE, fqn, "e", "f"); loader.put(fqn, "a", "b"); loader.put(fqn, map); loader.put(Collections.singletonList(mod)); assertEquals("put right away", 3, loader.get(fqn).size()); loader.remove(fqn); } public void testBounded() throws Exception { configureCache( "cache.async.queueSize=1\n" + "cache.async.pollWait=10\n" + ""); CacheLoader loader = cache.getCacheLoaderManager().getCacheLoader(); Fqn fqn = Fqn.fromString("/bound"); loader.remove(fqn); // You can't really see it block though :-/ for (int i = 0; i < 50; i++) { cache.put(fqn, "key" + i, "value1"); } Thread.sleep(1000); assertEquals(50, loader.get(fqn).size()); loader.remove(fqn); } public void testNoReturnOld() throws Exception { configureCache( "cache.async.returnOld=false\n" + "cache.async.pollWait=10\n" + ""); CacheLoader loader = cache.getCacheLoaderManager().getCacheLoader(); System.out.println("Loader " + loader); cache.put(Fqn.ROOT, "key1", "value1"); Thread.sleep(100); assertEquals(null, loader.put(Fqn.ROOT, "key1", "value1")); assertEquals(null, loader.remove(Fqn.ROOT, "key1")); loader.remove(Fqn.ROOT); } public void testStoreState() throws Exception { configureCache(); Fqn X = Fqn.fromString("/x"); CacheLoader loader = cache.getCacheLoaderManager().getCacheLoader(); loader.remove(X); cache.put(X, "key1", "value1"); Thread.sleep(1000); ByteArrayOutputStream baos = new ByteArrayOutputStream(1024); MarshalledValueOutputStream os = new MarshalledValueOutputStream(baos); loader.loadEntireState(os); cache.getMarshaller().objectToObjectStream(StateTransferManager.STREAMING_DELIMITER_NODE, os); //os.close(); assertTrue(baos.size() > 0); loader.remove(X); ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); MarshalledValueInputStream is = new MarshalledValueInputStream(bais); loader.storeEntireState(is); //is.close(); assertEquals("X found", true, loader.exists(X)); loader.remove(X); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/loader/FileCacheLoaderTest.java0000644000175000017500000000710011017455042030334 0ustar twernertwernerpackage org.jboss.cache.loader; import org.jboss.cache.Fqn; import org.jboss.cache.util.TestingUtil; import static org.testng.AssertJUnit.assertEquals; import org.testng.annotations.Test; @Test(groups = {"functional"}) public class FileCacheLoaderTest extends CacheLoaderTestsBase { protected void configureCache() throws Exception { String tmpDir = System.getProperty("java.io.tmpdir", "/tmp"); String tmpCLLoc = tmpDir + "/JBossCache-FileCacheLoaderTest"; cache.getConfiguration().setCacheLoaderConfig(getSingleCacheLoaderConfig("", "org.jboss.cache.loader.FileCacheLoader", "location=" + tmpCLLoc, false, true, false)); TestingUtil.recursiveFileRemove(tmpCLLoc); } public void testIsCharacterPortableLocation() { FileCacheLoader fcl = new FileCacheLoader(); Object[][] data = new Object[][]{ {"C:\\here\\there.txt", true}, {"/home/here/there", true}, {"/home/*/jboss", false}, {"C:\\>/jgroups/jboss", false}, {"/cache/jboss<", false}, {"/pojocache|/galder", false}, {"/pojocache/gal\"der", false}}; for (Object[] aData : data) { String path = (String) aData[0]; boolean expected = (Boolean) aData[1]; assertEquals(path, expected, fcl.isCharacterPortableLocation(path)); } } public void testIsCharacterPortableTree() { FileCacheLoader fcl = new FileCacheLoader(); Object[][] data = new Object[][]{ {Fqn.fromString("/a/b/c/d/e"), true}, {Fqn.fromString("/a/*/c/d/e"), false}, {Fqn.fromString("/a/b/>/d/e"), false}, {Fqn.fromString("/a/ TS_BASE_FQN = Fqn.fromString("/TS"); public static final Fqn REGION_PREFIX_FQN = Fqn.fromString("/test"); public static final Fqn TS_FQN = Fqn.fromRelativeFqn(TS_BASE_FQN, Fqn.fromRelativeFqn(REGION_PREFIX_FQN, Fqn.fromString("/org/hibernate/cache/UpdateTimestampsCache"))); public static final String ITEM = "item"; /** * Prevent instantiation. */ private HibernateIntegrationTestUtil() { // no-op } } ././@LongLink0000000000000000000000000000015600000000000011567 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/integration/hibernate/UpdateTimestampsCachingTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/integration/hibernate/UpdateTimestampsCaching0000644000175000017500000001073710777151664033437 0ustar twernertwernerpackage org.jboss.cache.integration.hibernate; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.Cache; import org.jboss.cache.CacheFactory; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.config.Configuration; import static org.jboss.cache.integration.hibernate.HibernateIntegrationTestUtil.*; import static org.testng.AssertJUnit.assertEquals; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.Synchronization; import javax.transaction.Transaction; import javax.transaction.TransactionManager; import java.util.HashSet; import java.util.Set; /** * Tests that mimic the Hibernate Second Level Cache UpdateTimestamps * use case. *

* FIXME: Make this much more closely duplicate the real Hibernate usage; * use a CacheManager with configs consistent with Hibernate, etc. *

* * @author Brian Stansberry */ @Test(groups = "integration") public class UpdateTimestampsCachingTest { private static final Log log = LogFactory.getLog(UpdateTimestampsCachingTest.class); private static final Fqn ENTITY_TYPE_FQN = Fqn.fromString("/com/foo/MyEntity"); private Set> caches; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { caches = new HashSet>(); } @AfterMethod(alwaysRun = true) public void tearDown() { for (Cache cache : caches) cache.stop(); } private Cache createCache(boolean optimistic) { CacheFactory cf = new DefaultCacheFactory(); Cache cache = cf.createCache("META-INF/conf-test/local-tx-service.xml", false); cache.getConfiguration().setNodeLockingScheme(optimistic ? Configuration.NodeLockingScheme.OPTIMISTIC : Configuration.NodeLockingScheme.PESSIMISTIC); cache.start(); caches.add(cache); return cache; } public void testTimestampUpdateInAfterCompletionPessimistic() throws Exception { timestampUpdateInAfterCompletionTest(false); } public void testTimestampUpdateInAfterCompletionOptimistic() throws Exception { timestampUpdateInAfterCompletionTest(true); } /** * FIXME Make this use a cluster, not just a single cache * * @param optimistic * @throws Exception */ private void timestampUpdateInAfterCompletionTest(boolean optimistic) throws Exception { Cache cache = createCache(optimistic); TransactionManager tm = cache.getConfiguration().getRuntimeConfig().getTransactionManager(); tm.begin(); Transaction tx = tm.getTransaction(); UpdateTimestampsSynchronization sync = new UpdateTimestampsSynchronization(ENTITY_TYPE_FQN, cache, tm); tx.registerSynchronization(sync); Fqn fqn = Fqn.fromRelativeFqn(REGION_PREFIX_FQN, ENTITY_TYPE_FQN); fqn = Fqn.fromRelativeElements(fqn, "1"); cache.put(fqn, ITEM, "value"); tm.commit(); fqn = Fqn.fromRelativeFqn(HibernateIntegrationTestUtil.TS_FQN, ENTITY_TYPE_FQN); assertEquals(sync.getTimestamp(), cache.get(fqn, ITEM)); } private class UpdateTimestampsSynchronization implements Synchronization { private final Cache cache; private final Fqn entityType; private final TransactionManager tm; private Long timestamp; UpdateTimestampsSynchronization(Fqn entityType, Cache cache, TransactionManager tm) { this.entityType = entityType; this.cache = cache; this.tm = tm; } public Long getTimestamp() { return timestamp; } public void beforeCompletion() { // no-op } public void afterCompletion(int status) { Fqn fqn = Fqn.fromRelativeFqn(TS_FQN, entityType); try { timestamp = System.currentTimeMillis(); Transaction tx = tm.suspend(); cache.getInvocationContext().getOptionOverrides().setForceAsynchronous(true); cache.put(fqn, ITEM, timestamp); tm.resume(tx); log.info("Updated timestamp " + entityType); } catch (Exception e) { log.error("Problem updating timestamp " + entityType, e); throw new RuntimeException(e); } } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/manager/0000755000175000017500000000000011376173765024066 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/manager/CacheManagerTest.java0000644000175000017500000001174711005362317030057 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.manager; import org.jboss.cache.Cache; import org.jboss.cache.CacheManagerImpl; import org.jboss.cache.CacheStatus; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.ConfigurationRegistry; import org.jgroups.JChannelFactory; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.Test; import java.util.HashSet; import java.util.Iterator; import java.util.Set; /** * Tests CacheRegistryImpl. * * @author Brian Stansberry */ @Test(groups = {"functional"}) public class CacheManagerTest { /** * A file that includes every configuration element I could think of */ public static final String DEFAULT_CONFIGURATION_FILE = "META-INF/jbc2-configs.xml"; private Set> caches = new HashSet>(); @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { for (Cache cache : caches) { try { cache.stop(); cache.destroy(); } catch (Exception e) { e.printStackTrace(System.out); } } } /** * A test that instantiates a CacheRegistryImpl and cycles through all its * configs, creating and releasing each. *

* TODO: 2.2.0: Break this up into more fine-grained tests * * @throws Exception */ public void testBasic() throws Exception { JChannelFactory cf = new JChannelFactory(); cf.setMultiplexerConfig("stacks.xml"); // the default stacks in jgroups.jar CacheManagerImpl registry = new CacheManagerImpl(DEFAULT_CONFIGURATION_FILE, cf); registry.start(); ConfigurationRegistry configRegistry = registry.getConfigurationRegistry(); Set configNames = registry.getConfigurationNames(); assertEquals(7, configNames.size()); Set cacheNames = registry.getCacheNames(); assertEquals(0, cacheNames.size()); for (String configName : configNames) { assertNull(configName + " not created", registry.getCache(configName, false)); Cache cache = registry.getCache(configName, true); caches.add(cache); // Cache shouldn't be started assertEquals(CacheStatus.INSTANTIATED, cache.getCacheStatus()); cache.create(); cache.start(); // Config should be a clone Configuration rawConfig = configRegistry.getConfiguration(configName); Configuration realConfig = cache.getConfiguration(); assertFalse(rawConfig == realConfig); assertEquals(rawConfig.getClusterName(), realConfig.getClusterName()); } cacheNames = registry.getCacheNames(); assertEquals(configNames, cacheNames); // Test basic releasing of caches for (String configName : configNames) { registry.releaseCache(configName); } cacheNames = registry.getCacheNames(); assertEquals(0, cacheNames.size()); // We shouldn't have affected configuration set Set configNames2 = registry.getConfigurationNames(); assertEquals(configNames, configNames2); // Releasing only checkout of cache should have destroyed it for (Iterator> it = caches.iterator(); it.hasNext();) { assertEquals(CacheStatus.DESTROYED, it.next().getCacheStatus()); it.remove(); } // Get cache w/o asking to create returns null String configName = configNames.iterator().next(); assertNull(configName + " not created", registry.getCache(configName, false)); // Get cache w/ asking to create returns cache Cache cache = registry.getCache(configName, true); assertFalse(null == cache); caches.add(cache); cache.create(); cache.start(); // Test 2 checkouts of the same cache Cache cache2 = registry.getCache(configName, true); assertTrue(cache == cache2); registry.releaseCache(configName); // One release does not cause registry to stop cache assertEquals(CacheStatus.STARTED, cache.getCacheStatus()); registry.stop(); // Now it's stopped assertEquals(CacheStatus.DESTROYED, cache.getCacheStatus()); caches.remove(cache); cacheNames = registry.getCacheNames(); assertEquals(0, cacheNames.size()); assertEquals(cacheNames, registry.getConfigurationNames()); } public void testNullConfigResource() throws Exception { JChannelFactory cf = new JChannelFactory(); cf.setMultiplexerConfig("stacks.xml"); // the default stacks in jgroups.jar String configResource = null; CacheManagerImpl registry = new CacheManagerImpl(configResource, cf); registry.start(); assertEquals("No configs", 0, registry.getConfigurationNames().size()); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/GlobalTransactionTest.java0000644000175000017500000001162510672147360027560 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertFalse; import static org.testng.AssertJUnit.assertNotNull; import static org.testng.AssertJUnit.assertTrue; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.net.UnknownHostException; import org.jboss.cache.transaction.GlobalTransaction; import org.jgroups.stack.IpAddress; import org.testng.annotations.Test; /** * @author Bela Ban Apr 14, 2003 * @version $Id: GlobalTransactionTest.java 4458 2007-09-13 05:47:28Z genman $ */ public class GlobalTransactionTest { @Test(groups = {"functional"}) public void testEquality() throws UnknownHostException { IpAddress a1 = new IpAddress("localhost", 4444); GlobalTransaction tx1, tx2; tx1 = GlobalTransaction.create(a1); tx2 = GlobalTransaction.create(a1); System.out.println("\ntx1: " + tx1 + "\ntx2: " + tx2); assertTrue(tx1.equals(tx2) == false); tx2 = tx1; assertTrue(tx1.equals(tx2)); } @Test(groups = {"functional"}) public void testEqualityWithOtherObject() throws UnknownHostException { IpAddress a1 = new IpAddress("localhost", 4444); GlobalTransaction tx1 = GlobalTransaction.create(a1); System.out.println("\ntx1: " + tx1); assertFalse(tx1.equals(Thread.currentThread())); } @Test(groups = {"functional"}) public void testEqualityWithNull() throws UnknownHostException { IpAddress a1 = new IpAddress("localhost", 4444); GlobalTransaction tx1 = GlobalTransaction.create(a1); System.out.println("\ntx1: " + tx1); assertFalse(tx1.equals(null)); } @Test(groups = {"functional"}) public void testHashcode() throws UnknownHostException { IpAddress a1 = new IpAddress("localhost", 4444); GlobalTransaction tx1, tx2; tx1 = GlobalTransaction.create(a1); tx2 = GlobalTransaction.create(a1); System.out.println("\ntx1: " + tx1 + "\ntx2: " + tx2); assertTrue(tx1.equals(tx2) == false); int hcode_1 = tx1.hashCode(); int hcode_2 = tx2.hashCode(); assertFalse(hcode_1 == hcode_2); tx2 = tx1; assertTrue(tx1.equals(tx2)); hcode_1 = tx1.hashCode(); hcode_2 = tx2.hashCode(); assertEquals(hcode_1, hcode_2); } @Test(groups = {"functional"}) public void testExternalization() throws Exception { IpAddress a1 = new IpAddress("localhost", 4444); IpAddress a2 = new IpAddress("localhost", 5555); GlobalTransaction tx1, tx2, tx1_copy = null, tx2_copy = null; ByteArrayOutputStream bos = null; ByteArrayInputStream bis = null; ObjectOutputStream out = null; ObjectInputStream in = null; byte[] buf = null; tx1 = GlobalTransaction.create(a1); tx2 = GlobalTransaction.create(a2); bos = new ByteArrayOutputStream(1024); out = new ObjectOutputStream(bos); out.writeObject(tx1); out.writeObject(tx2); out.flush(); buf = bos.toByteArray(); bis = new ByteArrayInputStream(buf); in = new ObjectInputStream(bis); tx1_copy = (GlobalTransaction) in.readObject(); tx2_copy = (GlobalTransaction) in.readObject(); System.out.println("\ntx1: " + tx1 + ", tx1_copy: " + tx1_copy + "\ntx2: " + tx2 + ", tx2_copy: " + tx2_copy); assertNotNull(tx1_copy); assertNotNull(tx2_copy); assertEquals(tx1, tx1_copy); assertEquals(tx2, tx2_copy); int hcode_1 = tx1.hashCode(); int hcode_2 = tx2.hashCode(); int hcode_3 = tx1_copy.hashCode(); int hcode_4 = tx2_copy.hashCode(); assertFalse(hcode_1 == hcode_2); assertFalse(hcode_3 == hcode_4); assertEquals(hcode_1, hcode_3); assertEquals(hcode_2, hcode_4); } @Test(groups = {"functional"}) public void testWithNullAddress() { GlobalTransaction tx1, tx2, tmp_tx1; tx1 = GlobalTransaction.create(null); tx2 = GlobalTransaction.create(null); tmp_tx1 = tx1; assertEquals(tx1, tmp_tx1); assertTrue(tx1.equals(tx2) == false); } @Test(groups = {"functional"}) public void testOneNullAddress() throws UnknownHostException { GlobalTransaction tx1, tx2; tx1 = GlobalTransaction.create(null); assertFalse(tx1.equals(null)); tx2 = GlobalTransaction.create(null); assertFalse(tx1.equals(tx2)); assertFalse(tx2.equals(tx1)); IpAddress a1 = new IpAddress("localhost", 4444); tx2 = GlobalTransaction.create(a1); assertFalse(tx1.equals(tx2)); assertFalse(tx2.equals(tx1)); } void log(String msg) { System.out.println("-- [" + Thread.currentThread() + "]: " + msg); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/transaction/0000755000175000017500000000000011376173747025001 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/transaction/ConcurrentBankTest.java0000644000175000017500000001661311017455042031410 0ustar twernertwernerpackage org.jboss.cache.transaction; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.CacheException; import org.jboss.cache.CacheFactory; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.UnitTestCacheConfigurationFactory; import org.jboss.cache.lock.IsolationLevel; import org.jboss.cache.lock.TimeoutException; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.util.CachePrinter; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.fail; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.UserTransaction; import java.util.HashMap; import java.util.Iterator; import java.util.Set; /** * Unit test for local CacheImpl with concurrent transactions. * Uses locking and multiple threads to test concurrent r/w access to the tree. * * @author Stefan Pohl * @author Ben Wang * @version $Revision: 5906 $ */ @Test(groups = {"functional", "transaction"}, enabled = true) public class ConcurrentBankTest { private CacheSPI cache; private static Log log = LogFactory.getLog(ConcurrentBankTest.class); private final Fqn NODE = Fqn.fromString("/cachetest"); private final int ROLLBACK_CHANCE = 100; private static String customer[] = {"cu1", "cu2", "cu3"}; private static final int BOOKINGS = 1000; private static boolean testFailedinThread = false; private void failMain() { testFailedinThread = true; } @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { Configuration conf = UnitTestCacheConfigurationFactory.createConfiguration(Configuration.CacheMode.LOCAL, true); CacheFactory instance = new DefaultCacheFactory(); cache = (CacheSPI) instance.createCache(conf, false); cache.getConfiguration().setIsolationLevel(IsolationLevel.SERIALIZABLE); cache.getConfiguration().setTransactionManagerLookupClass(TransactionSetup.getManagerLookup()); cache.create(); cache.start(); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { cache.stop(); // BW. kind of a hack to destroy jndi binding and thread local tx before next run. TransactionSetup.cleanup(); } public void testConcurrentBooking() throws Exception { Teller one, two; if (cache.getRoot().get(NODE) == null) { cache.put(NODE, "cu1", 1000); cache.put(NODE, "cu2", 1000); cache.put(NODE, "cu3", 1000); } one = new Teller("one", cache); two = new Teller("two", cache); one.start(); TestingUtil.sleepThread((long) 100); two.start(); one.join(); two.join(); log("lock info:\n" + CachePrinter.printCacheLockingInfo(cache) + testFailedinThread); assert !testFailedinThread; } private static void log(String msg) { // System.out.println("-- [" + Thread.currentThread() + "]: " + msg); log.info("-- [" + Thread.currentThread() + "]: " + msg); } private class Teller extends Thread { CacheSPI cache; public Teller(String str, CacheSPI cache) { super(str); this.cache = cache; } public void run() { int count = customer.length; UserTransaction tx = null; try { tx = TransactionSetup.getUserTransaction(); boolean again = false; int src = 0; int dst = 0; int amo = 0; int anz = 0; while (anz < BOOKINGS) { if (!again) { src = (int) (Math.random() * count); dst = (int) (Math.random() * (count - 1)); amo = 1 + (int) (Math.random() * 20); if (dst >= src) dst++; } tx.begin(); HashMap accounts = getAccounts();// read lock on NODE tx.commit();// releases read lock int sum = sumAccounts(accounts); log(anz + ": " + accounts + " Summe: " + sum); // the sum of all accounts always has to be 3000 if (sum != 3000) { failMain(); return;// terminate thread } assertEquals("the sum of all accounts always has to be 3000", 3000, sum); try { tx.begin(); deposit(customer[src], customer[dst], amo, tx);// gets write lock tx.commit();// releases write lock again = false; } catch (TimeoutException timeout_ex) { System.out.println("transaction is rolled back, will try again (ex=" + timeout_ex.getClass() + ")"); tx.rollback(); again = true; } catch (Throwable e) { System.out.println("transaction is rolled back, will try again (ex=" + e.getMessage() + ")"); tx.rollback(); again = true; } anz++; yield(); } } catch (Throwable t) { t.printStackTrace(); fail(t.toString()); } } /** * Posting */ public void deposit(String from, String to, int amount, UserTransaction tx) throws Exception { log("deposit(" + from + ", " + to + ", " + amount + ") called."); int act; // debit act = cache.get(NODE, from); cache.put(NODE, from, act - amount); log("deposit(" + from + ", " + to + ", " + amount + ") debited."); // eventually rollback the transaction if ((int) (Math.random() * ROLLBACK_CHANCE) == 0) { log("!!!manually set rollback (" + from + ", " + to + ", " + amount + ")."); tx.setRollbackOnly(); throw new Exception("Manually set rollback!"); } // credit act = cache.get(NODE, to); cache.put(NODE, to, act + amount); log("deposit(" + from + ", " + to + ", " + amount + ") finished."); } /** * retrieving amounts of accounts */ public HashMap getAccounts() throws CacheException { log("getAccounts() called."); HashMap result = new HashMap(); try { Set set = cache.getRoot().getChild(NODE).getKeys();// gets read lock for (Object name : set) { result.put(name, cache.get(NODE, name)); } return result; } catch (CacheException ce) { throw ce; } } protected int sumAccounts(HashMap map) { Iterator iter = map.values().iterator(); int result = 0; while (iter.hasNext()) { result += iter.next(); } return result; } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/transaction/AbortionTest.java0000644000175000017500000001472711017455042030253 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.transaction; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.interceptors.OrderedSynchronizationHandler; import org.jboss.cache.transaction.NotifyingTransactionManager.Notification; import org.jgroups.JChannel; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.RollbackException; import javax.transaction.Synchronization; import javax.transaction.SystemException; import javax.transaction.Transaction; import javax.transaction.TransactionManager; /** * @author Manik Surtani (manik@jboss.org) */ @Test(groups = {"functional"}) public class AbortionTest { private CacheSPI cache1, cache2, cache3; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { cache1 = initCache(false); TestingUtil.sleepThread(1500); // to ensure cache1 is the coordinator cache2 = initCache(false); cache3 = initCache(true); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { TestingUtil.killCaches(cache3, cache2, cache1); } private CacheSPI initCache(boolean notifying) { CacheSPI c = (CacheSPI) new DefaultCacheFactory().createCache(false); c.getConfiguration().setCacheMode("REPL_SYNC"); c.getConfiguration().setClusterConfig(getJGroupsStack()); c.getConfiguration().setFetchInMemoryState(false); if (!notifying) { c.getConfiguration().setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); } else { c.getConfiguration().setTransactionManagerLookupClass("org.jboss.cache.transaction.NotifyingTransactionManager"); } c.start(); return c; } // we need a 'special' stack that does not attempt redelivery since we kill a channel midway during a tx in this test. private String getJGroupsStack() { return JChannel.DEFAULT_PROTOCOL_STACK; } private void destroyCache(CacheSPI c) { if (c != null) { c.stop(); c.destroy(); } } public void testSyncCaches() throws Exception { performTest(false, false); } public void testSyncCachesSyncCommitRollback() throws Exception { performTest(true, false); } /** * Note that this tests a *remote* beforeCompletion abort - which is a part of the calling instance's afterCompletion. * * @throws Exception */ public void testAbortBeforeCompletion() throws Exception { performTest(true, true); } private void performTest(boolean syncCommitRollback, boolean abortBeforeCompletion) throws Exception { cache1.getConfiguration().setSyncCommitPhase(syncCommitRollback); cache1.getConfiguration().setSyncRollbackPhase(syncCommitRollback); cache2.getConfiguration().setSyncCommitPhase(syncCommitRollback); cache2.getConfiguration().setSyncRollbackPhase(syncCommitRollback); cache3.getConfiguration().setSyncCommitPhase(syncCommitRollback); cache3.getConfiguration().setSyncRollbackPhase(syncCommitRollback); TransactionManager mgr1 = cache1.getTransactionManager(); TransactionManager mgr2 = cache2.getTransactionManager(); assertTrue(cache3.getTransactionManager() instanceof NotifyingTransactionManager); NotifyingTransactionManager mgr3 = (NotifyingTransactionManager) cache3.getTransactionManager(); mgr3.setCache(cache3); assertSame(mgr1, mgr2); assertNotSame(mgr1, mgr3); assertNotSame(mgr2, mgr3); assertTrue(mgr1 instanceof DummyTransactionManager); assertTrue(mgr2 instanceof DummyTransactionManager); cache1.put("/test", "key", "value"); assertEquals("value", cache1.get("/test", "key")); assertEquals("value", cache2.get("/test", "key")); assertEquals("value", cache3.get("/test", "key")); mgr3.setNotification(new TestNotification(abortBeforeCompletion)); mgr1.begin(); cache1.put("/test", "key", "value2"); mgr1.commit(); TestingUtil.sleepThread(5000); // only test cache1 and cache2. Assume cache3 has crashed out. assertEquals(0, cache1.getNumberOfLocksHeld()); assertEquals(0, cache2.getNumberOfLocksHeld()); assertEquals("put in transaction should NOT have been rolled back", "value2", cache1.get("/test", "key")); assertEquals("put in transaction should NOT have been rolled back", "value2", cache2.get("/test", "key")); } class TestNotification implements Notification { boolean abortBeforeCompletion; public TestNotification(boolean abortBeforeCompletion) { this.abortBeforeCompletion = abortBeforeCompletion; } public void notify(Transaction tx, TransactionEntry entry) throws SystemException, RollbackException { OrderedSynchronizationHandler osh = entry.getOrderedSynchronizationHandler(); final Transaction finalTx = tx; System.out.println("Notify called."); // add an aborting sync handler. Synchronization abort = new Synchronization() { public void beforeCompletion() { if (abortBeforeCompletion) { cache3.getConfiguration().getRuntimeConfig().getChannel().close(); System.out.println("Returning from abort.beforeCompletion"); try { finalTx.setRollbackOnly(); } catch (SystemException e) { throw new RuntimeException("Unable to set rollback", e); } throw new RuntimeException("Dummy exception"); } } public void afterCompletion(int i) { if (!abortBeforeCompletion) { cache3.getConfiguration().getRuntimeConfig().getChannel().close(); System.out.println("Returning from abort.afterCompletion"); throw new RuntimeException("Dummy exception"); } } }; osh.registerAtHead(abort); System.out.println("Added sync handler."); } } } ././@LongLink0000000000000000000000000000015100000000000011562 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/transaction/IsolationLevelRepeatableReadTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/transaction/IsolationLevelRepeatableReadTest.0000644000175000017500000001077210732752176033355 0ustar twernertwernerpackage org.jboss.cache.transaction; import org.jboss.cache.CacheFactory; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.lock.IsolationLevel; import org.jboss.cache.lock.TimeoutException; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.NotSupportedException; import javax.transaction.SystemException; import javax.transaction.TransactionManager; import java.util.concurrent.CountDownLatch; /** * Tests READ_COMMITED isolation level. * * @author Ovidiu Feodorov * @version $Id: IsolationLevelRepeatableReadTest.java 4912 2007-12-21 14:58:06Z manik.surtani@jboss.com $ */ @Test(groups = {"functional", "transaction"}) public class IsolationLevelRepeatableReadTest { private CacheSPI cache = null; private final Fqn FQN = Fqn.fromString("/a"); private final String KEY = "key"; private final String VALUE = "value"; private volatile boolean writerFailed; private volatile AssertionError writerError; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { writerFailed = false; writerError = null; CacheFactory instance = new DefaultCacheFactory(); cache = (CacheSPI) instance.createCache(false); cache.getConfiguration().setCacheMode("LOCAL"); cache.getConfiguration().setIsolationLevel(IsolationLevel.REPEATABLE_READ); cache.getConfiguration().setTransactionManagerLookupClass(TransactionSetup.getManagerLookup()); cache.getConfiguration().setLockAcquisitionTimeout(1000); cache.start(); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { cache.stop(); } /** * Test creates a cache node then starts a separate thread that removes * the node inside a tx. Test confirms that the removal cannot be seen * before the test commits. * * @throws Exception */ public void testNodeRemoved() throws Exception { final CountDownLatch readerCanRead = new CountDownLatch(1); final CountDownLatch readerDone = new CountDownLatch(1); final CountDownLatch writerDone = new CountDownLatch(1); cache.put(FQN, KEY, VALUE); assertEquals(VALUE, cache.get(FQN, KEY)); // start a writer thread and a transaction Thread writerThread = new Thread(new Runnable() { public void run() { try { TransactionManager tm = startTransaction(); // change VALUE in a transaction cache.removeNode(FQN); // notify the reading thread readerCanRead.countDown(); readerDone.await(); tm.commit(); } catch (AssertionError e) { writerError = e; } catch (Throwable t) { t.printStackTrace(); writerFailed = true; } finally { System.out.println("writer thread exits"); readerCanRead.countDown(); writerDone.countDown(); } } }, "WRITER"); writerThread.start(); try { // wait until the writer thread changes the value in a transaction, // but it did not yet commit or roll back. readerCanRead.await(); // I shouldn't be able to see the "dirty" value assertEquals("2nd thread cannot see uncommitted changes", VALUE, cache.get(FQN, KEY)); } catch (TimeoutException t) { // ignore, this is good } finally { System.out.println("reader thread exits"); readerDone.countDown(); } // wait for the writer to finish writerDone.await(); assertNull("Node was not removed", cache.getNode(FQN)); // If any assertion failed, throw on the AssertionFailedError if (writerError != null) { throw writerError; } if (writerFailed) { fail("The writer thread exited incorrectly. Watch the log for previous stack traces"); } } private TransactionManager startTransaction() throws SystemException, NotSupportedException { TransactionManager mgr = cache.getTransactionManager(); mgr.begin(); return mgr; } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/transaction/PrepareTxTest.java0000644000175000017500000001135311017221212030366 0ustar twernertwernerpackage org.jboss.cache.transaction; import org.jboss.cache.CacheException; import org.jboss.cache.CacheFactory; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import static org.testng.AssertJUnit.assertEquals; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.NotSupportedException; import javax.transaction.Synchronization; import javax.transaction.Transaction; import javax.transaction.TransactionManager; /** * Created by IntelliJ IDEA. * User: bela * Date: Jun 9, 2004 * Time: 9:05:19 AM */ @Test(groups = {"functional", "transaction"}) public class PrepareTxTest { CacheSPI cache; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { CacheFactory instance = new DefaultCacheFactory(); cache = (CacheSPI) instance.createCache(false); cache.getConfiguration().setCacheMode("local"); cache.getConfiguration().setTransactionManagerLookupClass(TransactionSetup.getManagerLookup()); cache.create(); cache.start(); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { cache.stop(); cache.destroy(); } /** * Tests cache modification inside the afterCompletion() callback. Reproduces a bug fixed in * connection with JBossCache being used as Hibernate's second level cache * * @throws Exception * @throws NotSupportedException */ public void testCacheModificationInBeforeCompletionPhase() throws Exception { int numLocks = 0; TransactionManager mgr = cache.getTransactionManager(); mgr.begin(); Transaction tx = mgr.getTransaction(); // this will cause the cache to register with TransactionManager for TX completion callbacks cache.put("/one/two/three", "key1", "val1"); numLocks = cache.getNumberOfLocksHeld(); assertEquals(4, numLocks); // we register *second* tx.registerSynchronization(new Synchronization() { public void beforeCompletion() { try { cache.put("/a/b/c", null); } catch (CacheException e) { e.printStackTrace(); } } public void afterCompletion(int status) { } }); mgr.commit(); numLocks = cache.getNumberOfLocksHeld(); assertEquals(0, numLocks); int num_local_txs, num_global_txs; TransactionTable tx_table = cache.getTransactionTable(); num_local_txs = tx_table.getNumLocalTransactions(); num_global_txs = tx_table.getNumGlobalTransactions(); System.out.println("Number of Transactions: " + num_local_txs + "\nNumber of GlobalTransactions: " + num_global_txs + "\nTransactionTable:\n " + tx_table.toString(true)); assertEquals(num_local_txs, num_global_txs); assertEquals(0, num_local_txs); } /** * Tests cache modification inside the afterCompletion() callback. Reproduces a bug fixed in * connection with JBossCache being used as Hibernate's second level cache * * @throws Exception * @throws NotSupportedException */ public void testCacheModificationInAfterCompletionPhase() throws Exception { int numLocks = 0; TransactionManager mgr = cache.getTransactionManager(); mgr.begin(); Transaction tx = mgr.getTransaction(); // this will cause the cache to register with TransactionManager for TX completion callbacks cache.put("/one/two/three", "key1", "val1"); numLocks = cache.getNumberOfLocksHeld(); assertEquals(4, numLocks); // we register *second* tx.registerSynchronization(new Synchronization() { public void beforeCompletion() { } public void afterCompletion(int status) { try { cache.put("/a/b/c", null); } catch (CacheException e) { e.printStackTrace(); } } }); mgr.commit(); numLocks = cache.getNumberOfLocksHeld(); assertEquals(0, numLocks); int num_local_txs, num_global_txs; TransactionTable tx_table = cache.getTransactionTable(); num_local_txs = tx_table.getNumLocalTransactions(); num_global_txs = tx_table.getNumGlobalTransactions(); System.out.println("Number of Transactions: " + num_local_txs + "\nNumber of GlobalTransactions: " + num_global_txs + "\nTransactionTable:\n " + tx_table.toString(true)); assertEquals(num_local_txs, num_global_txs); assertEquals(0, num_local_txs); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/transaction/TransactionTest.java0000644000175000017500000007702111017522374030762 0ustar twernertwerner/* * * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.transaction; import org.jboss.cache.CacheException; import org.jboss.cache.CacheFactory; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.Node; import org.jboss.cache.NodeSPI; import org.jboss.cache.lock.IsolationLevel; import org.jboss.cache.lock.LockManager; import static org.jboss.cache.lock.LockType.READ; import static org.jboss.cache.lock.LockType.WRITE; import org.jboss.cache.util.CachePrinter; import org.jboss.cache.util.TestingUtil; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.HeuristicMixedException; import javax.transaction.HeuristicRollbackException; import javax.transaction.NotSupportedException; import javax.transaction.RollbackException; import javax.transaction.SystemException; import javax.transaction.Transaction; import javax.transaction.TransactionManager; import javax.transaction.UserTransaction; import java.util.HashMap; import java.util.Map; import java.util.Set; /** * Tests transactional access to a local CacheImpl. * Note: we use DummpyTranasctionManager to replace jta * * @version $Id: TransactionTest.java 5912 2008-05-29 12:43:40Z manik.surtani@jboss.com $ */ @Test(groups = {"functional", "transaction"}) public class TransactionTest { CacheSPI cache = null; UserTransaction tx = null; Exception exception; LockManager lockManager; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { CacheFactory instance = new DefaultCacheFactory(); cache = (CacheSPI) instance.createCache(false); cache.getConfiguration().setClusterName("test"); cache.getConfiguration().setStateRetrievalTimeout(10000); cache.getConfiguration().setIsolationLevel(IsolationLevel.SERIALIZABLE); cache.getConfiguration().setLockParentForChildInsertRemove(true);// this test case is written to assume this. cache.getConfiguration().setTransactionManagerLookupClass(TransactionSetup.getManagerLookup()); tx = TransactionSetup.getUserTransaction(); cache.create(); cache.start(); exception = null; lockManager = TestingUtil.extractLockManager(cache); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { if (cache != null) { cache.stop(); cache = null; } // BW. kind of a hack to destroy jndi binding and thread local tx before next run. TransactionSetup.cleanup(); if (tx != null) { try { tx.rollback(); } catch (Throwable t) { // do nothing } tx = null; } } public void testPutTx() throws Exception { tx.begin(); cache.put("/a/b/c", "age", 38); // the tx interceptor should know that we're in the same tx. assertEquals(cache.get("/a/b/c", "age"), 38); cache.put("/a/b/c", "age", 39); tx.commit(); // This test is done outside the TX, it wouldn't work if someone else // modified "age". This works because we're the only TX running. assertEquals(cache.get("/a/b/c", "age"), 39); } public void testRollbackTx1() { try { tx.begin(); cache.put("/a/b/c", "age", 38); cache.put("/a/b/c", "age", 39); tx.rollback(); // This test is done outside the TX, it wouldn't work if someone else // modified "age". This works because we're the only TX running. assertNull(cache.get("/a/b/c", "age")); } catch (Throwable t) { t.printStackTrace(); fail(t.toString()); } } public void testGetAfterRemovalRollback() throws Exception { assertEquals(0, cache.getNumberOfLocksHeld()); cache.put("/a/b", null); assertEquals(0, cache.getNumberOfLocksHeld()); assertTrue(cache.exists("/a/b")); tx.begin(); cache.removeNode("/a/b"); assertFalse(cache.exists("/a/b")); tx.rollback(); assertTrue(cache.exists("/a/b")); assertEquals(0, cache.getNumberOfLocksHeld()); // new tx in new thread Thread th = new Thread() { public void run() { try { cache.getTransactionManager().begin(); assertNotNull(cache.getNode("/a/b")); cache.getTransactionManager().rollback(); } catch (Exception e) { e.printStackTrace(); fail("Caught exception"); } } }; th.start(); th.join(); assertEquals(0, cache.getNumberOfLocksHeld()); } public void testRollbackTx2() { try { tx.begin(); cache.put("/a/b/c", "age", 38); cache.remove("/a/b/c", "age"); tx.rollback(); // This test is done outside the TX, it wouldn't work if someone else // modified "age". This works because we're the only TX running. assertNull(cache.get("/a/b/c", "age")); } catch (Throwable t) { t.printStackTrace(); fail(t.toString()); } } public void testRollbackTx2a() { try { cache.put("/a/b/c", "age", 38); tx.begin(); cache.remove("/a/b/c", "age"); tx.rollback(); // This test is done outside the TX, it wouldn't work if someone else // modified "age". This works because we're the only TX running. assertEquals(38, cache.get("/a/b/c", "age")); } catch (Throwable t) { t.printStackTrace(); fail(t.toString()); } } public void testRollbackTx3() { try { java.util.Map map1 = new java.util.HashMap(); map1.put("age", 38); java.util.Map map2 = new java.util.HashMap(); map2.put("age", 39); tx.begin(); cache.put("/a/b/c", map1); cache.put("/a/b/c", map2); tx.rollback(); // This test is done outside the TX, it wouldn't work if someone else // modified "age". This works because we're the only TX running. assertNull(cache.get("/a/b/c", "age")); } catch (Throwable t) { t.printStackTrace(); fail(t.toString()); } } public void testRollbackTx4() { try { Map map = new HashMap(); map.put("age", 38); tx.begin(); cache.put("/a/b/c", map); cache.removeNode("/a/b/c"); tx.rollback(); // This test is done outside the TX, it wouldn't work if someone else // modified "age". This works because we're the only TX running. assertNull(cache.get("/a/b/c", "age")); } catch (Throwable t) { t.printStackTrace(); fail(t.toString()); } } public void testNodeCreationRollback() { try { tx.begin(); System.out.println("initial state:\n" + cache); cache.put("/bela/ban", "key", "value"); System.out.println("after put():\n" + cache); tx.rollback(); System.out.println("after rollback():\n" + cache); assertNull("node should be not existent", cache.getNode("/bela/ban")); } catch (Throwable t) { t.printStackTrace(); fail(t.toString()); } } public void testNodeCreationRollback2() { try { cache.put("/bela/ban", null); tx.begin(); cache.put("/bela/ban/michelle", null); tx.rollback(); assertNotNull("node should be not null", cache.getNode("/bela/ban")); assertNull("node should be not existent", cache.getNode("/bela/ban/michelle")); } catch (Throwable t) { t.printStackTrace(); fail(t.toString()); } } public void testNodeDeletionRollback() { try { cache.put("/a/b/c", null); tx.begin(); cache.removeNode("/a/b/c"); assertNull(cache.getNode("/a/b/c")); cache.removeNode("/a/b"); assertNull(cache.getNode("/a/b")); cache.removeNode("/a"); assertNull(cache.getNode("/a")); tx.rollback(); assertNotNull(cache.getNode("/a/b/c")); assertNotNull(cache.getNode("/a/b")); assertNotNull(cache.getNode("/a")); } catch (Throwable t) { t.printStackTrace(); fail(t.toString()); } } public void testNodeDeletionRollback2() throws Exception { cache.put("/a/b/c", null); cache.put("/a/b/c1", null); cache.put("/a/b/c2", null); tx.begin(); cache.removeNode("/a"); assertNull(cache.getNode("/a/b/c")); assertNull(cache.getNode("/a/b/c1")); assertNull(cache.getNode("/a/b/c2")); assertNull(cache.getNode("/a/b")); assertNull(cache.getNode("/a")); Set children = cache.getChildrenNames(Fqn.fromString("/a/b")); assertTrue(children.isEmpty()); children = cache.getChildrenNames("/a"); assertTrue(children.isEmpty()); tx.rollback(); assertNotNull(cache.getNode("/a")); assertNotNull(cache.getNode("/a/b")); assertNotNull(cache.getNode("/a/b/c")); assertNotNull(cache.getNode("/a/b/c1")); assertNotNull(cache.getNode("/a/b/c2")); children = cache.getChildrenNames(Fqn.fromString("/a/b")); assertEquals(3, children.size()); } public void testNodeCreation() throws Exception { GlobalTransaction gtx; cache.put("/a/b", null); tx.begin(); gtx = cache.getCurrentTransaction(); cache.put("/a/b/c", null); assertLocked(gtx, "/a", false); assertLocked(gtx, "/a/b", true); assertLocked(gtx, "/a/b/c", true); } public void testNodeCreation2() throws Exception { GlobalTransaction gtx; tx.begin(); gtx = cache.getCurrentTransaction(); cache.put("/a/b/c", null); assertLocked(gtx, "/a", true); assertLocked(gtx, "/a/b", true); assertLocked(gtx, "/a/b/c", true); } public void testNodeRemoval() throws SystemException, NotSupportedException { GlobalTransaction gtx; cache.put("/a/b/c", null); tx.begin(); gtx = cache.getCurrentTransaction(); cache.removeNode("/a/b/c");// need to remove the node, not just the data in the node. assertLocked(gtx, "/a", false); assertLocked(gtx, "/a/b", true); assertLocked(gtx, "/a/b/c", true); tx.rollback(); } public void testNodeRemoval2() throws SystemException, NotSupportedException { GlobalTransaction gtx; cache.put("/a/b/c", null); tx.begin(); gtx = cache.getCurrentTransaction(); cache.removeNode("/a/b");// need to remove the node, not just the data in the node. assertLocked(gtx, "/a", true); assertLocked(gtx, "/a/b", true); assertLocked(gtx, "/a/b/c", true); tx.rollback(); } public void testIntermediateNodeCreationOnWrite() throws Exception { cache.put("/a", null); tx.begin(); cache.put("/a/b/c", null); // expecting WLs on /a, /a/b and /a/b/c. GlobalTransaction gtx = cache.getCurrentTransaction(); assertLocked(gtx, "/a", true); assertLocked(gtx, "/a/b", true); assertLocked(gtx, "/a/b/c", true); tx.rollback(); } public void testIntermediateNodeCreationOnRead() throws Exception { cache.put("/a", null); tx.begin(); cache.getNode("/a/b/c"); // expecting RLs on /, /a // /a/b, /a/b/c should NOT be created! GlobalTransaction gtx = cache.getCurrentTransaction(); assertLocked(gtx, "/", false); assertLocked(gtx, "/a", false); assertNull("/a/b should not exist", cache.peek(Fqn.fromString("/a/b"), true)); assertNull("/a/b/c should not exist", cache.peek(Fqn.fromString("/a/b/c"), true)); tx.rollback(); assertNull("/a/b should not exist", cache.peek(Fqn.fromString("/a/b"), true)); assertNull("/a/b/c should not exist", cache.peek(Fqn.fromString("/a/b/c"), true)); } public void testIntermediateNodeCreationOnRemove() throws Exception { cache.put("/a", null); tx.begin(); cache.removeNode("/a/b/c"); // expecting RLs on /, /a // /a/b, /a/b/c should NOT be created! GlobalTransaction gtx = cache.getCurrentTransaction(); assertLocked(gtx, "/", false); assertLocked(gtx, "/a", true); assertLocked(gtx, "/a/b", true); assertLocked(gtx, "/a/b/c", true); assertNotNull("/a/b should exist", cache.peek(Fqn.fromString("/a/b"), true)); assertNotNull("/a/b/c should exist", cache.peek(Fqn.fromString("/a/b/c"), true)); assertNotNull("/a/b should NOT be visible", cache.exists(Fqn.fromString("/a/b"))); assertNotNull("/a/b/c should NOT be visible", cache.exists(Fqn.fromString("/a/b/c"))); tx.rollback(); assertNull("/a/b should not exist", cache.peek(Fqn.fromString("/a/b"), true)); assertNull("/a/b/c should not exist", cache.peek(Fqn.fromString("/a/b/c"), true)); } public void testNodeDeletionRollback3() throws Exception { GlobalTransaction gtx; cache.put("/a/b/c1", null); tx.begin(); gtx = cache.getCurrentTransaction(); cache.put("/a/b/c1", null); assertLocked(gtx, "/a", false); assertLocked(gtx, "/a/b", false); assertLocked(gtx, "/a/b/c1", true); cache.put("/a/b/c2", null); assertLocked(gtx, "/a/b", true); assertLocked(gtx, "/a/b/c2", true); cache.put("/a/b/c3", null); cache.put("/a/b/c1/one", null); assertLocked(gtx, "/a/b/c1", true); assertLocked(gtx, "/a/b/c1/one", true); cache.put("/a/b/c1/two", null); cache.put("/a/b/c1/one/1", null); assertLocked(gtx, "/a/b/c1", true); assertLocked(gtx, "/a/b/c1/one", true); assertLocked(gtx, "/a/b/c1/one/1", true); cache.put("/a/b/c1/two/2/3/4", null); assertLocked(gtx, "/a/b/c1", true); assertLocked(gtx, "/a/b/c1/two", true); assertLocked(gtx, "/a/b/c1/two/2", true); assertLocked(gtx, "/a/b/c1/two/2/3", true); assertLocked(gtx, "/a/b/c1/two/2/3/4", true); cache.removeNode("/a/b"); tx.rollback(); assertTrue(cache.getChildrenNames("/a/b/c1").isEmpty()); Set cn = cache.getChildrenNames(Fqn.fromString("/a/b")); assertEquals(1, cn.size()); assertEquals("c1", cn.iterator().next()); } public void testDoubleLocks() throws Exception { tx.begin(); GlobalTransaction gtx = cache.getCurrentTransaction(); cache.put("/a/b/c", null); cache.put("/a/b/c", null); NodeSPI n = cache.getNode("/a"); assert !lockManager.isLocked(n, READ); // make sure this is write locked. assertLocked(gtx, "/a", true); n = cache.getNode("/a/b"); assert !lockManager.isLocked(n, READ); // make sure this is write locked. assertLocked(gtx, "/a/b", true); n = cache.getNode("/a/b/c"); assert !lockManager.isLocked(n, READ); // make sure this is write locked. assertLocked(gtx, "/a/b/c", true); tx.rollback(); assertEquals(0, cache.getNumberOfLocksHeld()); } private void assertLocked(Object owner, String fqn, boolean writeLocked) { NodeSPI n = cache.peek(Fqn.fromString(fqn), true); if (owner == null) owner = Thread.currentThread(); assertTrue("node " + fqn + " is not locked", lockManager.isLocked(n)); if (writeLocked) { assertTrue("node " + fqn + " is not write-locked by owner " + owner + ". Lock details: " + lockManager.printLockInfo(n), lockManager.ownsLock(Fqn.fromString(fqn), WRITE, owner)); } else { assertTrue("node " + fqn + " is not read-locked by owner " + owner + ". Lock details: " + lockManager.printLockInfo(n), lockManager.ownsLock(Fqn.fromString(fqn), READ, owner)); } } public void testConcurrentNodeAccessOnRemovalWithTx() throws Exception { cache.put("/a/b/c", null); tx.begin(); cache.removeNode("/a/b/c"); // this node should now be locked. TransactionManager tm = cache.getConfiguration().getRuntimeConfig().getTransactionManager(); Transaction t = tm.suspend(); // start a new tx tm.begin(); try { cache.getNode("/a/b/c");// should fail fail("Should not be able to get a hold of /a/b/c until the deleting tx completes"); } catch (Exception e) { // expected //cache.getTransactionManager().commit(); tm.commit(); } tm.resume(t); tx.rollback(); assertNotNull(cache.getNode("/a/b/c")); assertEquals(0, cache.getNumberOfLocksHeld()); } public void testConcurrentNodeAccessOnRemovalWithoutTx() throws Exception { cache.put("/a/b/c", null); tx.begin(); cache.removeNode("/a/b/c"); // this node should now be locked. Transaction t = cache.getTransactionManager().suspend(); Thread th = new Thread() { public void run() { try { cache.getNode("/a/b/c");// should fail fail("Should not be able to get a hold of /a/b/c until the deleting tx completes"); } catch (Exception e) { // expected } } }; th.start(); th.join(); cache.getTransactionManager().resume(t); tx.rollback(); assertNotNull(cache.getNode("/a/b/c")); assertEquals(0, cache.getNumberOfLocksHeld()); } public void testRemove() throws CacheException, SystemException, NotSupportedException, HeuristicMixedException, HeuristicRollbackException, RollbackException { cache.put("/a/b/c", null); cache.put("/a/b/c/1", null); cache.put("/a/b/c/2", null); cache.put("/a/b/c/3", null); cache.put("/a/b/c/3/a/b/c", null); assertEquals(0, cache.getNumberOfLocksHeld()); tx.begin(); cache.removeNode("/a/b/c"); // this used to test for 2 locks held. After the fixes for JBCACHE-875 however, 2 more locks are acquired - for the root node as well as the deleted node. // and since we would lock all children of the deleted node as well, we have 10 locks here. assertEquals(10, cache.getNumberOfLocksHeld()); tx.commit(); assertEquals(0, cache.getNumberOfLocksHeld()); } public void testRemoveAndRollback() throws CacheException, SystemException, NotSupportedException, HeuristicMixedException, HeuristicRollbackException, RollbackException { cache.put("/a/b/c", null); cache.put("/a/b/c/1", null); cache.put("/a/b/c/2", null); cache.put("/a/b/c/3", null); cache.put("/a/b/c/3/a/b/c", null); assertEquals(0, cache.getNumberOfLocksHeld()); tx.begin(); cache.removeNode("/a/b/c"); assertEquals(10, cache.getNumberOfLocksHeld()); tx.rollback(); assertEquals(0, cache.getNumberOfLocksHeld()); assertTrue(cache.exists("/a/b/c")); assertTrue(cache.exists("/a/b/c/1")); assertTrue(cache.exists("/a/b/c/2")); assertTrue(cache.exists("/a/b/c/3")); assertTrue(cache.exists("/a/b/c/3/a")); assertTrue(cache.exists("/a/b/c/3/a/b")); assertTrue(cache.exists("/a/b/c/3/a/b/c")); } public void testRemoveKeyRollback() throws CacheException, SystemException, NotSupportedException { cache.put("/bela/ban", "name", "Bela"); tx.begin(); cache.remove("/bela/ban", "name"); assertNull(cache.get("/bela/ban", "name")); tx.rollback(); assertEquals("Bela", cache.get("/bela/ban", "name")); } public void testRemoveKeyRollback2() { try { Map m = new HashMap(); m.put("name", "Bela"); m.put("id", 322649); cache.put("/bela/ban", m); tx.begin(); cache.remove("/bela/ban", "name"); assertNull(cache.get("/bela/ban", "name")); tx.rollback(); assertEquals("Bela", cache.get("/bela/ban", "name")); } catch (Throwable t) { t.printStackTrace(); fail(t.toString()); } } public void testRemoveKeyRollback3() { try { cache.put("/bela/ban", "name", "Bela"); tx.begin(); cache.put("/bela/ban", "name", "Michelle"); cache.remove("/bela/ban", "name"); assertNull(cache.get("/bela/ban", "name")); tx.rollback(); assertEquals("Bela", cache.get("/bela/ban", "name")); } catch (Throwable t) { t.printStackTrace(); fail(t.toString()); } } public void testDoubleRemovalOfSameData() throws Exception { tx.begin(); cache.put("/foo/1", "item", 1); assertEquals(cache.get("/foo/1", "item"), 1); cache.removeNode("/foo/1"); assertNull(cache.get("/foo/1", "item")); cache.removeNode("/foo/1"); assertNull(cache.get("/foo/1", "item")); tx.rollback(); assertFalse(cache.exists("/foo/1")); assertNull(cache.get("/foo/1", "item")); } /** * put(Fqn, Map) with a previous null map */ public void testPutDataRollback1() { try { cache.put("/bela/ban", null);// create a node /bela/ban with a null map tx.begin(); Map m = new HashMap(); m.put("name", "Bela"); m.put("id", 322649); cache.put("/bela/ban", m); tx.rollback(); Node n = cache.getNode("/bela/ban"); if (n.getData() == null) return; assertEquals("map should be empty", 0, n.getData().size()); } catch (Throwable t) { t.printStackTrace(); fail(t.toString()); } } /** * put(Fqn, Map) with a previous non-null map */ public void testputDataRollback2() throws Exception { Map m1, m2; m1 = new HashMap(); m1.put("name", "Bela"); m1.put("id", 322649); m2 = new HashMap(); m2.put("other", "bla"); m2.put("name", "Michelle"); cache.put("/bela/ban", m1); tx.begin(); cache.put("/bela/ban", m2); Map tmp = cache.getNode("/bela/ban").getData(); assertEquals(3, tmp.size()); assertEquals("Michelle", tmp.get("name")); assertEquals(tmp.get("id"), 322649); assertEquals("bla", tmp.get("other")); tx.rollback(); tmp = cache.getNode("/bela/ban").getData(); assertEquals(2, tmp.size()); assertEquals("Bela", tmp.get("name")); assertEquals(tmp.get("id"), 322649); } public void testPutRollback() { try { cache.put("/bela/ban", null);// /bela/ban needs to exist tx.begin(); cache.put("/bela/ban", "name", "Bela"); assertEquals("Bela", cache.get("/bela/ban", "name")); tx.rollback(); assertNull(cache.get("/bela/ban", "name")); } catch (Throwable t) { t.printStackTrace(); fail(t.toString()); } } public void testPutRollback2() { try { cache.put("/bela/ban", "name", "Bela");// /bela/ban needs to exist tx.begin(); cache.put("/bela/ban", "name", "Michelle"); assertEquals("Michelle", cache.get("/bela/ban", "name")); tx.rollback(); assertEquals("Bela", cache.get("/bela/ban", "name")); } catch (Throwable t) { t.printStackTrace(); fail(t.toString()); } } public void testSimpleRollbackTransactions() throws Exception { final Fqn fqn = Fqn.fromString("/a/b/c"); tx.begin(); cache.put(fqn, "entry", "commit"); tx.commit(); tx.begin(); cache.put(fqn, "entry", "rollback"); cache.removeNode(fqn); tx.rollback(); assertEquals("Node should keep the commited value", "commit", cache.getNode(fqn).get("entry")); tx.begin(); cache.removeNode(fqn); cache.put(fqn, "entry", "rollback"); tx.rollback(); System.out.println("Cache: " + CachePrinter.printCacheDetails(cache)); assertEquals("Node should keep the commited value", "commit", cache.getNode(fqn).get("entry"));// THIS FAILS } private TransactionManager startTransaction() throws Exception { TransactionManager mgr = cache.getConfiguration().getRuntimeConfig().getTransactionManager(); mgr.begin(); return mgr; } public void testConcurrentReadAndWriteAccess() throws Exception { cache.stop(); cache.getConfiguration().setIsolationLevel(IsolationLevel.REPEATABLE_READ); cache.start(); cache.put("/1/2/3/4", "foo", "bar");// no TX, no locks held after put() returns class Reader extends Thread { TransactionManager thread_tx; public Reader() { super("Reader"); } public void run() { try { thread_tx = startTransaction(); log("acquiring RL"); cache.get("/1/2/3", "foo");// acquires RLs on all 3 nodes log("RL acquired successfully"); sleep(2000); log("committing TX"); thread_tx.commit();// releases RLs log("committed TX"); } catch (Exception e) { exception = e; } } } class Writer extends Thread { TransactionManager thread_tx; public Writer() { super("Writer"); } public void run() { try { sleep(500);// give the Reader a chance to acquire the RLs thread_tx = startTransaction(); log("acquiring WL"); cache.put("/1", "foo", "bar2");// needs to acquired a WL on /1 log("acquired WL successfully"); log("committing TX"); thread_tx.commit(); log("committed TX"); } catch (Exception e) { exception = e; } } } Reader reader = new Reader(); Writer writer = new Writer(); reader.start(); writer.start(); reader.join(); writer.join(); if (exception != null) { throw exception; } } public void testRemoveAndGetInTx() throws Exception { Fqn A_B = Fqn.fromString("/a/b"); Fqn A = Fqn.fromString("/a"); cache.put(A_B, "k", "v"); assertTrue(cache.exists(A_B)); assertTrue(cache.exists(A)); cache.getTransactionManager().begin(); cache.removeNode(A); cache.get(A_B, "k"); cache.getTransactionManager().commit(); } public void testRemoveAndPutInTx() throws Exception { Fqn A_B = Fqn.fromString("/a/b"); Fqn A = Fqn.fromString("/a"); cache.put(A_B, "k", "v"); assertTrue(cache.exists(A_B)); assertTrue(cache.exists(A)); cache.getTransactionManager().begin(); cache.removeNode(A_B); cache.put(A_B, "k", "v2"); cache.getTransactionManager().commit(); assertTrue(cache.exists(A_B)); assertTrue(cache.exists(A)); assert cache.peek(A, true, true).isValid(); assert cache.peek(A_B, true, true).isValid(); assertEquals("v2", cache.get(A_B, "k")); } public void testRemoveParentAndPutInTx() throws Exception { Fqn A_B = Fqn.fromString("/a/b"); Fqn A = Fqn.fromString("/a"); cache.put(A_B, "k", "v"); assertTrue(cache.exists(A_B)); assertTrue(cache.exists(A)); cache.getTransactionManager().begin(); cache.removeNode(A); cache.put(A_B, "k", "v2"); cache.getTransactionManager().commit(); assertTrue(cache.exists(A_B)); assertTrue(cache.exists(A)); assertEquals("v2", cache.get(A_B, "k")); } public void testRemoveGrandParentAndPutInTx() throws Exception { Fqn A_B_C = Fqn.fromString("/a/b/c"); Fqn A = Fqn.fromString("/a"); cache.put(A_B_C, "k", "v"); assertTrue(cache.exists(A_B_C)); assertTrue(cache.exists(A)); cache.getTransactionManager().begin(); cache.removeNode(A); cache.put(A_B_C, "k", "v2"); cache.getTransactionManager().commit(); assertTrue(cache.exists(A_B_C)); assertTrue(cache.exists(A)); assertEquals("v2", cache.get(A_B_C, "k")); } public void testRootNodeRemoval() throws Exception { Fqn root = Fqn.ROOT; Fqn fqn = Fqn.fromElements(1); //put first time tx.begin(); this.cache.put(fqn, "k", "v"); tx.commit(); //get works fine tx.begin(); assertEquals("v", this.cache.get(fqn, "k")); tx.commit(); //remove all tx.begin(); this.cache.removeNode(root); tx.commit(); //get returns null - ok //put - endless loop tx.begin(); assertNull(this.cache.get(fqn, "k")); this.cache.put(fqn, "k", "v"); tx.commit(); } public void testNodeAdditionAfterRemoval() throws Exception { Fqn fqn = Fqn.fromString("/1/2/3/4"); //put first time tx.begin(); this.cache.put(fqn, "k", "v"); tx.commit(); //get works fine tx.begin(); assertEquals("v", this.cache.get(fqn, "k")); tx.commit(); //remove all tx.begin(); this.cache.removeNode(Fqn.ROOT); tx.commit(); //get returns null - ok //put - endless loop tx.begin(); assertNull(this.cache.get(fqn, "k")); this.cache.put(fqn, "k", "v"); tx.commit(); } public void testRootNodeRemovalRollback() throws Exception { @SuppressWarnings("unchecked") Fqn root = Fqn.ROOT; Fqn fqn = Fqn.fromRelativeElements(root, 1); //put first time tx.begin(); this.cache.put(fqn, "k", "v"); tx.commit(); //get works fine tx.begin(); assertEquals("v", this.cache.get(fqn, "k")); tx.commit(); //remove all tx.begin(); this.cache.removeNode(root); tx.rollback(); assertEquals("v", this.cache.get(fqn, "k")); } /* public void testConcurrentReadAccess() throws Exception { cache.setIsolationLevel(IsolationLevel.REPEATABLE_READ); cache.setUseMbean(false); cache.put("/1/2/3/4", "foo", "bar"); // no TX, no locks held after put() returns class Reader extends Thread { Transaction thread_tx; public Reader(String name) { super(name); } public void run() { try { thread_tx=startTransaction(); log("acquiring RL"); cache.get("/1/2/3", "foo"); // acquires RLs on all 3 nodes log("RL acquired successfully"); sleep(200000); log("committing TX"); thread_tx.commit(); // releases RLs log("committed TX"); } catch(Exception e) { thread_ex=e; } } } Reader reader=new Reader("R1"); Reader reader2=new Reader("R2"); reader.start(); reader2.start(); reader.join(); reader2.join(); if(thread_ex != null) throw thread_ex; } */ private static void log(String msg) { System.out.println(Thread.currentThread().getName() + ": " + msg); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/transaction/PrepareCommitContentionTest.java0000644000175000017500000001266511017455042033305 0ustar twernertwernerpackage org.jboss.cache.transaction; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.RPCManager; import org.jboss.cache.commands.ReplicableCommand; import org.jboss.cache.commands.remote.ReplicateCommand; import org.jboss.cache.commands.tx.CommitCommand; import org.jboss.cache.commands.tx.PrepareCommand; import static org.jboss.cache.config.Configuration.CacheMode.REPL_SYNC; import org.jboss.cache.factories.ComponentRegistry; import org.jboss.cache.factories.UnitTestCacheConfigurationFactory; import org.jboss.cache.util.TestingUtil; import org.jgroups.Address; import org.jgroups.Channel; import org.jgroups.blocks.RspFilter; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Vector; /** * This is to test the scenario described in http://jira.jboss.org/jira/browse/JBCACHE-1270 *

* i) Node A sends prepare for GTX1; synchronous. Gets applied on Node B. Locks are held on B. * ii) Node A sends commit for GTX1; *asynchronous*. * iii) Node A sends lots of other messages related to other sessions. * iv) Node A sends prepare for GTX2; synchronous. * v) Node B is busy, and by luck the GTX2 prepare gets to UNICAST before the GTX1 commit. * vi) GTX2 prepare blocks due to locks from GTX1. * vii) GTX1 commit is blocked in UNICAST because another thread from Node A is executing. * * @author Manik Surtani (manik@jboss.org) * @since 2.1.0 */ @Test(groups = "functional") public class PrepareCommitContentionTest { CacheSPI c1; @BeforeMethod public void setUp() throws CloneNotSupportedException { c1 = (CacheSPI) new DefaultCacheFactory().createCache(UnitTestCacheConfigurationFactory.createConfiguration(REPL_SYNC)); } @AfterMethod public void tearDown() { TestingUtil.killCaches(c1); } public void testOOBFlag() throws Exception { DelegatingRPCManager delegatingRPCManager = new DelegatingRPCManager(); ComponentRegistry cr = TestingUtil.extractComponentRegistry(c1); RPCManager origRpcManager = cr.getComponent(RPCManager.class); delegatingRPCManager.delegate = origRpcManager; cr.registerComponent(delegatingRPCManager, RPCManager.class); cr.rewire(); c1.getTransactionManager().begin(); c1.put("/a", "k", "v"); c1.getTransactionManager().commit(); // now check what we have gathered: assert delegatingRPCManager.log.get(CommitCommand.class) : "Commit commands should be sent using OOB!"; assert !delegatingRPCManager.log.get(PrepareCommand.class) : "Prepare commands should NOT be sent using OOB!"; } private static class DelegatingRPCManager implements RPCManager { RPCManager delegate; Map, Boolean> log = new HashMap, Boolean>(); public void disconnect() { delegate.disconnect(); } public void stop() { delegate.stop(); } public void start() { delegate.start(); } void logCall(ReplicableCommand command, boolean oob) { if (command instanceof ReplicateCommand) { ReplicableCommand cmd = ((ReplicateCommand) command).getSingleModification(); log.put(cmd.getClass(), oob); } } public List callRemoteMethods(Vector
recipients, ReplicableCommand cacheCommand, int mode, long timeout, RspFilter responseFilter, boolean useOutOfBandMessage) throws Exception { logCall(cacheCommand, useOutOfBandMessage); return delegate.callRemoteMethods(recipients, cacheCommand, mode, timeout, responseFilter, useOutOfBandMessage); } public List callRemoteMethods(Vector
recipients, ReplicableCommand cacheCommand, int mode, long timeout, boolean useOutOfBandMessage) throws Exception { logCall(cacheCommand, useOutOfBandMessage); return delegate.callRemoteMethods(recipients, cacheCommand, mode, timeout, useOutOfBandMessage); } public List callRemoteMethods(Vector
recipients, ReplicableCommand command, boolean synchronous, long timeout, boolean useOutOfBandMessage) throws Exception { logCall(command, useOutOfBandMessage); return delegate.callRemoteMethods(recipients, command, synchronous, timeout, useOutOfBandMessage); } public boolean isCoordinator() { return delegate.isCoordinator(); } public Address getCoordinator() { return delegate.getCoordinator(); } public Address getLocalAddress() { return delegate.getLocalAddress(); } public List
getMembers() { return delegate.getMembers(); } public void fetchPartialState(List
sources, Fqn sourceTarget, Fqn integrationTarget) throws Exception { delegate.fetchPartialState(sources, sourceTarget, integrationTarget); } public void fetchPartialState(List
sources, Fqn subtree) throws Exception { delegate.fetchPartialState(sources, subtree); } public Channel getChannel() { return delegate.getChannel(); } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/transaction/AsyncRollbackTxTest.java0000644000175000017500000001476310732232543031543 0ustar twernertwernerpackage org.jboss.cache.transaction; import org.jboss.cache.CacheFactory; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.config.Configuration; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.SystemException; import javax.transaction.TransactionManager; /** * Test behaviour of async rollback timeouted transaction * * @author Jacek Halat * @since 1.4.0 */ @Test(groups = {"functional"}) public class AsyncRollbackTxTest { private CacheSPI cache; private TransactionManager tm; private Fqn fqn = Fqn.fromString("/test"); // This sleep time (millis) should be longer than the transaction timeout time. private long sleepTime = 2500; // seconds private int txTimeout = 2; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { Configuration c = new Configuration(); c.setTransactionManagerLookupClass("org.jboss.cache.transaction.AsyncRollbackTransactionManagerLookup"); CacheFactory instance = new DefaultCacheFactory(); cache = (CacheSPI) instance.createCache(c); tm = cache.getConfiguration().getRuntimeConfig().getTransactionManager(); tm.setTransactionTimeout(txTimeout); } @AfterMethod(alwaysRun = true) public void tearDown() { try { if (tm != null && tm.getTransaction() != null) { try { tm.rollback(); } catch (SystemException e) { // do nothing } } } catch (SystemException e) { // do nothing } if (cache != null) cache.stop(); cache = null; tm = null; } public void testCommitCreationInSameTx() throws Exception { assertEquals(0, cache.getNumberOfLocksHeld()); tm.begin(); cache.put(fqn, "k", "v"); assertEquals(2, cache.getNumberOfLocksHeld()); Thread.sleep(sleepTime); tm.commit(); assertEquals(0, cache.getNumberOfLocksHeld()); } public void testRollbackCreationInSameTx() throws Exception { assertEquals(0, cache.getNumberOfLocksHeld()); tm.begin(); cache.put(fqn, "k", "v"); assertEquals(2, cache.getNumberOfLocksHeld()); Thread.sleep(sleepTime); tm.rollback(); assertEquals(0, cache.getNumberOfLocksHeld()); // make sure the node was NOT added!! assertFalse(cache.exists(fqn)); // even in a "deleted" form assertNull(cache.peek(fqn, true)); } private void doTest(boolean commit, boolean writeLock) throws Exception { assertEquals(0, cache.getNumberOfLocksHeld()); cache.put(fqn, "k", "v");//Executed in Not transactional context assertEquals(0, cache.getNumberOfLocksHeld()); SeparateThread t = new SeparateThread(commit, writeLock); t.start(); t.join(); if (t.getException() != null) { throw t.getException(); } assertEquals(0, cache.getNumberOfLocksHeld()); assertEquals("v", cache.get(fqn, "k")); } public void testRollbackCreationInDifferentTxReadLock() throws Exception { doTest(false, false); } public void testCommitCreationInDifferentTxReadLock() throws Exception { doTest(true, false); } public void testRollbackCreationInDifferentTxWriteLock() throws Exception { doTest(false, true); } public void testCommitCreationInDifferentTxWriteLock() throws Exception { doTest(true, true); } public void testTxTimeoutAndPutAfter() throws Exception { assertEquals(0, cache.getNumberOfLocksHeld()); tm.begin(); cache.put(fqn, "k", "v"); assertEquals(2, cache.getNumberOfLocksHeld()); assertNotNull(tm.getTransaction()); Thread.sleep(sleepTime); tm.rollback(); assertNull(tm.getTransaction()); assertEquals(0, cache.getNumberOfLocksHeld()); // make sure the node was NOT added!! assertFalse(cache.exists(fqn)); // even in a "deleted" form assertNull(cache.peek(fqn, true)); //Put not some data into cache. Because no transaction is used //no locks should be helds after this line. cache.put(fqn, "k", "v"); assertEquals(0, cache.getNumberOfLocksHeld()); } public void testTxTimeoutAndPutGetAfter() throws Exception { assertEquals(0, cache.getNumberOfLocksHeld()); tm.begin(); cache.put(fqn, "k", "v"); assertEquals(2, cache.getNumberOfLocksHeld()); assertNotNull(tm.getTransaction()); Thread.sleep(sleepTime); tm.rollback(); // make sure the node was NOT added!! assertFalse(cache.exists(fqn)); // even in a "deleted" form assertNull(cache.peek(fqn, true)); assertNull(tm.getTransaction()); assertEquals(0, cache.getNumberOfLocksHeld()); //Put not some data into cache. Because no transaction is used //no locks should be helds after this line. cache.put(fqn, "k", "v"); cache.get(fqn, "k"); // Make sure no write lock is retained by the main thread. Test that another thread can read. SeparateThread t = new SeparateThread(false, false); t.start(); t.join(); if (t.getException() != null) { throw t.getException(); } } private class SeparateThread extends Thread { Exception e = null; boolean commit, writeLock; public SeparateThread(boolean commit, boolean writeLock) { this.commit = commit; this.writeLock = writeLock; } public Exception getException() { return e; } public void run() { try { tm.begin(); if (writeLock) { cache.put(fqn, "k", "v2");// obtain write lock on node } else { cache.get(fqn, "k");// obtain read lock on node } // sleep to ensure tx times out. sleep(2500); if (commit) { tm.commit(); } else { tm.rollback(); } assertEquals(0, cache.getNumberOfLocksHeld()); } catch (Exception e) { this.e = e; } } } } ././@LongLink0000000000000000000000000000014500000000000011565 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/transaction/InvocationContextCleanupTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/transaction/InvocationContextCleanupTest.java0000644000175000017500000001075211017455042033456 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.transaction; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.config.Configuration; import org.jboss.cache.interceptors.OrderedSynchronizationHandler; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.util.CachePrinter; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertTrue; import org.testng.annotations.AfterMethod; import org.testng.annotations.Test; import javax.transaction.Synchronization; import javax.transaction.SystemException; import javax.transaction.Transaction; import javax.transaction.TransactionManager; /** * Tests cleaning of invocation contexts on completion of txs * * @author Manik Surtani (manik@jboss.org) */ @Test(groups = {"functional", "transaction"}) public class InvocationContextCleanupTest { private CacheSPI[] caches; private CacheSPI createCache(boolean optimistic) { CacheSPI cache = (CacheSPI) new DefaultCacheFactory().createCache(false); cache.getConfiguration().setCacheMode(Configuration.CacheMode.REPL_SYNC); if (optimistic) cache.getConfiguration().setNodeLockingScheme("OPTIMISTIC"); cache.getConfiguration().setClusterName("InvocationContextCleanupTest"); cache.getConfiguration().setTransactionManagerLookupClass(TransactionSetup.getManagerLookup()); cache.getConfiguration().setLockAcquisitionTimeout(2000); cache.start(); return cache; } @AfterMethod(alwaysRun = true) public void tearDown() { if (caches != null) { for (int i = 0; i < caches.length; i++) { if (caches[i] != null) { caches[i].stop(); caches[i] = null; } } caches = null; } } public void testInvocationContextCleanupPessimistic() throws Exception { test2CachesSync(false); } private void test2CachesSync(boolean optimistic) throws Exception { caches = new CacheSPI[2]; CacheSPI cache0 = createCache(optimistic); CacheSPI cache1 = createCache(optimistic); caches[0] = cache0; caches[1] = cache1; TestingUtil.blockUntilViewsReceived(caches, 2000); TransactionManager mgr = caches[0].getTransactionManager(); mgr.begin(); cache0.put("/test", "x", "y"); GlobalTransaction gtx = cache0.getTransactionTable().get(mgr.getTransaction()); OrderedSynchronizationHandler orderedHandler = cache0.getTransactionTable().get(gtx).getOrderedSynchronizationHandler(); // OrderedSynchronizationHandler orderedHandler = OrderedSynchronizationHandler.getInstance(mgr.getTransaction()); orderedHandler.registerAtTail(new DummySynchronization(cache0, mgr)); try { mgr.commit(); } finally { } System.out.println(CachePrinter.printCacheLockingInfo(caches[0])); System.out.println(CachePrinter.printCacheLockingInfo(caches[1])); assertEquals("y", cache0.get("/test", "x")); assertEquals("y", cache0.get("/test", "x")); } public static class DummySynchronization implements Synchronization { private CacheSPI cache; private TransactionManager mgr; public DummySynchronization(CacheSPI cache, TransactionManager mgr) { this.cache = cache; this.mgr = mgr; } public void beforeCompletion() { // before returning, do a put (non-tx) on the cache!! Transaction tx = null; try { tx = mgr.suspend(); } catch (SystemException e) { throw new RuntimeException("Unable to sustend transaction! " + e.getMessage()); } try { cache.put("/test", "blah", "blahblah"); assertTrue("Should fail with a lock exception!", false); } catch (Exception e) { assertTrue("Should fail!", true); } finally { if (tx != null) { try { mgr.resume(tx); } catch (Exception e) { } } } } public void afterCompletion(int i) { // do nothing } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/transaction/IsolationLevelNoneTest.java0000755000175000017500000000736510732232543032254 0ustar twernertwernerpackage org.jboss.cache.transaction; import org.jboss.cache.CacheFactory; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.Node; import org.jboss.cache.config.Configuration; import org.jboss.cache.lock.IsolationLevel; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertTrue; import org.testng.annotations.AfterMethod; import org.testng.annotations.Test; import javax.transaction.NotSupportedException; import javax.transaction.SystemException; import javax.transaction.TransactionManager; /** * Tests whether modifications within callbacks (TreeCacheListener) are handled correctly * * @author Bela Ban * @version $Id: IsolationLevelNoneTest.java 4880 2007-12-19 15:14:43Z manik.surtani@jboss.com $ */ @Test(groups = {"functional", "transaction"}) public class IsolationLevelNoneTest { CacheSPI cache = null; final Fqn FQN = Fqn.fromString("/a/b/c"); final String KEY = "key"; final String VALUE = "value"; TransactionManager tm; @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { if (cache != null) { cache.stop(); cache.destroy(); cache = null; } } public void testWithoutTransactions() throws Exception { CacheFactory instance = new DefaultCacheFactory(); cache = (CacheSPI) instance.createCache(false); cache.getConfiguration().setCacheMode(Configuration.CacheMode.LOCAL); cache.getConfiguration().setIsolationLevel(IsolationLevel.NONE); cache.start(); cache.put(FQN, KEY, VALUE); cache.put(FQN + "/d", KEY, VALUE); Node node = cache.peek(FQN, false, false); assertTrue(node != null && node.getKeys().contains(KEY)); assertEquals(VALUE, cache.get(FQN, KEY)); assertEquals(0, cache.getNumberOfLocksHeld()); } public void testWithTransactions() throws Exception { CacheFactory instance = new DefaultCacheFactory(); cache = (CacheSPI) instance.createCache(false); cache.getConfiguration().setCacheMode(Configuration.CacheMode.LOCAL); cache.getConfiguration().setIsolationLevel(IsolationLevel.NONE); cache.getConfiguration().setTransactionManagerLookupClass(TransactionSetup.getManagerLookup()); cache.start(); tm = startTransaction(); cache.put(FQN, KEY, VALUE); cache.put(FQN + "/d", KEY, VALUE); Node node = cache.peek(FQN, false, false); assertTrue(node != null && node.getKeys().contains(KEY)); assertEquals(VALUE, cache.get(FQN, KEY)); assertEquals(0, cache.getNumberOfLocksHeld()); tm.commit(); } public void testWithTransactionsRepeatableRead() throws Exception { CacheFactory instance = new DefaultCacheFactory(); cache = (CacheSPI) instance.createCache(false); cache.getConfiguration().setCacheMode(Configuration.CacheMode.LOCAL); cache.getConfiguration().setIsolationLevel(IsolationLevel.REPEATABLE_READ); cache.getConfiguration().setTransactionManagerLookupClass(TransactionSetup.getManagerLookup()); cache.start(); tm = startTransaction(); cache.put(FQN, KEY, VALUE); cache.put(FQN + "/d", KEY, VALUE); Node node = cache.peek(FQN, false, false); assertTrue(node != null && node.getKeys().contains(KEY)); assertEquals(VALUE, cache.get(FQN, KEY)); assertEquals(5, cache.getNumberOfLocksHeld()); tm.commit(); } private TransactionManager startTransaction() throws SystemException, NotSupportedException { TransactionManager mgr = cache.getTransactionManager(); mgr.begin(); return mgr; } } ././@LongLink0000000000000000000000000000015000000000000011561 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/transaction/IsolationLevelReadCommittedTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/transaction/IsolationLevelReadCommittedTest.j0000644000175000017500000002312510732752176033404 0ustar twernertwernerpackage org.jboss.cache.transaction; import org.jboss.cache.Cache; import org.jboss.cache.CacheFactory; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.lock.IsolationLevel; import org.jboss.cache.lock.TimeoutException; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.NotSupportedException; import javax.transaction.SystemException; import javax.transaction.TransactionManager; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; /** * Tests READ_COMMITED isolation level. * * @author Ovidiu Feodorov * @version $Id: IsolationLevelReadCommittedTest.java 4912 2007-12-21 14:58:06Z manik.surtani@jboss.com $ */ @Test(groups = {"functional", "transaction"}) public class IsolationLevelReadCommittedTest { private Cache cache = null; private final Fqn FQN = Fqn.fromString("/a/b/c"); private final Fqn PARENT_FQN = FQN.getParent(); private final String KEY = "key"; private final String VALUE = "value"; private volatile boolean writerFailed; private volatile boolean readerFailed; private volatile AssertionError writerError; private volatile AssertionError readerError; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { writerFailed = false; readerFailed = false; writerError = null; readerError = null; Configuration config = new Configuration(); config.setCacheMode(CacheMode.LOCAL); config.setIsolationLevel(IsolationLevel.READ_COMMITTED); config.setLockAcquisitionTimeout(1000); config.setTransactionManagerLookupClass(TransactionSetup.getManagerLookup()); CacheFactory instance = new DefaultCacheFactory(); cache = instance.createCache(config); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { cache.stop(); cache.destroy(); cache = null; } /** * The test starts a reader and writer thread and coordinates access to the cache so the reader * reads after the writer changes a node in a transaction, but before roll back. The reader * should never read anything else than the original value (uncommitted values should be * isolated) */ public void testReadCommitted() throws Exception { final CountDownLatch readerCanRead = new CountDownLatch(1); final CountDownLatch readerDone = new CountDownLatch(1); final CountDownLatch writerCanWrite = new CountDownLatch(1); final CountDownLatch writerCanRollback = new CountDownLatch(1); final CountDownLatch writerDone = new CountDownLatch(1); cache.put(FQN, KEY, VALUE); assertEquals(VALUE, cache.get(FQN, KEY)); // start a reader thread; need a transaction so its initial // read lock is maintained while the writer does its work Thread readerThread = new Thread(new Runnable() { public void run() { TransactionManager tm = null; try { tm = startTransaction(); // Read the value, thus acquiring a read lock on FQN assertEquals("Could not read node with expected value!", VALUE, cache.get(FQN, KEY)); writerCanWrite.countDown(); // wait until the writer thread changes the value in a transaction, but it did not // yet commit or roll back. readerCanRead.await(); try { // I shouldn't be able to see the "dirty" value assertEquals("thread w/ read lock can see subsequent uncommitted changes!!", VALUE, cache.get(FQN, KEY)); } catch (TimeoutException good) { // this is what should happen due to writer's WL } // let the writer know it can rollback writerCanRollback.countDown(); // I should still be able to see the "clean" value assertEquals("Could not read node with expected value!", VALUE, cache.get(FQN, KEY)); } catch (AssertionError e) { readerError = e; } catch (Throwable t) { t.printStackTrace(); readerFailed = true; } finally { System.out.println("reader thread exits"); if (tm != null) { try { tm.commit(); } catch (Exception e) { } } writerCanWrite.countDown(); writerCanRollback.countDown(); readerDone.countDown(); } } }, "READER"); readerThread.start(); // start a writer thread and a transaction Thread writerThread = new Thread(new Runnable() { public void run() { try { // wait until the reader thread reads and allows me to start writerCanWrite.await(3, TimeUnit.SECONDS); TransactionManager tm = startTransaction(); // change VALUE in a transaction cache.put(FQN, KEY, "this-shouldnt-be-visible"); // notify the reading thread readerCanRead.countDown(); // wait until the reader thread reads and allows me to rollback or until I timeout writerCanWrite.await(3, TimeUnit.SECONDS); System.out.println("rolling back"); tm.rollback(); } catch (AssertionError e) { writerError = e; } catch (Throwable t) { t.printStackTrace(); writerFailed = true; } finally { System.out.println("writer thread exits"); readerCanRead.countDown(); writerDone.countDown(); } } }, "WRITER"); writerThread.start(); // wait for both threads to finish readerDone.await(); writerDone.await(); // If any assertion failed, throw on the AssertionFailedError if (readerError != null) { throw readerError; } if (writerError != null) { throw writerError; } if (readerFailed) { fail("The reader thread exited incorrectly. Watch the log for previous stack traces"); } if (writerFailed) { fail("The writer thread exited incorrectly. Watch the log for previous stack traces"); } } /** * Test creates a cache node then starts a separate thread that removes * the node inside a tx. Test confirms that the removal cannot be seen * before the test commits. * * @throws Exception */ public void testNodeRemoved() throws Exception { final CountDownLatch readerCanRead = new CountDownLatch(1); final CountDownLatch readerDone = new CountDownLatch(1); final CountDownLatch writerDone = new CountDownLatch(1); cache.put(FQN, KEY, VALUE); assertEquals(VALUE, cache.get(FQN, KEY)); // start a writer thread and a transaction Thread writerThread = new Thread(new Runnable() { public void run() { try { TransactionManager mgr = startTransaction(); // change VALUE in a transaction cache.removeNode(PARENT_FQN); // notify the reading thread readerCanRead.countDown(); readerDone.await(); mgr.commit(); } catch (AssertionError e) { writerError = e; } catch (Throwable t) { t.printStackTrace(); writerFailed = true; } finally { System.out.println("writer thread exits"); readerCanRead.countDown(); writerDone.countDown(); } } }, "WRITER"); writerThread.start(); try { // wait until the writer thread changes the value in a transaction, // but it did not yet commit or roll back. readerCanRead.await(); // I shouldn't be able to see the "dirty" value assertEquals("2nd thread cannot see uncommitted changes", VALUE, cache.get(FQN, KEY)); } catch (TimeoutException t) { // ignore, this is good } finally { System.out.println("reader thread exits"); readerDone.countDown(); } // wait for the writer to finish writerDone.await(); assertNull("Node was removed", cache.getNode(FQN)); // If any assertion failed, throw on the AssertionFailedError if (writerError != null) { throw writerError; } if (writerFailed) { fail("The writer thread exited incorrectly. Watch the log for previous stack traces"); } } private TransactionManager startTransaction() throws SystemException, NotSupportedException { TransactionManager mgr = cache.getConfiguration().getRuntimeConfig().getTransactionManager(); mgr.begin(); return mgr; } } ././@LongLink0000000000000000000000000000015200000000000011563 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/transaction/ReplicatedTransactionDeadlockTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/transaction/ReplicatedTransactionDeadlockTest0000644000175000017500000002004010777645574033477 0ustar twernertwernerpackage org.jboss.cache.transaction; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.CacheFactory; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.factories.UnitTestCacheConfigurationFactory; import static org.testng.AssertJUnit.fail; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.UserTransaction; /** * A test created to simulate an unexpected TimeoutException in JBossCache * 1.3.0SP1 (not relevant for 1.2.4 and earlier releases). The error has been * initially observed in a production environment, the test has been created to * simplify the analysis without the complexity of hundreds concurrent * transactions.
* This test is relevant for REPL_SYNC mode, (default) isolation level * REPEATABLE_READ and SyncCommitPhase.
* The error scenario is: *
    *
  • Two caches: SrcCache is the one where we put the modifications, DstCache * only receives the replicated modifications.
  • *
  • Two threads (resp. transactions) A and B modifying the same node X * concurrently on SrcCache.
  • *
  • Let's assume the transaction A is faster in aquiring the lock on X.
  • *
  • Transaction A modifies the node X and starts committing: local prepare, * remote prepare, local commit. The remote prepare will lock the X on DstCache * and it will stay locked until the remote commit releases it later. Note that * in JBossCache 1.3.0SP1 the transaction will release the lock on SrcCache * immediately after the local commit step and before the remote (sync.) commit. * It seems that this have changed between 1.2.4 and 1.3.0 releases.
  • *
  • As soon as the lock on SrcCache is released by A, transaction B aquires * the lock immediately and starts local modifications.Note that in some * cases B is fast enough to do the local prepare and send a remote prepare * message before the remote commit message of A. B is able to do this * because the lock is released by A before its remote commit call.
  • *
  • Now, we have the X locked by A on DstCache waiting for the remote commit * of A to release it. The next messages from SrcCache are in the following * order coming up the JGROUPS stack of the DstCache: remote prepare for B, * remote commit for A.
  • *
  • The remote prepare of B blocks on DstCache, trying to acquire the lock * still held by A.
  • *
  • The remote commit of A waits in the UP queue of the STATE_TRANSFER, * waiting for the previous message (which is the remote prepare of B) to be * processed.
  • *
  • So A cannot be committed because it's blocked by B, which cannot be * prepared, because it's blocked by A, which cannot be committed. Of course the * result is a TimeoutException and too many rolled back transactions.
  • *
* * @author Marian Nikolov * @author $Author: manik.surtani@jboss.com $ * @version $Date: 2008-04-11 13:29:00 +0200 (Fr, 11. Apr 2008) $ */ @Test(groups = {"functional", "transaction"}) public class ReplicatedTransactionDeadlockTest { // Number of worker threads private static final int NUM_WORKERS = 2; // The number of test runs to perform. private static final int NUM_RUNS = 100; // useful to increase this to get thread dumps, etc. Typically should be set to 10,000 though. private static final long LOCK_ACQUISITION_TIMEOUT = 10000; /** * Exception recorded if any of the worker threads fails. */ private static volatile Exception exception = null; /** * The source cache where we put modifications. */ private CacheSPI srcCache = null; /** * The target cache where we replicate modifications. */ private CacheSPI dstCache = null; private Log log = LogFactory.getLog(ReplicatedTransactionDeadlockTest.class); /** * Constructor. * * @param name The test name. */ /** * {@inheritDoc} */ @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { exception = null; CacheFactory instance = new DefaultCacheFactory(); // setup and start the source cache srcCache = (CacheSPI) instance.createCache(UnitTestCacheConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC), false); srcCache.getConfiguration().setTransactionManagerLookupClass(TransactionSetup.getManagerLookup()); srcCache.getConfiguration().setCacheMode(Configuration.CacheMode.REPL_SYNC); srcCache.getConfiguration().setSyncCommitPhase(true); srcCache.getConfiguration().setLockAcquisitionTimeout(LOCK_ACQUISITION_TIMEOUT); srcCache.create(); srcCache.start(); // setup and start the destination cache dstCache = (CacheSPI) instance.createCache(UnitTestCacheConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC), false); dstCache.getConfiguration().setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); dstCache.getConfiguration().setCacheMode(Configuration.CacheMode.REPL_SYNC); dstCache.getConfiguration().setSyncCommitPhase(true); dstCache.getConfiguration().setLockAcquisitionTimeout(LOCK_ACQUISITION_TIMEOUT); dstCache.create(); dstCache.start(); } /** * {@inheritDoc} */ @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { srcCache.stop(); srcCache = null; dstCache.stop(); dstCache = null; TransactionSetup.cleanup(); } /** * Test for a synchronously replicated cache with concurrent transactions on * the same node.
* This test fails very often with a TimeoutException. * * @throws Exception Any exception if thrown by the cache. */ public void testConcurrentReplicatedTransaction() throws Exception { performTest(); } /** * Perform a single test, using the pre-configured cache. * * @throws Exception Any exception if thrown by the cache. */ private void performTest() throws Exception { // repeat the test several times since it's not always reproducible for (int i = 0; i < NUM_RUNS; i++) { if (exception != null) { // terminate the test on the first failed worker fail("Due to an exception: " + exception); } // start several worker threads to work with the same FQN Worker[] t = new Worker[NUM_WORKERS]; for (int j = 0; j < t.length; j++) { t[j] = new Worker("worker " + i + ":" + j); t[j].start(); } // wait for all workers to complete before repeating the test for (Worker aT : t) aT.join(); } } /** * Returns a user transaction to be associated with the calling thread. * * @return A user transaction. * @throws Exception Any exception thrown by the context lookup. */ private UserTransaction getTransaction() { return TransactionSetup.getUserTransaction(); } /** * A worker thread that applies the concurrent modifications. * * @author Marian Nikolov * @author $Author: manik.surtani@jboss.com $ * @version $Date: 2008-04-11 13:29:00 +0200 (Fr, 11. Apr 2008) $ */ private class Worker extends Thread { /** * Constructor. */ public Worker(String name) { super(name); } /** * {@inheritDoc} */ public void run() { try { UserTransaction tx = getTransaction(); log.warn("begin"); tx.begin(); log.warn("put"); srcCache.put("/Node", Boolean.FALSE, Boolean.TRUE); log.warn("commit"); tx.commit(); log.warn("leave"); } catch (Exception e) { log.error("caught exception " + e, e); exception = e; } } } } ././@LongLink0000000000000000000000000000015600000000000011567 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/transaction/AsyncRollbackTransactionManagerLookup.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/transaction/AsyncRollbackTransactionManagerLo0000644000175000017500000000047410562447404033442 0ustar twernertwernerpackage org.jboss.cache.transaction; import javax.transaction.TransactionManager; public class AsyncRollbackTransactionManagerLookup implements TransactionManagerLookup { public TransactionManager getTransactionManager() throws Exception { return AsyncRollbackTransactionManager.getInstance(); } }././@LongLink0000000000000000000000000000015000000000000011561 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/transaction/AsyncRollbackTransactionManager.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/transaction/AsyncRollbackTransactionManager.j0000644000175000017500000001443711004124107033363 0ustar twernertwernerpackage org.jboss.cache.transaction; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.transaction.HeuristicMixedException; import javax.transaction.HeuristicRollbackException; import javax.transaction.InvalidTransactionException; import javax.transaction.NotSupportedException; import javax.transaction.RollbackException; import javax.transaction.SystemException; import javax.transaction.Transaction; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Properties; public class AsyncRollbackTransactionManager extends DummyTransactionManager { private static final long serialVersionUID = 5793952292960075970L; static AsyncRollbackTransactionManager instance = null; private static Log log = LogFactory.getLog(AsyncRollbackTransactionManager.class); public static DummyTransactionManager getInstance() { if (instance == null) { instance = new AsyncRollbackTransactionManager(); try { Properties p = new Properties(); p.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.cache.transaction.DummyContextFactory"); Context ctx = new InitialContext(p); ctx.bind("java:/TransactionManager", instance); ctx.bind("UserTransaction", new DummyUserTransaction(instance)); } catch (NamingException e) { log.error("binding of DummyTransactionManager failed", e); } } return instance; } private Thread timedOutTransactionsChecker = null; private int timeout = 30; private Map txMap = new HashMap(); public void setTransactionTimeout(int seconds) throws SystemException { this.timeout = seconds; } public AsyncRollbackTransactionManager() { timedOutTransactionsChecker = new TimedOutTransactionsChecker(); timedOutTransactionsChecker.start(); } private class TimedOutTransactionsChecker extends Thread { private boolean running; public TimedOutTransactionsChecker() { } public void run() { running = true; while (running) { try { Thread.sleep(500); synchronized (this) { Iterator iterator = txMap.values().iterator(); do { if (!iterator.hasNext()) { break; } AsyncRollbackTransaction t = iterator.next(); try { t.wakeUp(); } catch (SystemException e) { e.printStackTrace(); } } while (true); } } catch (InterruptedException e) { } } } } public void begin() throws NotSupportedException, SystemException { Transaction currentTx; if ((currentTx = getTransaction()) != null) { throw new NotSupportedException(Thread.currentThread() + " is already associated with a transaction (" + currentTx + ")"); } AsyncRollbackTransaction tx = new AsyncRollbackTransaction(this, timeout); setTransaction(tx); txMap.put(tx.generateTransactionId(), tx); } public void rollback() throws IllegalStateException, SecurityException, SystemException { removeTxFromMap((AsyncRollbackTransaction) getTransaction()); super.rollback(); } public void removeTxFromMap(AsyncRollbackTransaction tx) throws SystemException { if (tx != null) { txMap.remove(tx.getTransactionId()); } } public void commit() throws RollbackException, HeuristicMixedException, HeuristicRollbackException, SecurityException, IllegalStateException, SystemException { AsyncRollbackTransaction tx = (AsyncRollbackTransaction) getTransaction(); if (tx != null) { txMap.remove(tx.getTransactionId()); } super.commit(); } public void resume(Transaction tx) throws InvalidTransactionException, IllegalStateException, SystemException { super.resume(tx); } public Transaction suspend() throws SystemException { return super.suspend(); } static class AsyncRollbackTransaction extends DummyTransaction { private static long transactionNums = 0; private long transactionId; private long beginTimeMillis; private int timeoutSec; public AsyncRollbackTransaction(DummyBaseTransactionManager tm, int timeout) { super(tm); this.timeoutSec = timeout; this.beginTimeMillis = System.currentTimeMillis(); } /** * @return the transactionId */ public long getTransactionId() { return transactionId; } public long generateTransactionId() { long result = 0; synchronized (AsyncRollbackTransaction.class) { transactionNums++; result = transactionNums; } this.transactionId = result; return result; } final int getTimeoutSeconds() { return timeoutSec; } protected final void asyncRollback() throws SystemException { Thread asyncRollbackThread = new Thread() { public void run() { try { rollback(); } catch (Exception exception) { } } }; ((AsyncRollbackTransactionManager) tm_).removeTxFromMap(this); asyncRollbackThread.start(); } public void wakeUp() throws SystemException { if (isTransactionTimedOut()) { asyncRollback(); } } private boolean isTransactionTimedOut() { return (System.currentTimeMillis() - beginTimeMillis) > (timeoutSec * 1000); } } }jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/transaction/NotifyingTransactionManager.java0000644000175000017500000000430210764272723033304 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.transaction; import org.jboss.cache.CacheSPI; import javax.transaction.HeuristicMixedException; import javax.transaction.HeuristicRollbackException; import javax.transaction.RollbackException; import javax.transaction.SystemException; import javax.transaction.Transaction; import javax.transaction.TransactionManager; /** * A dummy transaction manager that notifies registered listeners of the various phases of a 2PC so exceptions, etc. can be injected. * * @author Manik Surtani (manik@jboss.org) */ public class NotifyingTransactionManager extends DummyTransactionManager implements TransactionManagerLookup { private static final long serialVersionUID = -2994163352889758708L; private Notification notification; private CacheSPI cache; @Override public void commit() throws HeuristicMixedException, SystemException, HeuristicRollbackException, RollbackException { notifyListeners(); super.commit(); } @Override public void rollback() throws SystemException { notifyListeners(); super.rollback(); } private void notifyListeners() { try { log.debug("Calling notification.notify()"); TransactionTable txTable = cache.getTransactionTable(); Transaction tx = getTransaction(); GlobalTransaction gtx = txTable.get(tx); notification.notify(tx, txTable.get(gtx)); } catch (Exception e) { log.debug(e); } } public TransactionManager getTransactionManager() throws Exception { return this; } public interface Notification { public void notify(Transaction tx, TransactionEntry entry) throws SystemException, RollbackException; } public CacheSPI getCache() { return cache; } public void setCache(CacheSPI cache) { this.cache = cache; } public Notification getNotification() { return notification; } public void setNotification(Notification notification) { this.notification = notification; } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/transaction/isolationlevels/0000755000175000017500000000000011376173747030215 5ustar twernertwerner././@LongLink0000000000000000000000000000015700000000000011570 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/transaction/isolationlevels/IsolationLevelTestBase.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/transaction/isolationlevels/IsolationLevelTes0000644000175000017500000002365210732232543033536 0ustar twernertwernerpackage org.jboss.cache.transaction.isolationlevels; import org.jboss.cache.Cache; import org.jboss.cache.CacheFactory; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.lock.IsolationLevel; import static org.jboss.cache.lock.IsolationLevel.*; import org.jboss.cache.transaction.TransactionSetup; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.Transaction; import javax.transaction.TransactionManager; import java.util.Collection; import java.util.HashSet; /** * Base class for testing isolation levels. * * @author Manik Surtani * @since 2.0.0 */ @Test(groups = {"functional", "transaction"}) public abstract class IsolationLevelTestBase { protected IsolationLevel isolationLevel; protected Cache cache; protected TransactionManager transactionManager; protected Fqn fqn = Fqn.fromString("/a/b/c"); protected Fqn fqnChild1 = Fqn.fromString("/a/b/c/child1"); protected Fqn fqnChild2 = Fqn.fromString("/a/b/c/child2"); protected String k = "key", v = "value"; protected Collection allowedLevels; @BeforeMethod(alwaysRun = true) public void setUp() { CacheFactory cf = new DefaultCacheFactory(); cache = cf.createCache(false); cache.getConfiguration().setIsolationLevel(isolationLevel); cache.getConfiguration().setTransactionManagerLookupClass(TransactionSetup.getManagerLookup()); // very short so the tests don't take ages cache.getConfiguration().setLockAcquisitionTimeout(250); cache.start(); transactionManager = cache.getConfiguration().getRuntimeConfig().getTransactionManager(); allowedLevels = new HashSet(); } @AfterMethod(alwaysRun = true) public void tearDown() { if (transactionManager != null) { // roll back any ongoing, potentially stuck transactions from failed tests. try { transactionManager.rollback(); } catch (Exception e) { // no-op } } cache.stop(); cache.destroy(); cache = null; allowedLevels = null; } public void testDirtyRead() throws Exception { // should be allowed in all cases except R_C, R_R and Serializable allowedLevels.add(NONE); allowedLevels.add(READ_UNCOMMITTED); // do a write transactionManager.begin(); cache.put(fqn, k, v); Transaction t1 = transactionManager.suspend(); // and now a simultaneous read transactionManager.begin(); try { assertEquals(v, cache.get(fqn, k)); transactionManager.commit(); if (!allowedLevels.contains(isolationLevel)) { fail("Should have thrown an exception"); } } catch (Exception e) { transactionManager.rollback(); if (allowedLevels.contains(isolationLevel)) { throw e; } } transactionManager.resume(t1); transactionManager.rollback(); } public void testDirtyReadWithNoData() throws Exception { // should be allowed in all cases except Serializable allowedLevels.add(NONE); allowedLevels.add(READ_UNCOMMITTED); allowedLevels.add(READ_COMMITTED); allowedLevels.add(REPEATABLE_READ); // do a write transactionManager.begin(); assertNull(cache.get(fqn, k)); Transaction t1 = transactionManager.suspend(); // and now a simultaneous read transactionManager.begin(); try { cache.put(fqn, k, v); transactionManager.commit(); if (!allowedLevels.contains(isolationLevel)) { fail("Should have thrown an exception"); } } catch (Exception e) { transactionManager.rollback(); if (allowedLevels.contains(isolationLevel)) { throw e; } } transactionManager.resume(t1); if (allowedLevels.contains(isolationLevel)) { assertEquals(v, cache.get(fqn, k)); } else { assertNull(cache.get(fqn, k)); } transactionManager.rollback(); } public void testTwoReads() throws Exception { // should be allowed in all cases except Serializable allowedLevels.add(NONE); allowedLevels.add(READ_UNCOMMITTED); allowedLevels.add(READ_COMMITTED); allowedLevels.add(REPEATABLE_READ); // set up some data cache.put(fqn, k, v); // do a read transactionManager.begin(); assertEquals(v, cache.get(fqn, k)); Transaction t1 = transactionManager.suspend(); // and now another simultaneous read transactionManager.begin(); try { assertEquals(v, cache.get(fqn, k)); transactionManager.commit(); if (!allowedLevels.contains(isolationLevel)) { fail("Should have thrown an exception"); } } catch (Exception e) { transactionManager.rollback(); if (allowedLevels.contains(isolationLevel)) { throw e; } } transactionManager.resume(t1); transactionManager.rollback(); } public void testTwoWrites() throws Exception { // should only be allowed for IsolationLevel.NONE allowedLevels.add(NONE); // set up some data cache.put(fqn, k, v); // do a write transactionManager.begin(); cache.put(fqn, k, v); Transaction t1 = transactionManager.suspend(); // and now another simultaneous write transactionManager.begin(); try { cache.put(fqn, k, v); transactionManager.commit(); if (!allowedLevels.contains(isolationLevel)) { fail("Should have thrown an exception"); } } catch (Exception e) { transactionManager.rollback(); if (allowedLevels.contains(isolationLevel)) { throw e; } } transactionManager.resume(t1); transactionManager.rollback(); } public void testNonRepeatableRead() throws Exception { // should be allowed in all cases except R_R and Serializable allowedLevels.add(NONE); allowedLevels.add(READ_UNCOMMITTED); allowedLevels.add(READ_COMMITTED); // set up some data cache.put(fqn, k, v); // do a read transactionManager.begin(); assertEquals(v, cache.get(fqn, k)); Transaction t1 = transactionManager.suspend(); // and now a simultaneous write transactionManager.begin(); try { cache.put(fqn, k, v); transactionManager.commit(); if (!allowedLevels.contains(isolationLevel)) { fail("Should have thrown an exception"); } } catch (Exception e) { transactionManager.rollback(); if (allowedLevels.contains(isolationLevel)) { throw e; } } transactionManager.resume(t1); assertEquals(v, cache.get(fqn, k)); transactionManager.rollback(); } public void testNonRepeatableReadWithNoData() throws Exception { // should be allowed in all cases except R_R and Serializable // This still does happen with R_R though since the database analogy breaks down here. // Since the node does not exist, it cannot be locked for repeatable read. // See http://www.jboss.com/index.html?module=bb&op=viewtopic&p=4036036 allowedLevels.add(NONE); allowedLevels.add(READ_UNCOMMITTED); allowedLevels.add(READ_COMMITTED); allowedLevels.add(REPEATABLE_READ); // do a read transactionManager.begin(); assertNull(cache.get(fqn, k)); Transaction t1 = transactionManager.suspend(); // and now a simultaneous write transactionManager.begin(); try { cache.put(fqn, k, v); transactionManager.commit(); if (!allowedLevels.contains(isolationLevel)) { fail("Should have thrown an exception"); } } catch (Exception e) { transactionManager.rollback(); if (allowedLevels.contains(isolationLevel)) { throw e; } } transactionManager.resume(t1); if (allowedLevels.contains(isolationLevel)) { assertEquals(v, cache.get(fqn, k)); } else { assertNull(cache.get(fqn, k)); } transactionManager.rollback(); } public void testPhantomRead() throws Exception { // should be allowed in all cases except Serializable allowedLevels.add(NONE); allowedLevels.add(READ_UNCOMMITTED); allowedLevels.add(READ_COMMITTED); allowedLevels.add(REPEATABLE_READ); // set up some data cache.put(fqn, k, v); cache.put(fqnChild1, k, v); // do a read transactionManager.begin(); int numChildren = cache.getRoot().getChild(fqn).getChildren().size(); assertEquals(1, numChildren); Transaction t1 = transactionManager.suspend(); // and now a simultaneous write transactionManager.begin(); try { cache.put(fqnChild2, k, v); transactionManager.commit(); if (!allowedLevels.contains(isolationLevel)) { fail("Should have thrown an exception"); } } catch (Exception e) { transactionManager.rollback(); if (allowedLevels.contains(isolationLevel)) { throw e; } } transactionManager.resume(t1); numChildren = cache.getRoot().getChild(fqn).getChildren().size(); assertEquals(allowedLevels.contains(isolationLevel) ? 2 : 1, numChildren); transactionManager.rollback(); } } ././@LongLink0000000000000000000000000000015400000000000011565 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/transaction/isolationlevels/ReadUncommittedTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/transaction/isolationlevels/ReadUncommittedTe0000644000175000017500000000053710633536145033510 0ustar twernertwernerpackage org.jboss.cache.transaction.isolationlevels; import org.jboss.cache.lock.IsolationLevel; /** * @author Manik Surtani * @since 2.0.0 */ public class ReadUncommittedTest extends IsolationLevelTestBase { public ReadUncommittedTest() { isolationLevel = IsolationLevel.READ_UNCOMMITTED; } } ././@LongLink0000000000000000000000000000015100000000000011562 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/transaction/isolationlevels/SerializableTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/transaction/isolationlevels/SerializableTest.0000644000175000017500000000062611011035040033431 0ustar twernertwernerpackage org.jboss.cache.transaction.isolationlevels; import org.jboss.cache.lock.IsolationLevel; import org.testng.annotations.Test; /** * @author Manik Surtani * @since 2.0.0 */ @Test(groups = "functional") public class SerializableTest extends IsolationLevelTestBase { public SerializableTest() { isolationLevel = IsolationLevel.SERIALIZABLE; } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/transaction/isolationlevels/NoneTest.java0000644000175000017500000000061710736537105032612 0ustar twernertwernerpackage org.jboss.cache.transaction.isolationlevels; import org.jboss.cache.lock.IsolationLevel; import org.testng.annotations.Test; /** * @author Manik Surtani * @since 2.0.0 */ @Test(groups = {"functional", "transaction"}) public class NoneTest extends IsolationLevelTestBase { public NoneTest() { isolationLevel = IsolationLevel.NONE; } } ././@LongLink0000000000000000000000000000015200000000000011563 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/transaction/isolationlevels/ReadCommittedTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/transaction/isolationlevels/ReadCommittedTest0000644000175000017500000000053110633536145033506 0ustar twernertwernerpackage org.jboss.cache.transaction.isolationlevels; import org.jboss.cache.lock.IsolationLevel; /** * @author Manik Surtani * @since 2.0.0 */ public class ReadCommittedTest extends IsolationLevelTestBase { public ReadCommittedTest() { isolationLevel = IsolationLevel.READ_COMMITTED; } } ././@LongLink0000000000000000000000000000015300000000000011564 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/transaction/isolationlevels/RepeatableReadTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/transaction/isolationlevels/RepeatableReadTes0000644000175000017500000000053410633536145033444 0ustar twernertwernerpackage org.jboss.cache.transaction.isolationlevels; import org.jboss.cache.lock.IsolationLevel; /** * @author Manik Surtani * @since 2.0.0 */ public class RepeatableReadTest extends IsolationLevelTestBase { public RepeatableReadTest() { isolationLevel = IsolationLevel.REPEATABLE_READ; } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/transaction/SuspendTxTest.java0000644000175000017500000000474510777013310030432 0ustar twernertwernerpackage org.jboss.cache.transaction; import org.jboss.cache.CacheFactory; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import static org.testng.AssertJUnit.assertEquals; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.Transaction; import javax.transaction.TransactionManager; /** * Based on a contribution by Owen Taylor * * @author otaylor@redhat.com * @author Manik Surtani (manik@jboss.org) */ @Test(groups = {"functional", "transaction"}) public class SuspendTxTest { CacheSPI cache; TransactionManager mgr; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { CacheFactory instance = new DefaultCacheFactory(); cache = (CacheSPI) instance.createCache(false); cache.getConfiguration().setCacheMode("local"); cache.getConfiguration().setTransactionManagerLookupClass(TransactionSetup.getManagerLookup()); cache.start(); mgr = cache.getTransactionManager(); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { cache.stop(); if (mgr.getTransaction() != null) { mgr.rollback(); } mgr = null; } /** * Tests that locks created when a transaction is suspended are independent * from the transaction. */ public void testSuspendedLocks() throws Exception { // create /one first cache.put("/one", null); cache.put("/a", null); mgr.begin(); cache.put("/one/two", "key1", "val1"); int numLocksBefore = cache.getNumberOfLocksHeld(); Transaction tx = mgr.suspend(); cache.put("/a/b", "key1", "val1"); mgr.resume(tx); assertEquals(numLocksBefore, cache.getNumberOfLocksHeld()); } /** * Tests that locks created when a transaction is suspended are independent * from the transaction. */ public void testSuspendedUsingOptionsLocks() throws Exception { mgr.begin(); cache.put("/one/two", "key1", "val1"); int numLocksBefore = cache.getNumberOfLocksHeld(); cache.getInvocationContext().getOptionOverrides().setFailSilently(true);// will cause any current gtx2EntryMap to be suspended for the duration of this call. cache.put(Fqn.fromString("/a/b"), "key1", "val1"); assertEquals(numLocksBefore, cache.getNumberOfLocksHeld()); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/transaction/DeadlockTest.java0000755000175000017500000003456611017455042030212 0ustar twernertwerner/* * * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.transaction; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.CacheException; import org.jboss.cache.CacheFactory; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.config.Configuration; import org.jboss.cache.lock.IsolationLevel; import org.jboss.cache.lock.TimeoutException; import org.jboss.cache.lock.UpgradeException; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.util.CachePrinter; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.NotSupportedException; import javax.transaction.SystemException; import javax.transaction.TransactionManager; /** * Tests transactional access to a local Cache, with concurrent (deadlock-prone) access. * * @version $Id: DeadlockTest.java 5906 2008-05-29 07:24:18Z mircea.markus $ */ @Test(groups = {"functional", "transaction"}) public class DeadlockTest { CacheSPI cache = null; Exception thread_ex; final Fqn NODE = Fqn.fromString("/a/b/c"); final Fqn PARENT_NODE = Fqn.fromString("/a/b"); final Fqn FQN1 = NODE; final Fqn FQN2 = Fqn.fromString("/1/2/3"); final Log log = LogFactory.getLog(DeadlockTest.class); @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { CacheFactory instance = new DefaultCacheFactory(); cache = (CacheSPI) instance.createCache(false); cache.getConfiguration().setStateRetrievalTimeout(10000); cache.getConfiguration().setClusterName("test"); cache.getConfiguration().setCacheMode(Configuration.CacheMode.LOCAL); cache.getConfiguration().setTransactionManagerLookupClass(TransactionSetup.getManagerLookup()); cache.getConfiguration().setIsolationLevel(IsolationLevel.REPEATABLE_READ); cache.getConfiguration().setLockParentForChildInsertRemove(true); cache.getConfiguration().setLockAcquisitionTimeout(3000); cache.create(); cache.start(); thread_ex = null; } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { if (cache != null) { cache.stop(); } if (thread_ex != null) { throw thread_ex; } } public void testConcurrentUpgrade() throws CacheException, InterruptedException { MyThread t1 = new MyThreadTimeout("MyThread#1", NODE); MyThread t2 = new MyThread("MyThread#2", NODE); cache.put(NODE, null); t1.start(); t2.start(); TestingUtil.sleepThread((long) 5000); synchronized (t1) { t1.notify();// t1 will now try to upgrade RL to WL, but fails b/c t2 still has a RL } TestingUtil.sleepThread((long) 5000); synchronized (t2) { t2.notify();// t1 should now be able to upgrade because t1 was rolled back (RL was removed) } t1.join(); t2.join(); } /** * Typical deadlock: t1 acquires WL on /a/b/c, t2 WL on /1/2/3, then t1 attempts to get WL on /1/2/3 (locked by t2), * and t2 tries to acquire WL on /a/b/c. One (or both) of the 2 transactions is going to timeout and roll back. */ public void testPutDeadlock() throws CacheException, InterruptedException { MyPutter t1 = new MyPutterTimeout("MyPutter#1", FQN1, FQN2); MyPutter t2 = new MyPutter("MyPutter#2", FQN2, FQN1); cache.put(FQN1, null); cache.put(FQN2, null); t1.start(); t2.start(); TestingUtil.sleepThread((long) 1000); synchronized (t1) { t1.notify();// t1 will now try to acquire WL on /1/2/3 (held by t2) - this will time out } TestingUtil.sleepThread((long) 1000); synchronized (t2) { t2.notify();// t2 tries to acquire WL on /a/b/c (held by t1) } t1.join(); t2.join(); } /* Commented out since JBCACHE-875 onwards, this will end up in a classic deadlock since we lock parent when removing a child. public void testCreateIfNotExistsLogic() throws CacheException, InterruptedException { cache.put(NODE, null); class T0 extends GenericThread { public T0(String name) { super(name); } protected void _run() throws Exception { Transaction myTx = startTransaction(); log("put(" + NODE + ")"); cache.put(NODE, null); log("put(" + NODE + "): OK"); synchronized (this) {wait();} log("remove(" + NODE + ")"); cache.remove(NODE); log("remove(" + NODE + "): OK"); log("committing TX"); myTx.commit(); } } class T1 extends GenericThread { public T1(String name) { super(name); } protected void _run() throws Exception { Transaction myTx = startTransaction(); log("put(" + NODE + ")"); cache.put(NODE, null); log("put(" + NODE + "): OK"); log("committing TX"); myTx.commit(); } } T0 t0 = new T0("T0"); t0.start(); TestingUtil.sleepThread((long) 500); T1 t1 = new T1("T1"); t1.start(); TestingUtil.sleepThread((long) 500); synchronized (t0) { t0.notify(); } t0.join(); t1.join(); } */ public void testMoreThanOneUpgrader() throws Exception { final int NUM = 2; final Object lock = new Object(); cache.put(NODE, "bla", "blo"); MyUpgrader[] upgraders = new MyUpgrader[NUM]; for (int i = 0; i < upgraders.length; i++) { upgraders[i] = new MyUpgrader("Upgrader#" + i, NODE, lock); upgraders[i].start(); } TestingUtil.sleepThread((long) 1000); log("locks: " + CachePrinter.printCacheLockingInfo(cache)); synchronized (lock) { lock.notifyAll(); } // all threads now try to upgrade the RL to a WL for (MyUpgrader upgrader : upgraders) { upgrader.join(); } } public void testPutsAndRemovesOnParentAndChildNodes() throws InterruptedException { ContinuousPutter putter = new ContinuousPutter("Putter", NODE); ContinuousRemover remover = new ContinuousRemover("Remover", PARENT_NODE); putter.start(); remover.start(); TestingUtil.sleepThread((long) 5000); log("stopping Putter"); putter.looping = false; log("stopping Remover"); remover.looping = false; putter.join(); remover.join(); } public void testPutsAndRemovesOnParentAndChildNodesReversed() throws InterruptedException { ContinuousPutter putter = new ContinuousPutter("Putter", PARENT_NODE); ContinuousRemover remover = new ContinuousRemover("Remover", NODE); putter.start(); remover.start(); TestingUtil.sleepThread((long) 5000); log("stopping Putter"); putter.looping = false; log("stopping Remover"); remover.looping = false; putter.join(); remover.join(); } public void testPutsAndRemovesOnSameNode() throws InterruptedException { ContinuousPutter putter = new ContinuousPutter("Putter", NODE); ContinuousRemover remover = new ContinuousRemover("Remover", NODE); putter.start(); remover.start(); TestingUtil.sleepThread((long) 5000); log("stopping Putter"); putter.looping = false; log("stopping Remover"); remover.looping = false; putter.join(); remover.join(); } class GenericThread extends Thread { protected TransactionManager tm; protected boolean looping = true; public GenericThread() { } public GenericThread(String name) { super(name); } public void setLooping(boolean looping) { this.looping = looping; } public void run() { try { _run(); } catch (Exception t) { System.out.println(getName() + ": " + t); if (thread_ex == null) { thread_ex = t; } } if (log.isTraceEnabled()) { log.trace("Thread " + getName() + " terminated"); } } protected void _run() throws Exception { throw new UnsupportedOperationException(); } } class ContinuousRemover extends GenericThread { Fqn fqn; public ContinuousRemover(String name, Fqn fqn) { super(name); this.fqn = fqn; } protected void _run() throws Exception { while (thread_ex == null && looping) { try { if (interrupted()) { break; } tm = startTransaction(); log("remove(" + fqn + ")"); cache.removeNode(fqn); sleep(random(20)); tm.commit(); } catch (InterruptedException interrupted) { tm.rollback(); break; } catch (Exception ex) { tm.rollback(); throw ex; } } } } class ContinuousPutter extends GenericThread { Fqn fqn; public ContinuousPutter(String name, Fqn fqn) { super(name); this.fqn = fqn; } protected void _run() throws Exception { while (thread_ex == null && looping) { try { if (interrupted()) { break; } tm = startTransaction(); log("put(" + fqn + ")"); cache.put(fqn, "foo", "bar"); sleep(random(20)); tm.commit(); } catch (InterruptedException interrupted) { tm.rollback(); break; } catch (Exception ex) { tm.rollback(); throw ex; } } } } private static long random(long range) { return (long) ((Math.random() * 100000) % range) + 1; } class MyThread extends GenericThread { Fqn fqn; public MyThread(String name, Fqn fqn) { super(name); this.fqn = fqn; } protected void _run() throws Exception { tm = startTransaction(); log("get(" + fqn + ")"); cache.get(fqn, "bla");// acquires RL log("done, locks: " + CachePrinter.printCacheLockingInfo(cache)); synchronized (this) { wait(); } log("put(" + fqn + ")"); cache.put(fqn, "key", "val");// need to upgrade RL to WL log("done, locks: " + CachePrinter.printCacheLockingInfo(cache)); tm.commit(); log("committed TX, locks: " + CachePrinter.printCacheLockingInfo(cache)); } } class MyUpgrader extends MyThread { Object lock; public MyUpgrader(String name, Fqn fqn) { super(name, fqn); } public MyUpgrader(String name, Fqn fqn, Object lock) { super(name, fqn); this.lock = lock; } protected void _run() throws Exception { tm = startTransaction(); try { log("get(" + fqn + ")"); cache.get(fqn, "bla");// acquires RL synchronized (lock) { lock.wait(); } log("put(" + fqn + ")"); cache.put(fqn, "key", "val");// need to upgrade RL to WL log("done, locks: " + CachePrinter.printCacheLockingInfo(cache)); tm.commit(); log("committed TX, locks: " + CachePrinter.printCacheLockingInfo(cache)); } catch (UpgradeException upge) { log("Exception upgrading lock"); tm.rollback(); } } } class MyThreadTimeout extends MyThread { public MyThreadTimeout(String name, Fqn fqn) { super(name, fqn); } protected void _run() throws Exception { try { super._run(); } catch (UpgradeException upgradeEx) { log("received UpgradeException as expected"); tm.rollback(); log("rolled back TX, locks: " + CachePrinter.printCacheLockingInfo(cache)); } catch (TimeoutException timeoutEx) { log("received TimeoutException as expected"); tm.rollback(); log("rolled back TX, locks: " + CachePrinter.printCacheLockingInfo(cache)); } } } class MyPutter extends GenericThread { Fqn fqn1, fqn2; public MyPutter(String name, Fqn fqn1, Fqn fqn2) { super(name); this.fqn1 = fqn1; this.fqn2 = fqn2; } protected void _run() throws Exception { tm = startTransaction(); log("put(" + fqn1 + ")"); cache.put(fqn1, "key", "val");// need to upgrade RL to WL log("done, locks: " + CachePrinter.printCacheLockingInfo(cache)); synchronized (this) { wait(); } log("put(" + fqn2 + ")"); cache.put(fqn2, "key", "val");// need to upgrade RL to WL log("done, locks: " + CachePrinter.printCacheLockingInfo(cache)); tm.commit(); log("committed TX, locks: " + CachePrinter.printCacheLockingInfo(cache)); } } class MyPutterTimeout extends MyPutter { public MyPutterTimeout(String name, Fqn fqn1, Fqn fqn2) { super(name, fqn1, fqn2); } protected void _run() throws Exception { try { super._run(); } catch (TimeoutException timeoutEx) { log("received TimeoutException as expected"); tm.rollback(); log("rolled back TX, locks: " + CachePrinter.printCacheLockingInfo(cache)); } } } private static void log(String msg) { System.out.println(Thread.currentThread().getName() + ": " + msg); } private TransactionManager startTransaction() throws SystemException, NotSupportedException { TransactionManager mgr = cache.getTransactionManager(); mgr.begin(); return mgr; } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/transaction/RemoveOnTxTest.java0000644000175000017500000001343711063762652030552 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * Copyright 2008, Red Hat Middleware LLC, and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.cache.transaction; import javax.transaction.TransactionManager; import javax.transaction.Transaction; import org.jboss.cache.*; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.config.Configuration; import org.testng.annotations.Test; import org.testng.annotations.BeforeMethod; import org.testng.annotations.AfterMethod; import static org.testng.Assert.*; /** * This is for checking issue: https://jira.jboss.org/jira/browse/JBCACHE-1406 * This was an issue on 1.4 but didn't reproduce on 2.2.x. * Anyway, the tests is here just in case... * * @author Mircea.Markus@jboss.com */ @Test (groups = "functional") public class RemoveOnTxTest { private CacheSPI cache; private DataContainerImpl dataContainer; @BeforeMethod protected void setUp() throws Exception { Configuration configuration = new Configuration(); configuration.setCacheMode(Configuration.CacheMode.LOCAL); configuration.setTransactionManagerLookupClass("org.jboss.cache.transaction.GenericTransactionManagerLookup"); configuration.setNodeLockingScheme(Configuration.NodeLockingScheme.PESSIMISTIC); cache = (CacheSPI) new DefaultCacheFactory().createCache(configuration); dataContainer = (DataContainerImpl) TestingUtil.extractComponentRegistry(cache).getComponent(DataContainer.class); } @AfterMethod protected void tearDown() throws Exception { cache.stop(); cache.destroy(); } public void testFailure() throws Exception { TransactionManager tm = cache.getTransactionManager(); try { tm.begin(); print("put()"); cache.put("/a/b/c", "test", "test"); assertTrue(cache.get("/a/b/c", "test").equals("test")); print("remove()"); cache.removeNode("/a/b"); assertTrue(!cache.exists("/a/b")); assertTrue(!cache.exists("/a/b/c")); cache.put("/a/b/d", "test1", "test1"); assertTrue(!cache.exists("/a/b/c")); assertTrue(cache.exists("/a/b/d")); print("Exists? " + cache.exists("/a/b/c")); print("get(): " + cache.get("/a/b/c", "test")); tm.commit(); assertTrue(cache.peek(Fqn.fromString("/a/b/c"), true, true) == null); assertTrue(!cache.exists("/a/b/c")); assertTrue(cache.exists("/a/b/d")); dataContainer.printLockInfo(); } catch (Exception ex) { tm.rollback(); throw ex; } dataContainer.printLockInfo(); System.out.println(" lock info " + dataContainer.printLockInfo()); try { tm.begin(); print(cache.exists("/a/b/c")); print("get(): " + cache.get("/a/b/c", "test")); print("get(): " + cache.get("/a/b/c", "test")); Transaction t = tm.suspend(); try { cache.putForExternalRead(Fqn.fromString("/a/b/c"), "test", "test"); } catch (Exception ignore) { ignore.printStackTrace(); } tm.resume(t); print("get(): " + cache.get("/a/b/c", "test")); print(cache.exists("/a/b/c")); cache.put("/a/b/c", "test", "test"); print(cache.exists("/a/b/c")); tm.commit(); } catch (Exception ex) { tm.rollback(); throw ex; } } private void print(Object s) { System.out.println(s); } private void print(boolean s) { System.out.println(s); } public void testReal() throws Exception { TransactionManager tm = cache.getTransactionManager(); tm.begin(); print("put()"); cache.put("/a/b/c", "test", "test"); assertTrue(cache.get("/a/b/c", "test").equals("test")); print("remove()"); cache.removeNode("/a/b"); assertTrue(!cache.exists("/a/b")); assertTrue(!cache.exists("/a/b/c")); cache.put("/a/b/d", "test1", "test1"); assertTrue(!cache.exists("/a/b/c")); assertTrue(cache.exists("/a/b/d")); print("Exists? " + cache.exists("/a/b/c")); print("get(): " + cache.get("/a/b/c", "test")); tm.commit(); assertNull(cache.peek(Fqn.fromString("/a/b/c"), true, true)); assertTrue(!cache.exists("/a/b/c")); assertTrue(cache.exists("/a/b/d")); dataContainer.printLockInfo(); } public void testSimplified() throws Exception { TransactionManager tm = cache.getTransactionManager(); tm.begin(); print("put()"); cache.put("/a/b/c", "test", "test"); assertTrue(cache.peek(Fqn.fromString("/a/b/c"), true, true) != null); cache.removeNode("/a/b"); tm.commit(); assertTrue(cache.peek(Fqn.fromString("/a/b/c"), true, true) == null); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/transaction/StatusUnknownTest.java0000644000175000017500000001101710736672510031336 0ustar twernertwernerpackage org.jboss.cache.transaction; import org.jboss.cache.Cache; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.transaction.NotSupportedException; import javax.transaction.RollbackException; import javax.transaction.Status; import javax.transaction.Synchronization; import javax.transaction.SystemException; import javax.transaction.Transaction; import javax.transaction.TransactionManager; import java.util.LinkedList; import java.util.List; import java.util.Properties; /** * This test checks how the cache behaves when a JTA STATUS_UNKNOWN is passed in to the cache during afterCompletion(). * * @author Manik Surtani */ @SuppressWarnings("unchecked") @Test(groups = "functional") public class StatusUnknownTest { private Cache cache; private TransactionManager tm; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { cache = new DefaultCacheFactory().createCache(false); cache.getConfiguration().setTransactionManagerLookupClass(HeuristicFailingDummyTransactionManagerLookup.class.getName()); cache.start(); tm = cache.getConfiguration().getRuntimeConfig().getTransactionManager(); } @AfterMethod(alwaysRun = true) public void tearDown() { cache.stop(); } public void testStatusUnknown() throws Exception { tm.begin(); Fqn fqn = Fqn.fromString("/a/b/c"); cache.put(fqn, "k", "v"); assertEquals(4, ((CacheSPI) cache).getNumberOfLocksHeld()); assertTrue(cache.getRoot().hasChild(fqn)); tm.commit(); assertEquals(0, ((CacheSPI) cache).getNumberOfLocksHeld()); assertFalse(cache.getRoot().hasChild(fqn)); } public static class HeuristicFailingDummyTransactionManager extends DummyTransactionManager { private static final long serialVersionUID = 6325631394461739211L; @Override public void begin() throws SystemException, NotSupportedException { super.begin(); Transaction tx = new HeuristicFailingDummyTransaction(this); setTransaction(tx); } public static DummyTransactionManager getInstance() { if (instance == null) { instance = new HeuristicFailingDummyTransactionManager(); try { Properties p = new Properties(); p.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.cache.transaction.DummyContextFactory"); Context ctx = new InitialContext(p); ctx.bind("java:/TransactionManager", instance); ctx.bind("UserTransaction", new DummyUserTransaction(instance)); } catch (NamingException e) { log.error("binding of DummyTransactionManager failed", e); } } return instance; } } public static class HeuristicFailingDummyTransaction extends DummyTransaction { public HeuristicFailingDummyTransaction(DummyBaseTransactionManager mgr) { super(mgr); } @Override public void commit() throws RollbackException { try { notifyBeforeCompletion(); notifyAfterCompletion(Status.STATUS_UNKNOWN); } finally { // Disassociate tx from thread. tm_.setTransaction(null); } } @Override protected void notifyAfterCompletion(int status) { List tmp; synchronized (participants) { tmp = new LinkedList(participants); } for (Synchronization s : tmp) { try { s.afterCompletion(status); } catch (Throwable t) { throw (RuntimeException) t; } } synchronized (participants) { participants.clear(); } } } public static class HeuristicFailingDummyTransactionManagerLookup implements TransactionManagerLookup { public TransactionManager getTransactionManager() throws Exception { return HeuristicFailingDummyTransactionManager.getInstance(); } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/transaction/TransactionSetup.java0000644000175000017500000000766010700470066031143 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * Copyright 2005, JBoss Inc., and individual contributors as indicated * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.cache.transaction; import javax.transaction.TransactionManager; import javax.transaction.UserTransaction; /** * A simple abstraction for transaction manager interaction * * @author Jason T. Greene */ public class TransactionSetup { private interface Operations { UserTransaction getUserTransaction(); String getLookup(); void cleanup(); TransactionManager getManager(); } private static final String JBOSS_JTA="jboss-jta"; private static Operations operations; static { init(); } private static void init() { String property = System.getProperty("org.jboss.cache.test.tm"); if (JBOSS_JTA.equals(property)) { final String lookup = JBossStandaloneJTAManagerLookup.class.getName(); final JBossStandaloneJTAManagerLookup instance = new JBossStandaloneJTAManagerLookup(); operations = new Operations() { public UserTransaction getUserTransaction() { try { return instance.getUserTransaction(); } catch (Exception e) { throw new RuntimeException(e); } } public void cleanup() { } public String getLookup() { return lookup; } public TransactionManager getManager() { try { return instance.getTransactionManager(); } catch (Exception e) { throw new RuntimeException(e); } } }; } else { final String lookup = DummyTransactionManagerLookup.class.getName(); final DummyTransactionManagerLookup instance = new DummyTransactionManagerLookup(); operations = new Operations() { public UserTransaction getUserTransaction() { return instance.getUserTransaction(); } public void cleanup() { instance.cleanup(); } public String getLookup() { return lookup; } public TransactionManager getManager() { try { return instance.getTransactionManager(); } catch (Exception e) { throw new RuntimeException(e); } } }; } } public static TransactionManager getManager() { return operations.getManager(); } public static String getManagerLookup() { return operations.getLookup(); } public static UserTransaction getUserTransaction() { return operations.getUserTransaction(); } public static void cleanup() { operations.cleanup(); } }jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/transaction/ConcurrentTransactionalTest.java0000644000175000017500000001264010746355150033342 0ustar twernertwerner/* * * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.transaction; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.CacheFactory; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.XmlConfigurationParser; import org.jboss.cache.factories.UnitTestCacheConfigurationFactory; import org.jboss.cache.lock.IsolationLevel; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.fail; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.UserTransaction; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Set; /** * Unit test for local CacheSPI. Use locking and multiple threads to test * concurrent access to the tree. * * @version $Id: ConcurrentTransactionalTest.java 5238 2008-01-25 12:47:36Z mircea.markus $ */ @Test(groups = {"functional", "transaction"}) public class ConcurrentTransactionalTest { private CacheSPI cache; private Log logger_ = LogFactory.getLog(ConcurrentTransactionalTest.class); private static Throwable thread_ex = null; private static final int NUM = 10000; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { } private void createCache(IsolationLevel level) { CacheFactory factory = new DefaultCacheFactory(); Configuration conf = UnitTestCacheConfigurationFactory.createConfiguration(Configuration.CacheMode.LOCAL, true); conf.setCacheMode(Configuration.CacheMode.LOCAL); conf.setIsolationLevel(level); conf.setTransactionManagerLookupClass(TransactionSetup.getManagerLookup()); cache = (CacheSPI) new DefaultCacheFactory().createCache(conf); cache.put("/a/b/c", null); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { cache.stop(); thread_ex = null; TransactionSetup.cleanup(); } public void testConcurrentAccessWithRWLock() throws Throwable { createCache(IsolationLevel.REPEATABLE_READ); work_(); } public void testConcurrentAccessWithExclusiveLock() throws Throwable { createCache(IsolationLevel.SERIALIZABLE); work_(); } private void work_() throws Throwable { Updater one, two; try { one = new Updater("Thread one"); two = new Updater("Thread two"); long current = System.currentTimeMillis(); one.start(); two.start(); one.join(); two.join(); if (thread_ex != null) { throw thread_ex; } long now = System.currentTimeMillis(); log("*** Time elapsed: " + (now - current)); System.out.println("cache content: " + cache.toString()); Set keys = cache.getNode(Fqn.fromString("/a/b/c")).getKeys(); System.out.println("number of keys=" + keys.size()); if (keys.size() != NUM) { scanForNullValues(keys); try { System.out.println("size=" + keys.size()); List l = new LinkedList(keys); Collections.sort(l); System.out.println("keys: " + l); for (int i = 0; i < NUM; i++) { if (!l.contains(new Integer(i))) { System.out.println("missing: " + i); } } LinkedList duplicates = new LinkedList(); for (Integer integer : l) { if (duplicates.contains(integer)) { System.out.println(integer + " is a duplicate"); } else { duplicates.add(integer); } } } catch (Exception e1) { e1.printStackTrace(); } } assertEquals(NUM, keys.size()); } catch (Exception e) { e.printStackTrace(); fail(e.toString()); } } private void scanForNullValues(Set keys) { for (Object o : keys) { if (o == null) { System.err.println("found a null value in keys"); } } } private void log(String msg) { logger_.debug(" [" + Thread.currentThread() + "]: " + msg); } private class Updater extends Thread { private String val = null; private UserTransaction tx; public Updater(String name) { this.val = name; } public void run() { try { log("adding data"); tx = TransactionSetup.getUserTransaction(); for (int i = 0; i < NUM; i++) { log("adding data i=" + i); tx.begin(); cache.put("/a/b/c", i, val); tx.commit(); yield(); } } catch (Throwable t) { t.printStackTrace(); thread_ex = t; } } } } ././@LongLink0000000000000000000000000000014700000000000011567 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/transaction/IsolationLevelSerializableTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/transaction/IsolationLevelSerializableTest.ja0000644000175000017500000001116210732752176033430 0ustar twernertwernerpackage org.jboss.cache.transaction; import org.jboss.cache.Cache; import org.jboss.cache.CacheFactory; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.lock.IsolationLevel; import org.jboss.cache.lock.TimeoutException; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.NotSupportedException; import javax.transaction.SystemException; import javax.transaction.TransactionManager; import java.util.concurrent.CountDownLatch; /** * Tests READ_COMMITED isolation level. * * @author Ovidiu Feodorov * @version $Id: IsolationLevelSerializableTest.java 4912 2007-12-21 14:58:06Z manik.surtani@jboss.com $ */ @Test(groups = {"functional", "transaction"}) public class IsolationLevelSerializableTest { private Cache cache = null; private final Fqn FQN = Fqn.fromString("/a"); private final String KEY = "key"; private final String VALUE = "value"; private volatile boolean writerFailed; private volatile AssertionError writerError; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { writerFailed = false; writerError = null; Configuration config = new Configuration(); config.setCacheMode(CacheMode.LOCAL); config.setIsolationLevel(IsolationLevel.SERIALIZABLE); config.setLockAcquisitionTimeout(1000); config.setTransactionManagerLookupClass(TransactionSetup.getManagerLookup()); CacheFactory instance = new DefaultCacheFactory(); cache = instance.createCache(config); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { cache.stop(); cache.destroy(); cache = null; } /** * Test creates a cache node then starts a separate thread that removes * the node inside a tx. Test confirms that the removal cannot be seen * before the test commits. * * @throws Exception */ public void testNodeRemoved() throws Exception { final CountDownLatch readerCanRead = new CountDownLatch(1); final CountDownLatch readerDone = new CountDownLatch(1); final CountDownLatch writerDone = new CountDownLatch(1); cache.put(FQN, KEY, VALUE); assertEquals(VALUE, cache.get(FQN, KEY)); // start a writer thread and a transaction Thread writerThread = new Thread(new Runnable() { public void run() { try { TransactionManager tx = startTransaction(); // change VALUE in a transaction cache.removeNode(FQN); // notify the reading thread readerCanRead.countDown(); readerDone.await(); tx.commit(); } catch (AssertionError e) { writerError = e; } catch (Throwable t) { t.printStackTrace(); writerFailed = true; } finally { System.out.println("writer thread exits"); readerCanRead.countDown(); writerDone.countDown(); } } }, "WRITER"); writerThread.start(); try { // wait until the writer thread changes the value in a transaction, // but it did not yet commit or roll back. readerCanRead.await(); // I shouldn't be able to see the "dirty" value assertEquals("2nd thread cannot see uncommitted changes", VALUE, cache.get(FQN, KEY)); } catch (TimeoutException good) { // ignore; means worked as it should } finally { System.out.println("reader thread exits"); readerDone.countDown(); } // wait for the writer to finish writerDone.await(); assertNull("Node was removed", cache.getNode(FQN)); // If any assertion failed, throw on the AssertionFailedError if (writerError != null) { throw writerError; } if (writerFailed) { fail("The writer thread exited incorrectly. Watch the log for previous stack traces"); } } private TransactionManager startTransaction() throws SystemException, NotSupportedException { TransactionManager mgr = cache.getConfiguration().getRuntimeConfig().getTransactionManager(); mgr.begin(); return mgr; } } ././@LongLink0000000000000000000000000000017400000000000011567 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/transaction/IsolationLevelReadCommittedNodeCreationRollbackTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/transaction/IsolationLevelReadCommittedNodeCr0000644000175000017500000001505710777521744033420 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.transaction; import org.jboss.cache.CacheFactory; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.config.Configuration; import org.jboss.cache.lock.IsolationLevel; import org.jboss.cache.lock.TimeoutException; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.NotSupportedException; import javax.transaction.SystemException; import javax.transaction.TransactionManager; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; /** * Tests READ_COMMITED isolation level. * * @author Ovidiu Feodorov * @version $Id: IsolationLevelReadCommittedNodeCreationRollbackTest.java 5529 2008-04-10 23:33:56Z manik.surtani@jboss.com $ */ @Test(groups = {"functional", "transaction"}) public class IsolationLevelReadCommittedNodeCreationRollbackTest { private CacheSPI cache = null; private final String KEY = "key"; private final String VALUE = "value"; private volatile boolean writerFailed; private volatile boolean readerFailed; private volatile AssertionError writerError; private volatile AssertionError readerError; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { writerFailed = false; readerFailed = false; writerError = null; readerError = null; CacheFactory instance = new DefaultCacheFactory(); cache = (CacheSPI) instance.createCache(false); cache.getConfiguration().setCacheMode(Configuration.CacheMode.LOCAL); cache.getConfiguration().setIsolationLevel(IsolationLevel.READ_COMMITTED); cache.getConfiguration().setTransactionManagerLookupClass(TransactionSetup.getManagerLookup()); cache.start(); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { cache.stop(); cache.destroy(); cache = null; } public void testNodeCreationRollback() throws Exception { final CountDownLatch secondCanWrite = new CountDownLatch(1); final CountDownLatch secondCanRead = new CountDownLatch(1); final CountDownLatch secondDone = new CountDownLatch(1); final CountDownLatch firstCanRollback = new CountDownLatch(1); final CountDownLatch firstDone = new CountDownLatch(1); final Fqn PARENT = Fqn.fromString("/a"); // start a first thread and a transaction Thread firstThread = new Thread(new Runnable() { public void run() { try { TransactionManager tm = startTransaction(); System.out.println("Writing /a/1"); // Create an empty parent node and a node with data Fqn a1 = Fqn.fromRelativeElements(PARENT, "1"); cache.put(a1, KEY, VALUE); // notify the second thread it can write secondCanWrite.countDown(); // wait until the second thread writes and allows me to rollback or until I timeout firstCanRollback.await(3000, TimeUnit.MILLISECONDS); System.out.println("rolling back"); tm.rollback(); assertNull("a1 empty", cache.get(a1, KEY)); // notify the reading thread secondCanRead.countDown(); } catch (AssertionError e) { writerError = e; } catch (Throwable t) { t.printStackTrace(); writerFailed = true; } finally { System.out.println("first thread exits"); secondCanWrite.countDown(); secondCanRead.countDown(); firstDone.countDown(); } } }, "FIRST"); firstThread.start(); // start a second thread; no transaction is necessary here Thread secondThread = new Thread(new Runnable() { public void run() { try { // wait until the first thread has created PARENT and a child secondCanWrite.await(); System.out.println("writing a2"); // create a second child under parent Fqn a2 = Fqn.fromRelativeElements(PARENT, "2"); try { cache.put(a2, KEY, VALUE); } catch (TimeoutException good) { // first thread locked us out of parent System.out.println("Prevented from writing a2 -- " + good.getLocalizedMessage()); return; } // let the first thread know it can rollback firstCanRollback.countDown(); // wait until the first thread rolls back. secondCanRead.await(); // I should still see the value I put assertEquals("write lock not acquired on " + "creation of an empty node", VALUE, cache.get(a2, KEY)); } catch (AssertionError e) { readerError = e; } catch (Throwable t) { t.printStackTrace(); readerFailed = true; } finally { System.out.println("second thread exits"); firstCanRollback.countDown(); secondDone.countDown(); } } }, "SECOND"); secondThread.start(); // wait for both threads to finish secondDone.await(); firstDone.await(); // If any assertion failed, throw on the AssertionFailedError if (readerError != null) { throw readerError; } if (writerError != null) { throw writerError; } if (readerFailed) { fail("The second thread exited incorrectly. Watch the log for previous stack traces"); } if (writerFailed) { fail("The first thread exited incorrectly. Watch the log for previous stack traces"); } } private TransactionManager startTransaction() throws SystemException, NotSupportedException { TransactionManager mgr = cache.getTransactionManager(); mgr.begin(); return mgr; } } ././@LongLink0000000000000000000000000000014700000000000011567 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/transaction/SimultaneousRollbackAndPutTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/transaction/SimultaneousRollbackAndPutTest.ja0000644000175000017500000000733711017455042033424 0ustar twernertwernerpackage org.jboss.cache.transaction; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.Cache; import org.jboss.cache.CacheException; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.invocation.CacheInvocationDelegate; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.util.CachePrinter; import org.testng.annotations.AfterMethod; import org.testng.annotations.AfterTest; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; import javax.transaction.RollbackException; import javax.transaction.Transaction; import javax.transaction.TransactionManager; import java.util.ArrayList; import java.util.List; /** * To test JBCACHE-923 * * @author Manik Surtani */ @Test(groups = {"functional", "transaction"}, enabled = true) // Known issue - disabled because of JBCACHE-923 public class SimultaneousRollbackAndPutTest { private Cache cache; private TransactionManager tm; private Fqn A = Fqn.fromString("/a"), B = Fqn.fromString("/b"); private Log log = LogFactory.getLog(SimultaneousRollbackAndPutTest.class); @BeforeTest(alwaysRun = true) protected void setUp() throws Exception { cache = new DefaultCacheFactory().createCache(false); cache.getConfiguration().setTransactionManagerLookupClass(TransactionSetup.getManagerLookup()); cache.start(); tm = cache.getConfiguration().getRuntimeConfig().getTransactionManager(); cache.put(A, "k", "v"); } @AfterTest(alwaysRun = true) protected void tearDown() { TestingUtil.killCaches(cache); } @AfterMethod(alwaysRun = true) protected void resetCache() throws Exception { try { cache.removeNode(B); cache.getRoot().getChild(A).clearData(); cache.put(A, "k", "v"); // make sure we clean up any gtx2EntryMap associa with the thread TestingUtil.killTransactions(cache); } catch (Exception e) { // restart the cache tearDown(); setUp(); } } @Test(invocationCount = 100, alwaysRun = false, enabled = false, description = "This is to do with a flaw in the way pessimistic locking deals with transactions. See JBCACHE-923") public void testStaleLocks() throws Exception { // scenario: // Thread starts tx in cache. E.g., create and put into B tm.begin(); final Transaction t = tm.getTransaction(); final List exceptions = new ArrayList(); cache.put(B, "k", "v"); // now the container should attempt to rollback the tx in a separate thread. Thread rollbackThread = new Thread("RollbackThread") { public void run() { try { t.rollback(); } catch (Exception e) { exceptions.add(e); } } }; rollbackThread.start(); try { // now try and put stuff in the main thread again cache.put(A, "k2", "v2"); tm.commit(); // assert false : "Should never reach here"; } catch (RollbackException expected) { // this is expected. } catch (CacheException ce) { // also expected at times } rollbackThread.join(); if (((CacheInvocationDelegate) cache).getNumberOfLocksHeld() > 0) { log.fatal("***********"); log.fatal(CachePrinter.printCacheLockingInfo(cache)); log.fatal("***********"); } assert 0 == ((CacheInvocationDelegate) cache).getNumberOfLocksHeld(); if (exceptions.size() > 0) throw ((Exception) exceptions.get(0)); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/mgmt/0000755000175000017500000000000011376173760023413 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/mgmt/PassivationTest.java0000644000175000017500000002200411074706347027412 0ustar twernertwernerpackage org.jboss.cache.mgmt; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertFalse; import static org.testng.AssertJUnit.assertNotNull; import static org.testng.AssertJUnit.assertNull; import java.util.HashMap; import org.jboss.cache.interceptors.ActivationInterceptor; import org.jboss.cache.interceptors.PassivationInterceptor; import org.jboss.cache.util.TestingUtil; import org.testng.annotations.Test; /** * Simple functional tests for ActivationInterceptor and PassivationInterceptor statistics * * @author Jerry Gauthier * @version $Id: PassivationTest.java 6919 2008-10-13 18:01:11Z bstansberry@jboss.com $ */ @Test(groups = "functional") public class PassivationTest extends MgmtTestBase { public PassivationTest() { passivation = true; } public void testPassivationMgmt() throws Exception { assertNotNull("Cache is null.", cache); // Note: because these tests are normally executed without a server, the interceptor // MBeans are usually not available for use in the tests. Consequently it's necessary // to obtain references to the interceptors and work with them directly. ActivationInterceptor act = TestingUtil.findInterceptor(cache, ActivationInterceptor.class); assertNotNull("ActivationInterceptor not found.", act); PassivationInterceptor pass = TestingUtil.findInterceptor(cache, PassivationInterceptor.class); assertNotNull("PassivationInterceptor not found.", pass); System.out.println("count of misses " + act.getCacheLoaderMisses()); int miss = 5; int activations = 0; // was 5 in 1.3 (one per attribute) // now just Europe/Albania and Europe/Hungary were loaded // verify statistics for entries loaded into cache assertEquals("CacheLoaderLoads count error: ", 0, act.getCacheLoaderLoads()); assertEquals("CacheLoaderMisses count error: ", miss, act.getCacheLoaderMisses()); assertEquals("Activations count error: ", activations, act.getActivations()); assertEquals("Passivations count error: ", 0, pass.getPassivations()); // now try retrieving a valid attribute and an invalid attribute assertNotNull("Retrieval error: expected to retrieve " + CAPITAL + " for " + AUSTRIA, cache.get(AUSTRIA, CAPITAL)); assertNull("Retrieval error: did not expect to retrieve " + AREA + " for " + AUSTRIA, cache.get(AUSTRIA, AREA)); // verify statistics after retrieving entries - no change since nodes were already loaded assertEquals("CacheLoaderLoads count error: ", 0, act.getCacheLoaderLoads()); assertEquals("CacheLoaderMisses count error: ", miss, act.getCacheLoaderMisses()); assertEquals("Activations count error: ", activations, act.getActivations()); assertEquals("Passivations count error: ", 0, pass.getPassivations()); // now try retrieving an attribute for an invalid node assertNull("Retrieval error: did not expect to retrieve " + CAPITAL + " for " + POLAND, cache.get(POLAND, CAPITAL)); // verify statistics after retrieving invalid node - misses should now be incremented miss++; assertEquals("CacheLoaderLoads count error: ", 0, act.getCacheLoaderLoads()); assertEquals("CacheLoaderMisses count error: ", miss, act.getCacheLoaderMisses()); assertEquals("Activations count error: ", activations, act.getActivations()); assertEquals("Passivations count error: ", 0, pass.getPassivations()); // now evict Austria and confirm that it's no longer in cache cache.evict(AUSTRIA, false); assertNull("Retrieval error: did not expect to find node " + AUSTRIA + " in cache", cache.peek(AUSTRIA, false)); // passivations should noe be 1 assertEquals("Passivations count error: ", 1, pass.getPassivations()); // now try retrieving the attributes again - first retrieval should trigger a cache load assertNotNull("Retrieval error: expected to retrieve " + CAPITAL + " for " + AUSTRIA, cache.get(AUSTRIA, CAPITAL)); assertNotNull("Retrieval error: expected to retrieve " + CURRENCY + " for " + AUSTRIA, cache.get(AUSTRIA, CURRENCY)); // verify statistics after retrieving evicted entry - loads and activations should now increment by 1 activations++; assertEquals("CacheLoaderLoads count error: ", 1, act.getCacheLoaderLoads()); assertEquals("CacheLoaderMisses count error: ", miss, act.getCacheLoaderMisses()); assertEquals("Activations count error: ", activations, act.getActivations()); assertEquals("Passivations count error: ", 1, pass.getPassivations()); // now remove Austria and confirm that it's not in cache or loader cache.removeNode(AUSTRIA); assertNull("Retrieval error: did not expect to find node " + AUSTRIA + " in cache", cache.peek(AUSTRIA, false)); assertFalse("Retrieval error: did not expect to find node " + AUSTRIA + " in loader", cl.exists(AUSTRIA)); // verify statistics after removing entry - should be unchanged assertEquals("CacheLoaderLoads count error: ", 1, act.getCacheLoaderLoads()); assertEquals("CacheLoaderMisses count error: ", miss, act.getCacheLoaderMisses()); assertEquals("Activations count error: ", activations, act.getActivations()); assertEquals("Passivations count error: ", 1, pass.getPassivations()); // now try retrieving attributes again - each attempt should fail and cause a miss since node is now removed assertNull("Retrieval error: did not expect to retrieve " + CAPITAL + " for " + AUSTRIA, cache.get(AUSTRIA, CAPITAL)); assertNull("Retrieval error: did not expect to retrieve " + CURRENCY + " for " + AUSTRIA, cache.get(AUSTRIA, CURRENCY)); // verify statistics after trying to retrieve removed node's attributes - should be two more misses miss += 2; assertEquals("CacheLoaderLoads count error: ", 1, act.getCacheLoaderLoads()); assertEquals("CacheLoaderMisses count error: ", miss, act.getCacheLoaderMisses()); assertEquals("Activations count error: ", activations, act.getActivations()); assertEquals("Passivations count error: ", 1, pass.getPassivations()); // add a new node and two attributes - this should cause a miss only miss++; cache.put(POLAND, new HashMap()); cache.put(POLAND, CAPITAL, "Warsaw"); cache.put(POLAND, CURRENCY, "Zloty"); assertEquals("CacheLoaderLoads count error: ", 1, act.getCacheLoaderLoads()); assertEquals("CacheLoaderMisses count error: ", miss, act.getCacheLoaderMisses()); assertEquals("Activations count error: ", activations, act.getActivations()); assertEquals("Passivations count error: ", 1, pass.getPassivations()); // evict Poland - this will cause a passivation cache.evict(POLAND, false); assertEquals("CacheLoaderLoads count error: ", 1, act.getCacheLoaderLoads()); assertEquals("CacheLoaderMisses count error: ", miss, act.getCacheLoaderMisses()); assertEquals("Activations count error: ", activations, act.getActivations()); assertEquals("Passivations count error: ", 2, pass.getPassivations()); // retrieve a valid attribute - this will cause an activation and a load activations++; assertNotNull("Retrieval error: expected to retrieve " + CURRENCY + " for " + POLAND, cache.get(POLAND, CURRENCY)); assertEquals("CacheLoaderLoads count error: ", 2, act.getCacheLoaderLoads()); assertEquals("CacheLoaderMisses count error: ", miss, act.getCacheLoaderMisses()); assertEquals("Activations count error: ", activations, act.getActivations()); assertEquals("Passivations count error: ", 2, pass.getPassivations()); // evict again - causing another passivation cache.evict(POLAND, false); assertEquals("CacheLoaderLoads count error: ", 2, act.getCacheLoaderLoads()); assertEquals("CacheLoaderMisses count error: ", miss, act.getCacheLoaderMisses()); assertEquals("Activations count error: ", activations, act.getActivations()); assertEquals("Passivations count error: ", 3, pass.getPassivations()); // retrieve an invalid attribute - this will cause an activation and a load activations++; assertNull("Retrieval error: did not expect to retrieve " + AREA + " for " + POLAND, cache.get(POLAND, AREA)); assertEquals("CacheLoaderLoads count error: ", 3, act.getCacheLoaderLoads()); assertEquals("CacheLoaderMisses count error: ", miss, act.getCacheLoaderMisses()); assertEquals("Activations count error: ", activations, act.getActivations()); assertEquals("Passivations count error: ", 3, pass.getPassivations()); // reset statistics act.resetStatistics(); pass.resetStatistics(); // check the statistics again assertEquals("CacheLoaderLoads count error after reset: ", 0, act.getCacheLoaderLoads()); assertEquals("CacheLoaderMisses count error after reset: ", 0, act.getCacheLoaderMisses()); assertEquals("Activations count error: ", 0, act.getActivations()); assertEquals("Passivations count error: ", 0, pass.getPassivations()); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/mgmt/CacheLoaderTest.java0000644000175000017500000001616511074652365027257 0ustar twernertwernerpackage org.jboss.cache.mgmt; import org.jboss.cache.interceptors.CacheLoaderInterceptor; import org.jboss.cache.interceptors.CacheStoreInterceptor; import org.jboss.cache.util.TestingUtil; import static org.testng.AssertJUnit.*; import org.testng.annotations.Test; import java.util.HashMap; /** * Simple functional tests for CacheLoaderInterceptor and CacheStoreInterceptor statistics * * @author Jerry Gauthier * @version $Id: CacheLoaderTest.java 6909 2008-10-13 14:02:29Z bstansberry@jboss.com $ */ @Test(groups = "functional") public class CacheLoaderTest extends MgmtTestBase { public void testCacheLoaderMgmt() throws Exception { assertNotNull("Cache is null.", cache); // Note: because these tests are normally executed without a server, the interceptor // MBeans are usually not available for use in the tests. Consequently it's necessary // to obtain references to the interceptors and work with them directly. CacheLoaderInterceptor loader = TestingUtil.findInterceptor(cache, CacheLoaderInterceptor.class); assertNotNull("CacheLoaderInterceptor not found.", loader); CacheStoreInterceptor store = TestingUtil.findInterceptor(cache, CacheStoreInterceptor.class); assertNotNull("CacheStoreInterceptor not found.", store); // verify cache loader statistics for entries loaded into cache int miss = 5; int load = 0; int stores = 5; assertEquals("CacheLoaderLoads count error: ", load, loader.getCacheLoaderLoads()); assertEquals("CacheLoaderMisses count error: ", miss, loader.getCacheLoaderMisses()); assertEquals("CacheLoaderStores count error: ", stores, store.getCacheLoaderStores()); // now try retrieving a valid attribute and an invalid attribute assertNotNull("Retrieval error: expected to retrieve " + CAPITAL + " for " + AUSTRIA, cache.get(AUSTRIA, CAPITAL)); assertNull("Retrieval error: did not expect to retrieve " + AREA + " for " + AUSTRIA, cache.get(AUSTRIA, AREA)); // verify statistics after retrieving entries - misses should still be same since nodes were already loaded assertEquals("CacheLoaderLoads count error: ", load, loader.getCacheLoaderLoads()); assertEquals("CacheLoaderMisses count error: ", miss, loader.getCacheLoaderMisses()); assertEquals("CacheLoaderStores count error: ", stores, store.getCacheLoaderStores()); // now try retrieving an attribute for an invalid node assertNull("Retrieval error: did not expect to retrieve " + CAPITAL + " for " + POLAND, cache.get(POLAND, CAPITAL)); // verify statistics for after retrieving entries - misses should now be +1 after attempt to load Poland miss++; assertEquals("CacheLoaderLoads count error: ", load, loader.getCacheLoaderLoads()); assertEquals("CacheLoaderMisses count error: ", miss, loader.getCacheLoaderMisses()); assertEquals("CacheLoaderStores count error: ", stores, store.getCacheLoaderStores()); // now evict Austria and confirm that it's no longer in cache cache.evict(AUSTRIA, false); assertNull("Retrieval error: did not expect to find node " + AUSTRIA + " in cache", cache.peek(AUSTRIA, false)); // now try retrieving its attributes again - first retrieval should trigger a cache load assertNotNull("Retrieval error: expected to retrieve " + CAPITAL + " for " + AUSTRIA, cache.get(AUSTRIA, CAPITAL)); assertNotNull("Retrieval error: expected to retrieve " + CURRENCY + " for " + AUSTRIA, cache.get(AUSTRIA, CURRENCY)); // verify statistics after retrieving evicted entry - loads should now be +1 load++; assertEquals("CacheLoaderLoads count error: ", load, loader.getCacheLoaderLoads()); assertEquals("CacheLoaderMisses count error: ", miss, loader.getCacheLoaderMisses()); assertEquals("CacheLoaderStores count error: ", stores, store.getCacheLoaderStores()); // now remove Austria and confirm that it's not in cache or loader cache.removeNode(AUSTRIA); assertNull("Retrieval error: did not expect to find node " + AUSTRIA + " in cache", cache.peek(AUSTRIA, false)); assertFalse("Retrieval error: did not expect to find node " + AUSTRIA + " in loader", cl.exists(AUSTRIA)); // verify statistics after removing entry - should be unchanged assertEquals("CacheLoaderLoads count error: ", load, loader.getCacheLoaderLoads()); assertEquals("CacheLoaderMisses count error: ", miss, loader.getCacheLoaderMisses()); assertEquals("CacheLoaderStores count error: ", stores, store.getCacheLoaderStores()); // now try retrieving attributes again - each attempt should fail and cause a miss since node is now removed assertNull("Retrieval error: did not expect to retrieve " + CAPITAL + " for " + AUSTRIA, cache.get(AUSTRIA, CAPITAL)); assertNull("Retrieval error: did not expect to retrieve " + CURRENCY + " for " + AUSTRIA, cache.get(AUSTRIA, CURRENCY)); // verify statistics after trying to retrieve removed node's attributes - should be two more misses miss += 2; assertEquals("CacheLoaderLoads count error: ", load, loader.getCacheLoaderLoads()); assertEquals("CacheLoaderMisses count error: ", miss, loader.getCacheLoaderMisses()); assertEquals("CacheLoaderStores count error: ", stores, store.getCacheLoaderStores()); // add a new node - this should cause a store stores++; miss++; cache.put(POLAND, new HashMap()); assertEquals("CacheLoaderLoads count error: ", load, loader.getCacheLoaderLoads()); assertEquals("CacheLoaderMisses count error: ", miss, loader.getCacheLoaderMisses()); assertEquals("CacheLoaderStores count error: ", stores, store.getCacheLoaderStores()); // add two attributes - this should cause two stores // plus one load as we're doing put(k, v) stores += 2; cache.put(POLAND, CAPITAL, "Warsaw"); cache.put(POLAND, CURRENCY, "Zloty"); assertEquals("CacheLoaderLoads count error: ", load, loader.getCacheLoaderLoads()); assertEquals("CacheLoaderMisses count error: ", miss, loader.getCacheLoaderMisses()); assertEquals("CacheLoaderStores count error: ", stores, store.getCacheLoaderStores()); // evict Poland and then try to retrieve an invalid attribute - this will cause a load as the node is restored load++; cache.evict(POLAND, false); assertNull("Retrieval error: did not expect to retrieve " + AREA + " for " + POLAND, cache.get(POLAND, AREA)); assertEquals("CacheLoaderLoads count error: ", load, loader.getCacheLoaderLoads()); assertEquals("CacheLoaderMisses count error: ", miss, loader.getCacheLoaderMisses()); assertEquals("CacheLoaderStores count error: ", stores, store.getCacheLoaderStores()); // reset statistics loader.resetStatistics(); store.resetStatistics(); // check the statistics again assertEquals("CacheLoaderLoads count error after reset: ", 0, loader.getCacheLoaderLoads()); assertEquals("CacheLoaderMisses count error after reset: ", 0, loader.getCacheLoaderMisses()); assertEquals("CacheLoaderStores count error after reset: ", 0, store.getCacheLoaderStores()); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/mgmt/TxTest.java0000644000175000017500000002445110732232543025505 0ustar twernertwernerpackage org.jboss.cache.mgmt; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.factories.UnitTestCacheConfigurationFactory; import org.jboss.cache.interceptors.TxInterceptor; import static org.testng.AssertJUnit.*; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.TransactionManager; import java.util.HashMap; import java.util.List; /** * Simple functional tests for TxInterceptor statistics * * @author Jerry Gauthier * @version $Id: TxTest.java 4880 2007-12-19 15:14:43Z manik.surtani@jboss.com $ */ @Test(groups = {"functional"}) public class TxTest { private static final String CLUSTER_NAME = "TxTestCluster"; private static final String CAPITAL = "capital"; private static final String CURRENCY = "currency"; private static final String POPULATION = "population"; private static final String AREA = "area"; private CacheSPI cache1 = null; private CacheSPI cache2 = null; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { cache1 = createCache(CLUSTER_NAME); cache2 = createCache(CLUSTER_NAME); } @BeforeMethod(alwaysRun = true) public void tearDown() throws Exception { if (cache1 != null) { cache1.stop(); cache1.destroy(); cache1 = null; } if (cache2 != null) { cache2.stop(); cache2.destroy(); cache2 = null; } } public void testTxMgmt() throws Exception { assertNotNull("Cache1 is null.", cache1); assertNotNull("Cache2 is null.", cache2); // Note: because these tests are normally executed without a server, the interceptor // MBeans are usually not available for use in the tests. Consequently it's necessary // to obtain a reference to the interceptor and work with it directly. TxInterceptor tx1 = getTxInterceptor(cache1); assertNotNull("Cache1 InvalidationInterceptor not found.", tx1); TxInterceptor tx2 = getTxInterceptor(cache2); assertNotNull("Cache2 InvalidationInterceptor not found.", tx2); TransactionManager tm1 = cache1.getTransactionManager(); assertNotNull("TransactionManager is null.", tm1); // populate cache1 with test data - no transaction loadCacheNoTx(cache1); // confirm that data is in cache1 and in cache2 Fqn key = Fqn.fromString("Europe/Austria"); assertNotNull("Cache1 retrieval error: expected to retrieve " + CAPITAL + " for " + key, cache1.get(key, CAPITAL)); assertNotNull("Cache2 retrieval error: expected to retrieve " + CAPITAL + " for " + key, cache2.get(key, CAPITAL)); key = Fqn.fromString("Europe/Albania"); assertNotNull("Cache1 retrieval error: expected to retrieve " + CAPITAL + " for " + key, cache1.get(key, CAPITAL)); assertNotNull("Cache2 retrieval error: expected to retrieve " + CAPITAL + " for " + key, cache2.get(key, CAPITAL)); // verify basic statistics for entries loaded into cache assertEquals("Cache1 Tx Prepares error after reset: ", new Long(0), new Long(tx1.getPrepares())); assertEquals("Cache1 Tx Commits error after reset: ", new Long(0), new Long(tx1.getCommits())); assertEquals("Cache1 Tx Rollbacks error after reset: ", new Long(0), new Long(tx1.getRollbacks())); assertEquals("Cache2 Tx Prepares error after reset: ", new Long(0), new Long(tx2.getPrepares())); assertEquals("Cache2 Tx Commits error after reset: ", new Long(0), new Long(tx2.getCommits())); assertEquals("Cache2 Tx Rollbacks error after reset: ", new Long(0), new Long(tx2.getRollbacks())); // populate cache1 with test data - then transaction commit loadCacheTxCommit(cache1, tm1); loadCacheTxCommit2(cache1, tm1); // confirm that committed data is in cache1 and in cache2 key = Fqn.fromString("Europe/England"); assertNotNull("Cache1 retrieval error: expected to retrieve " + CAPITAL + " for " + key, cache1.get(key, CAPITAL)); assertNotNull("Cache2 retrieval error: expected to retrieve " + CAPITAL + " for " + key, cache2.get(key, CAPITAL)); key = Fqn.fromString("Europe/Hungary"); assertNotNull("Cache1 retrieval error: expected to retrieve " + CAPITAL + " for " + key, cache1.get(key, CAPITAL)); assertNotNull("Cache2 retrieval error: expected to retrieve " + CAPITAL + " for " + key, cache2.get(key, CAPITAL)); // populate cache1 with test data - then transaction rollback loadCacheTxRollback(cache1, tm1); // confirm that rolled back data is not in cache1 or cache2 key = Fqn.fromString("Europe/France"); assertNull("Cache1 retrieval error: did not expect to retrieve " + CAPITAL + " for " + key, cache1.get(key, CAPITAL)); assertNull("Cache2 retrieval error: did not expect to retrieve " + CAPITAL + " for " + key, cache2.get(key, CAPITAL)); key = Fqn.fromString("Europe/Germany"); assertNull("Cache1 retrieval error: did not expect to retrieve " + CAPITAL + " for " + key, cache1.get(key, CAPITAL)); assertNull("Cache2 retrieval error: did not expect to retrieve " + CAPITAL + " for " + key, cache2.get(key, CAPITAL)); // check the statistics - transactions are only handled by JBoss Cache on the remote node (i.e., node2) assertEquals("Cache1 Tx Prepares error after reset: ", new Long(0), new Long(tx1.getPrepares())); assertEquals("Cache1 Tx Commits error after reset: ", new Long(0), new Long(tx1.getCommits())); assertEquals("Cache1 Tx Rollbacks error after reset: ", new Long(0), new Long(tx1.getRollbacks())); assertEquals("Cache2 Tx Prepares error after reset: ", new Long(2), new Long(tx2.getPrepares())); assertEquals("Cache2 Tx Commits error after reset: ", new Long(2), new Long(tx2.getCommits())); // rollbacks don't currently get propagated so the counter will be 0, not 1 assertEquals("Cache2 Tx Rollbacks error after reset: ", new Long(0), new Long(tx2.getRollbacks())); // reset statistics tx1.resetStatistics(); tx2.resetStatistics(); // check the statistics again assertEquals("Cache1 Tx Prepares error after reset: ", new Long(0), new Long(tx1.getPrepares())); assertEquals("Cache1 Tx Commits error after reset: ", new Long(0), new Long(tx1.getCommits())); assertEquals("Cache1 Tx Rollbacks error after reset: ", new Long(0), new Long(tx1.getRollbacks())); assertEquals("Cache2 Tx Prepares error after reset: ", new Long(0), new Long(tx2.getPrepares())); assertEquals("Cache2 Tx Commits error after reset: ", new Long(0), new Long(tx2.getCommits())); assertEquals("Cache2 Tx Rollbacks error after reset: ", new Long(0), new Long(tx2.getRollbacks())); } private void loadCacheNoTx(CacheSPI cache) { cache.put("Europe", new HashMap()); cache.put("Europe/Austria", new HashMap()); cache.put("Europe/Austria", CAPITAL, "Vienna"); cache.put("Europe/Austria", CURRENCY, "Euro"); cache.put("Europe/Austria", POPULATION, 8184691); HashMap albania = new HashMap(4); albania.put(CAPITAL, "Tirana"); albania.put(CURRENCY, "Lek"); albania.put(POPULATION, 3563112); albania.put(AREA, 28748); cache.put("Europe/Albania", albania); } private void loadCacheTxCommit(CacheSPI cache, TransactionManager tm) throws Exception { tm.begin(); cache.put("Europe/Czech Republic", new HashMap()); cache.put("Europe/Czech Republic", CAPITAL, "Prague"); cache.put("Europe/Czech Republic", CURRENCY, "Czech Koruna"); cache.put("Europe/Czech Republic", POPULATION, 10241138); cache.put("Europe/England", new HashMap()); cache.put("Europe/England", CAPITAL, "London"); cache.put("Europe/England", CURRENCY, "British Pound"); cache.put("Europe/England", POPULATION, 60441457); tm.commit(); } private void loadCacheTxCommit2(CacheSPI cache, TransactionManager tm) throws Exception { tm.begin(); HashMap hungary = new HashMap(4); hungary.put(CAPITAL, "Budapest"); hungary.put(CURRENCY, "Forint"); hungary.put(POPULATION, 10006835); hungary.put(AREA, 93030); cache.put("Europe/Hungary", hungary); HashMap romania = new HashMap(4); romania.put(CAPITAL, "Bucharest"); romania.put(CURRENCY, "Leu"); romania.put(POPULATION, 22329977); romania.put(AREA, 237500); cache.put("Europe/Romania", romania); tm.commit(); } private void loadCacheTxRollback(CacheSPI cache, TransactionManager tm) throws Exception { tm.begin(); cache.put("Europe/France", new HashMap()); cache.put("Europe/France", CAPITAL, "Paris"); cache.put("Europe/France", CURRENCY, "Euro"); cache.put("Europe/France", POPULATION, 60656178); cache.put("Europe/Germany", new HashMap()); cache.put("Europe/Germany", CAPITAL, "Berlin"); cache.put("Europe/Germany", CURRENCY, "Euro"); cache.put("Europe/Germany", POPULATION, 82431390); tm.rollback(); } private CacheSPI createCache(String clusterName) { CacheSPI cache = (CacheSPI) new DefaultCacheFactory().createCache(UnitTestCacheConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC), false); cache.getConfiguration().setUseRegionBasedMarshalling(false); cache.getConfiguration().setCacheMode(Configuration.CacheMode.REPL_SYNC); cache.getConfiguration().setExposeManagementStatistics(true); cache.getConfiguration().setClusterName(clusterName); cache.create(); cache.start(); return cache; } private TxInterceptor getTxInterceptor(CacheSPI cache) { List interceptors = cache.getInterceptorChain(); if (interceptors.isEmpty()) { return null; } for (Object o : interceptors) { if (o instanceof TxInterceptor) { return (TxInterceptor) o; } } return null; } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/mgmt/MgmtCoreTest.java0000644000175000017500000002725411005354216026627 0ustar twernertwernerpackage org.jboss.cache.mgmt; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.config.Configuration; import org.jboss.cache.interceptors.CacheMgmtInterceptor; import org.jboss.cache.interceptors.base.CommandInterceptor; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.HashMap; import java.util.List; /** * Simple functional tests for CacheMgmtInterceptor * * @author Jerry Gauthier * @version $Id: MgmtCoreTest.java 5720 2008-04-28 14:00:46Z manik.surtani@jboss.com $ */ @Test(groups = {"functional"}) public class MgmtCoreTest { private static final String CAPITAL = "capital"; private static final String CURRENCY = "currency"; private static final String POPULATION = "population"; private static final String AREA = "area"; CacheSPI cache = null; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { cache = (CacheSPI) new DefaultCacheFactory().createCache(false); cache.getConfiguration().setCacheMode(Configuration.CacheMode.LOCAL); cache.getConfiguration().setExposeManagementStatistics(true); cache.create(); cache.start(); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { if (cache != null) { cache.stop(); cache.destroy(); cache = null; } } public void testCacheMgmt() throws Exception { assertNotNull("Cache is null.", cache); // populate the cache with test data loadCacheData(); // Note: because these tests are normally executed without a server, the interceptor // MBeans are usually not available for use in the tests. Consequently it's necessary // to obtain a reference to the interceptor and work with it directly. CacheMgmtInterceptor mgmt = getCacheMgmtInterceptor(); assertNotNull("CacheMgmtInterceptor not found.", mgmt); // try some successful retrievals - fail if they miss since this shouldn't occur Fqn key = Fqn.fromString("Europe/Austria"); assertNotNull("Retrieval error: expected to retrieve " + CAPITAL + " for " + key, cache.get(key, CAPITAL)); assertNotNull("Retrieval error: expected to retrieve " + CURRENCY + " for " + key, cache.get(key, CURRENCY)); assertNotNull("Retrieval error: expected to retrieve " + POPULATION + " for " + key, cache.get(key, POPULATION)); key = Fqn.fromString("Europe/England"); assertNotNull("Retrieval error: expected to retrieve " + CAPITAL + " for " + key, cache.get(key, CAPITAL)); assertNotNull("Retrieval error: expected to retrieve " + CURRENCY + " for " + key, cache.get(key, CURRENCY)); assertNotNull("Retrieval error: expected to retrieve " + POPULATION + " for " + key, cache.get(key, POPULATION)); key = Fqn.fromString("Europe/France"); assertNotNull("Retrieval error: expected to retrieve " + CAPITAL + " for " + key, cache.get(key, CAPITAL)); assertNotNull("Retrieval error: expected to retrieve " + CURRENCY + " for " + key, cache.get(key, CURRENCY)); assertNotNull("Retrieval error: expected to retrieve " + POPULATION + " for " + key, cache.get(key, POPULATION)); key = Fqn.fromString("Europe/Germany"); assertNotNull("Retrieval error: expected to retrieve " + CAPITAL + " for " + key, cache.get(key, CAPITAL)); assertNotNull("Retrieval error: expected to retrieve " + CURRENCY + " for " + key, cache.get(key, CURRENCY)); assertNotNull("Retrieval error: expected to retrieve " + POPULATION + " for " + key, cache.get(key, POPULATION)); key = Fqn.fromString("Europe/Italy"); assertNotNull("Retrieval error: expected to retrieve " + CAPITAL + " for " + key, cache.get(key, CAPITAL)); assertNotNull("Retrieval error: expected to retrieve " + CURRENCY + " for " + key, cache.get(key, CURRENCY)); assertNotNull("Retrieval error: expected to retrieve " + POPULATION + " for " + key, cache.get(key, POPULATION)); key = Fqn.fromString("Europe/Switzerland"); assertNotNull("Retrieval error: expected to retrieve " + CAPITAL + " for " + key, cache.get(key, CAPITAL)); assertNotNull("Retrieval error: expected to retrieve " + CURRENCY + " for " + key, cache.get(key, CURRENCY)); assertNotNull("Retrieval error: expected to retrieve " + POPULATION + " for " + key, cache.get(key, POPULATION)); // try some unsuccessful retrievals - fail if they hit since this shouldn't occur key = Fqn.fromString("Europe/Austria"); assertNull("Retrieval error: did not expect to retrieve " + AREA + " for " + key, cache.get(key, AREA)); key = Fqn.fromString("Europe/England"); assertNull("Retrieval error: did not expect to retrieve " + AREA + " for " + key, cache.get(key, AREA)); key = Fqn.fromString("Europe/France"); assertNull("Retrieval error: did not expect to retrieve " + AREA + " for " + key, cache.get(key, AREA)); key = Fqn.fromString("Europe/Germany"); assertNull("Retrieval error: did not expect to retrieve " + AREA + " for " + key, cache.get(key, AREA)); key = Fqn.fromString("Europe/Italy"); assertNull("Retrieval error: did not expect to retrieve " + AREA + " for " + key, cache.get(key, AREA)); key = Fqn.fromString("Europe/Switzerland"); assertNull("Retrieval error: did not expect to retrieve " + AREA + " for " + key, cache.get(key, AREA)); // verify basic statistics for entries loaded into cache assertEquals("NumberOfNodes count error: ", new Integer(13), new Integer(mgmt.getNumberOfNodes())); assertEquals("NumberOfAttributes count error: ", new Integer(40), new Integer(mgmt.getNumberOfAttributes())); assertEquals("Stores count error: ", new Long(40), new Long(mgmt.getStores())); assertEquals("Evictions count error: ", new Long(0), new Long(mgmt.getEvictions())); assertEquals("Hits count error: ", new Long(18), new Long(mgmt.getHits())); assertEquals("Misses count error: ", new Long(6), new Long(mgmt.getMisses())); assertEquals("HitMissRatio error: ", 0.75, mgmt.getHitMissRatio()); assertEquals("ReadWriteRatio error: ", 0.60, mgmt.getReadWriteRatio()); // now evict some nodes (each node has 3 attributes) cache.evict(Fqn.fromString("Europe/Czech Republic")); cache.evict(Fqn.fromString("Europe/Poland")); assertEquals("NumberOfNodes count error after evictions: ", new Integer(11), new Integer(mgmt.getNumberOfNodes())); assertEquals("NumberOfAttributes count error after evictions: ", new Integer(34), new Integer(mgmt.getNumberOfAttributes())); assertEquals("Stores count error: ", new Long(40), new Long(mgmt.getStores())); assertEquals("Evictions count error: ", new Long(2), new Long(mgmt.getEvictions())); // time is measured in seconds so add a delay to ensure it's not rounded to zero Thread.sleep(1000); long t1 = mgmt.getElapsedTime(); if (t1 < 1) { fail("ElapsedTime should be greater than 0 seconds."); } t1 = mgmt.getTimeSinceReset(); if (t1 < 1) { fail("TimeSinceReset should be greater than 0 seconds."); } Thread.sleep(1000); // now reset the statistics (node count and attribute count aren't affected) mgmt.resetStatistics(); // check times again t1 = mgmt.getElapsedTime(); if (t1 < 2) { fail("ElapsedTime after reset should be greater than 1 second."); } t1 = mgmt.getTimeSinceReset(); if (t1 > 1)// assumes that reset takes less than 2 seconds { fail("TimeSinceReset after reset should be less than 2 seconds."); } // check other statistics assertEquals("NumberOfNodes count error after reset: ", new Integer(11), new Integer(mgmt.getNumberOfNodes())); assertEquals("NumberOfAttributes count error after reset: ", new Integer(34), new Integer(mgmt.getNumberOfAttributes())); assertEquals("Stores count error after reset: ", new Long(0), new Long(mgmt.getStores())); assertEquals("Evictions count error after reset: ", new Long(0), new Long(mgmt.getEvictions())); assertEquals("Hits count error after reset: ", new Long(0), new Long(mgmt.getHits())); assertEquals("Misses count error after reset: ", new Long(0), new Long(mgmt.getMisses())); } private void loadCacheData() { cache.put("Europe", new HashMap()); cache.put("Europe/Austria", new HashMap()); cache.put("Europe/Czech Republic", new HashMap()); cache.put("Europe/England", new HashMap()); cache.put("Europe/France", new HashMap()); cache.put("Europe/Germany", new HashMap()); cache.put("Europe/Italy", new HashMap()); cache.put("Europe/Poland", new HashMap()); cache.put("Europe/Switzerland", new HashMap()); cache.put("Europe/Austria", CAPITAL, "Vienna"); cache.put("Europe/Czech Republic", CAPITAL, "Prague"); cache.put("Europe/England", CAPITAL, "London"); cache.put("Europe/France", CAPITAL, "Paris"); cache.put("Europe/Germany", CAPITAL, "Berlin"); cache.put("Europe/Italy", CAPITAL, "Rome"); cache.put("Europe/Poland", CAPITAL, "Warsaw"); cache.put("Europe/Switzerland", CAPITAL, "Bern"); cache.put("Europe/Austria", CURRENCY, "Euro"); cache.put("Europe/Czech Republic", CURRENCY, "Czech Koruna"); cache.put("Europe/England", CURRENCY, "British Pound"); cache.put("Europe/France", CURRENCY, "Euro"); cache.put("Europe/Germany", CURRENCY, "Euro"); cache.put("Europe/Italy", CURRENCY, "Euro"); cache.put("Europe/Poland", CURRENCY, "Zloty"); cache.put("Europe/Switzerland", CURRENCY, "Swiss Franc"); cache.put("Europe/Austria", POPULATION, 8184691); cache.put("Europe/Czech Republic", POPULATION, 10241138); cache.put("Europe/England", POPULATION, 60441457); cache.put("Europe/France", POPULATION, 60656178); cache.put("Europe/Germany", POPULATION, 82431390); cache.put("Europe/Italy", POPULATION, 58103033); cache.put("Europe/Poland", POPULATION, 38635144); cache.put("Europe/Switzerland", POPULATION, 7489370); HashMap albania = new HashMap(4); albania.put(CAPITAL, "Tirana"); albania.put(CURRENCY, "Lek"); albania.put(POPULATION, 3563112); albania.put(AREA, 28748); cache.put("Europe/Albania", albania); HashMap hungary = new HashMap(4); hungary.put(CAPITAL, "Budapest"); hungary.put(CURRENCY, "Forint"); hungary.put(POPULATION, 10006835); hungary.put(AREA, 93030); cache.put("Europe/Hungary", hungary); HashMap romania = new HashMap(4); romania.put(CAPITAL, "Bucharest"); romania.put(CURRENCY, "Leu"); romania.put(POPULATION, 22329977); romania.put(AREA, 237500); cache.put("Europe/Romania", romania); HashMap slovakia = new HashMap(4); slovakia.put(CAPITAL, "Bratislava"); slovakia.put(CURRENCY, "Slovak Koruna"); slovakia.put(POPULATION, 5431363); slovakia.put(AREA, 48845); cache.put("Europe/Slovakia", slovakia); } private CacheMgmtInterceptor getCacheMgmtInterceptor() { List interceptors = cache.getInterceptorChain(); if (interceptors.isEmpty()) { return null; } for (CommandInterceptor interceptor : interceptors) { if (interceptor instanceof CacheMgmtInterceptor) { return (CacheMgmtInterceptor) interceptor; } } return null; } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/mgmt/InvalidationTest.java0000644000175000017500000001516211017455042027530 0ustar twernertwernerpackage org.jboss.cache.mgmt; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.factories.UnitTestCacheConfigurationFactory; import org.jboss.cache.interceptors.InvalidationInterceptor; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.HashMap; /** * Simple functional tests for InvalidationInterceptor statistics * * @author Jerry Gauthier * @version $Id: InvalidationTest.java 5906 2008-05-29 07:24:18Z mircea.markus $ */ @Test(groups = {"functional"}) public class InvalidationTest { private static final String CLUSTER_NAME = "InvalidationTestCluster"; private static final String CAPITAL = "capital"; private static final String CURRENCY = "currency"; private static final String POPULATION = "population"; private static final String AREA = "area"; private CacheSPI cache1 = null; private CacheSPI cache2 = null; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { cache1 = createCache(CLUSTER_NAME); cache2 = createCache(CLUSTER_NAME); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { if (cache1 != null) { cache1.stop(); cache1.destroy(); cache1 = null; } if (cache2 != null) { cache2.stop(); cache2.destroy(); cache2 = null; } } public void testInvalidationMgmt() throws Exception { assertNotNull("Cache1 is null.", cache1); assertNotNull("Cache2 is null.", cache2); // populate cache1 with test data loadCache1(cache1); // confirm that data is in cache1 and not in cache2 Fqn key = Fqn.fromString("Europe/Austria"); assertNotNull("Cache1 retrieval error: expected to retrieve " + CAPITAL + " for " + key, cache1.get(key, CAPITAL)); assertNull("Cache2 retrieval error: did not expect to retrieve " + CAPITAL + " for " + key, cache2.get(key, CAPITAL)); key = Fqn.fromString("Europe/Albania"); assertNotNull("Cache1 retrieval error: expected to retrieve " + CAPITAL + " for " + key, cache1.get(key, CAPITAL)); assertNull("Cache2 retrieval error: did not expect to retrieve " + CAPITAL + " for " + key, cache2.get(key, CAPITAL)); // populate cache2 with test data - this will invalidate Austria loadCache2(cache2); // confirm that Austria is now in cache2 and not in cache1 key = Fqn.fromString("Europe/Austria"); assertNull("Cache1 retrieval error: did not expect to retrieve " + CAPITAL + " for " + key, cache1.get(key, CAPITAL)); assertNotNull("Cache2 retrieval error: expected to retrieve " + CAPITAL + " for " + key, cache2.get(key, CAPITAL)); // confirm that Albania is still in cache1 key = Fqn.fromString("Europe/Albania"); assertNotNull("Cache1 retrieval error after unrelated eviction: expected to retrieve " + CAPITAL + " for " + key, cache1.get(key, CAPITAL)); // Note: because these tests are normally executed without a server, the interceptor // MBeans are usually not available for use in the tests. Consequently it's necessary // to obtain a reference to the interceptor and work with it directly. InvalidationInterceptor mgmt1 = TestingUtil.findInterceptor(cache1, InvalidationInterceptor.class); assertNotNull("Cache1 InvalidationInterceptor not found.", mgmt1); InvalidationInterceptor mgmt2 = TestingUtil.findInterceptor(cache2, InvalidationInterceptor.class); assertNotNull("Cache2 InvalidationInterceptor not found.", mgmt2); assertTrue("Cache1 not configured to use MBeans", cache1.getConfiguration().getExposeManagementStatistics()); assertTrue("Cache2 not configured to use MBeans", cache2.getConfiguration().getExposeManagementStatistics()); assertTrue("InvalidationInterceptor on Cache1 not set up to use statistics!", mgmt1.getStatisticsEnabled()); assertTrue("InvalidationInterceptor on Cache2 not set up to use statistics!", mgmt2.getStatisticsEnabled()); // verify basic statistics for entries loaded into cache assertEquals("Cache1 Invalidations count error: ", new Long(6), new Long(mgmt1.getInvalidations())); assertEquals("Cache2 Invalidations count error: ", new Long(9), new Long(mgmt2.getInvalidations())); // reset statistics mgmt1.resetStatistics(); mgmt2.resetStatistics(); // check the statistics again assertEquals("Cache1 Invalidations count error after reset: ", new Long(0), new Long(mgmt1.getInvalidations())); assertEquals("Cache2 Invalidations count error after reset: ", new Long(0), new Long(mgmt2.getInvalidations())); } private void loadCache1(CacheSPI cache) { cache.put("Europe", new HashMap()); cache.put("Europe/Austria", new HashMap()); cache.put("Europe/Austria", CAPITAL, "Vienna"); cache.put("Europe/Austria", CURRENCY, "Euro"); cache.put("Europe/Austria", POPULATION, 8184691); HashMap albania = new HashMap(4); albania.put(CAPITAL, "Tirana"); albania.put(CURRENCY, "Lek"); albania.put(POPULATION, 3563112); albania.put(AREA, 28748); cache.put("Europe/Albania", albania); } private void loadCache2(CacheSPI cache) { // the following line will invalidate /Europe (and its' children) across the cluster!! // cache.put("Europe", new HashMap()); cache.put("Europe/Austria", new HashMap()); cache.put("Europe/Austria", CAPITAL, "Vienna"); cache.put("Europe/Austria", CURRENCY, "Euro"); cache.put("Europe/Austria", POPULATION, 8184691); cache.put("Europe/Romania", new HashMap()); cache.put("Europe/Romania", CAPITAL, "Bucharest"); cache.put("Europe/Romania", CURRENCY, "Leu"); cache.put("Europe/Romania", POPULATION, 22329977); cache.put("Europe/Romania", AREA, 237500); } private CacheSPI createCache(String clusterName) { Configuration c = UnitTestCacheConfigurationFactory.createConfiguration(CacheMode.INVALIDATION_SYNC); c.setCacheMode(Configuration.CacheMode.INVALIDATION_SYNC); c.setExposeManagementStatistics(true); c.setClusterName(clusterName); return (CacheSPI) new DefaultCacheFactory().createCache(c); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/mgmt/MgmtTestBase.java0000644000175000017500000001035710732232543026611 0ustar twernertwernerpackage org.jboss.cache.mgmt; import org.jboss.cache.CacheFactory; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.XmlConfigurationParser; import org.jboss.cache.loader.CacheLoader; import org.jboss.cache.loader.DummyInMemoryCacheLoader; import org.jboss.cache.xml.XmlHelper; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import org.w3c.dom.Element; import java.util.HashMap; import java.util.Map; @Test(groups = "functional") public abstract class MgmtTestBase { protected static final String CAPITAL = "capital"; protected static final String CURRENCY = "currency"; protected static final String POPULATION = "population"; protected static final String AREA = "area"; protected static final Fqn EUROPE = Fqn.fromString("/Europe"); protected static final Fqn AUSTRIA = Fqn.fromString("/Europe/Austria"); protected static final Fqn ENGLAND = Fqn.fromString("/Europe/England"); protected static final Fqn ALBANIA = Fqn.fromString("/Europe/Albania"); protected static final Fqn HUNGARY = Fqn.fromString("/Europe/Hungary"); protected static final Fqn POLAND = Fqn.fromString("/Europe/Poland"); protected boolean passivation = false; protected CacheSPI cache = null; protected CacheLoader cl; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { cache = createCache(); // populate cache with test data loadCache(); cl = cache.getCacheLoaderManager().getCacheLoader(); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { if (cache != null) { cache.stop(); cache.destroy(); cache = null; } } /** * Should trigger 2 cache load events */ protected void loadCache() throws Exception { cache.put(EUROPE, null); Map austria = new HashMap(); austria.put(CAPITAL, "VIENNA"); austria.put(CURRENCY, "Euro"); austria.put(POPULATION, 8184691); cache.put(AUSTRIA, austria); Map england = new HashMap(); england.put(CAPITAL, "London"); england.put(CURRENCY, "British Pound"); england.put(POPULATION, 60441457); cache.put(ENGLAND, england); Map albania = new HashMap(4); albania.put(CAPITAL, "Tirana"); albania.put(CURRENCY, "Lek"); albania.put(POPULATION, 3563112); albania.put(AREA, 28748); cache.put(ALBANIA, albania); Map hungary = new HashMap(4); hungary.put(CAPITAL, "Budapest"); hungary.put(CURRENCY, "Forint"); hungary.put(POPULATION, 10006835); hungary.put(AREA, 93030); cache.put(HUNGARY, hungary); } private CacheSPI createCache() throws Exception { CacheFactory instance = new DefaultCacheFactory(); Configuration c = new Configuration(); c.setCacheMode(Configuration.CacheMode.LOCAL); c.setCacheLoaderConfig(getCacheLoaderConfig()); c.setExposeManagementStatistics(true); CacheSPI cache = (CacheSPI) instance.createCache(c, false); cache.create(); cache.start(); return cache; } private CacheLoaderConfig getCacheLoaderConfig() throws Exception { String xml = "\n" + "" + passivation + "\n" + "\n" + "false\n" + "\n" + "" + DummyInMemoryCacheLoader.class.getName() + "\n" + "debug=true\n" + "false\n" + "false\n" + "false\n" + "\n" + ""; Element element = XmlHelper.stringToElement(xml); return XmlConfigurationParser.parseCacheLoaderConfig(element); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/eviction/0000755000175000017500000000000011376173734024270 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/eviction/FIFOPolicyTest.java0000644000175000017500000002451411020007335027657 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.eviction; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.util.internals.EvictionController; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.EvictionConfig; import org.jboss.cache.config.EvictionRegionConfig; import org.jboss.cache.factories.UnitTestCacheConfigurationFactory; import org.jboss.cache.lock.IsolationLevel; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.List; import java.util.ArrayList; /** * Unit tests for FIFOPolicy. * * @author Daniel Huang (dhuang@jboss.org) * @version $Revision: 5919 $ */ @Test(groups = {"functional"}) public class FIFOPolicyTest { CacheSPI cache; int wakeupIntervalMillis = 0; final String ROOT_STR = "/test"; Throwable t1_ex, t2_ex; final long DURATION = 10000; boolean isTrue; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { initCaches(); wakeupIntervalMillis = cache.getConfiguration().getEvictionConfig().getWakeupIntervalSeconds() * 1000; log("wakeupInterval is " + wakeupIntervalMillis); if (wakeupIntervalMillis < 0) { fail("testEviction(): eviction thread wake up interval is illegal " + wakeupIntervalMillis); } t1_ex = t2_ex = null; isTrue = true; } void initCaches() throws Exception { Configuration config = UnitTestCacheConfigurationFactory.createConfiguration(Configuration.CacheMode.LOCAL, true); EvictionConfig evConfig = config.getEvictionConfig(); evConfig.setWakeupIntervalSeconds(3); evConfig.setDefaultEventQueueSize(20000); evConfig.setDefaultEvictionPolicyClass("org.jboss.cache.eviction.FIFOPolicy"); List erConfigs = new ArrayList(); erConfigs.add(createEvictionRegionConfig("/_default_",5000)); erConfigs.add(createEvictionRegionConfig("/org/jboss/test/data",5)); evConfig.setEvictionRegionConfigs(erConfigs); config.setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); config.setIsolationLevel(IsolationLevel.SERIALIZABLE); cache = (CacheSPI) new DefaultCacheFactory().createCache(config, true);// read in generic local xml } private EvictionRegionConfig createEvictionRegionConfig(String regionName, int maxNodes) { EvictionRegionConfig ercDefault = new EvictionRegionConfig(); ercDefault.setRegionName(regionName); FIFOConfiguration esConfig = new FIFOConfiguration(); if (maxNodes >= 0) esConfig.setMaxNodes(maxNodes); ercDefault.setEvictionPolicyConfig(esConfig); return ercDefault; } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { cache.stop(); } public void testEviction() { String rootStr = "/org/jboss/test/data/"; for (int i = 0; i < 10; i++) { String str = rootStr + i; Fqn fqn = Fqn.fromString(str); try { cache.put(fqn, str, str); } catch (Exception e) { fail("Failed to insert data" + e); e.printStackTrace(); } } EvictionController evictionController = new EvictionController(cache); evictionController.startEviction(); try { String val = (String) cache.get(rootStr + "3", rootStr + "3"); assertNull("DataNode should be empty ", val); assertNull(cache.get(rootStr + "1", rootStr + "1")); assertNull(cache.get(rootStr + "2", rootStr + "2")); assertNull(cache.get(rootStr + "0", rootStr + "0")); assertNull(cache.get(rootStr + "4", rootStr + "4")); assertNotNull(cache.get(rootStr + "5", rootStr + "5")); } catch (Exception e) { e.printStackTrace(); fail("Failed to get" + e); } } public void testEviction2() throws Exception { String rootStr = "/org/jboss/data"; int maxNodes = 5000; for (int i = 0; i < maxNodes * 2; i++) { String str = rootStr + i; Fqn fqn = Fqn.fromString(str); cache.put(fqn, str, str); } EvictionController evictionController = new EvictionController(cache); evictionController.startEviction(); assertEquals("Number of nodes", maxNodes + 2, cache.getNumberOfNodes()); for (int i = 0; i < maxNodes; i++) { String n = rootStr + i; assertNull("DataNode should be empty " + cache.getNode(n) + " " + n, cache.get(n, n)); } for (int i = maxNodes; i < maxNodes * 2; i++) { assertNotNull(cache.get(rootStr + Integer.toString(i), rootStr + Integer.toString(i))); } // add another node to cache. this should push node 5000 out of cache. cache.put(rootStr + "a", rootStr + "a", rootStr + "a"); for (int i = maxNodes + 1; i < maxNodes; i++) { assertNotNull(cache.get(rootStr + Integer.toString(i), rootStr + Integer.toString(i))); } assertNotNull(cache.get(rootStr + "a", rootStr + "a")); } public void testNodeVisited() { String rootStr = "/org/jboss/test/data/"; for (int i = 0; i < 10; i++) { String str = rootStr + i; Fqn fqn = Fqn.fromString(str); try { cache.put(fqn, str, str); } catch (Exception e) { fail("Failed to insert data" + e); e.printStackTrace(); } } int period = wakeupIntervalMillis + 500; log("sleeping for " + period + "ms"); EvictionController evictionController = new EvictionController(cache); evictionController.startEviction(); try { for (int i = 0; i < 5; i++) { String str = rootStr + Integer.toString(i); Fqn fqn = Fqn.fromString(str); assertNull(cache.get(fqn, str)); } for (int i = 5; i < 10; i++) { String str = rootStr + Integer.toString(i); Fqn fqn = Fqn.fromString(str); assertNotNull(cache.get(fqn, str)); } evictionController.startEviction(); // since it is FIFO if we leave it alone and revisit, cache should remain the same. for (int i = 5; i < 10; i++) { String str = rootStr + Integer.toString(i); Fqn fqn = Fqn.fromString(str); cache.get(fqn, str);// just to keep it fresh System.out.println("-- sleeping for " + period + "ms"); } } catch (Exception e) { e.printStackTrace(); fail("Failed to evict" + e); } } public void testNodeRemoved() { String rootStr = "/org/jboss/test"; for (int i = 0; i < 10; i++) { String str = rootStr + i + "/" + i; Fqn fqn = Fqn.fromString(str); String str2 = rootStr + i; Fqn fqn2 = Fqn.fromString(str2); try { cache.put(fqn, str, str); cache.put(fqn2, str2, str2); } catch (Exception e) { fail("Failed to insert data" + e); e.printStackTrace(); } } EvictionController evictionController = new EvictionController(cache); evictionController.startEviction(); String str1 = rootStr + "7"; Fqn fqn1 = Fqn.fromString(str1); String str2 = rootStr + "7/7"; Fqn fqn2 = Fqn.fromString(str2); try { assertNotNull(cache.get(fqn2, str2)); assertNotNull(cache.get(fqn1, str1)); cache.removeNode(fqn2); evictionController.startEviction(); assertNull(cache.get(fqn2, str2)); assertNotNull(cache.get(fqn1, str1)); cache.removeNode(fqn1); evictionController.startEviction(); assertNull(cache.get(fqn1, str1)); assertNull(cache.get(fqn2, str2)); String str3 = rootStr + "5/5"; String str4 = rootStr + "5"; Fqn fqn3 = Fqn.fromString(str3); Fqn fqn4 = Fqn.fromString(str4); assertNotNull(cache.get(fqn3, str3)); assertNotNull(cache.get(fqn4, str4)); evictionController.startEviction(); // remove the node above fqn4 /org/jboss/test/5 will cascade the delete into /org/jboss/test/5/5 cache.removeNode(fqn4); evictionController.startEviction(); assertNull(cache.get(fqn3, str3)); assertNull(cache.get(fqn4, str4)); } catch (Exception e) { e.printStackTrace(); fail("Failed to evict" + e); } } class MyPutter extends Thread { public MyPutter(String name) { super(name); } public void run() { int i = 0; final String myName = ROOT_STR + "/test1/node" + getName(); while (isTrue) { try { cache.put(myName + i++, "value", i); sleep(1); } catch (Throwable e) { e.printStackTrace(); if (t1_ex == null) { t1_ex = e; } } } } } public void testConcurrentPutAndEvict() throws Exception { cache.stop(); cache.destroy(); cache.getConfiguration().setIsolationLevel(IsolationLevel.REPEATABLE_READ); cache.start(); cache.put(ROOT_STR + "/concurrentPutAndEvict", "value", 1); for (int i = 0; i < 10; i++) { new MyPutter("Putter" + i).start(); } int counter = 0; while (true) { counter++; if (t1_ex != null) { fail("Exception generated in put() " + t1_ex); } log("nodes/locks: " + cache.getNumberOfNodes() + "/" + cache.getNumberOfLocksHeld()); TestingUtil.sleepThread(1000); if (counter > 10) {// run for 10 seconds isTrue = false; break; } } } void log(String msg) { System.out.println("-- " + msg); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/eviction/LFUAlgorithmTest.java0000644000175000017500000003751611002145716030265 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.eviction; import org.jboss.cache.Fqn; import org.jboss.cache.Region; import org.jboss.cache.RegionManager; import static org.testng.AssertJUnit.*; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.Iterator; /** * Unit test for LFUAlgorithm. * * @author Daniel Huang - dhuang@jboss.org - 10/2005 * @version $Revision: 5597 $ */ @Test(groups = {"functional"}) public class LFUAlgorithmTest { RegionManager regionManager; LFUAlgorithm algo; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { algo = new LFUAlgorithm(); LFUConfiguration config = new LFUConfiguration(); regionManager = new RegionManager(); config.setEvictionPolicyClass(DummyEvictionPolicy.class.getName()); regionManager.getRegion("/a/b", true).setEvictionPolicy(config); // doesn't this need a cache?!?? :-/ } public void testMaxNode1() { Fqn fqn1 = Fqn.fromString("/a/b/c"); Fqn fqn2 = Fqn.fromString("/a/b/d"); Region region = regionManager.getRegion("/a/b", true); LFUConfiguration config = new LFUConfiguration(); config.setMaxNodes(0); config.setMinNodes(20); region.setEvictionPolicy(config); region.putNodeEvent(new EvictedEventNode(fqn1, NodeEventType.ADD_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn2, NodeEventType.ADD_NODE_EVENT)); try { algo.process(region); } catch (EvictionException e) { fail("testMaxNode: process failed " + e); e.printStackTrace(); } assertEquals("Queue size should be ", 2, algo.getEvictionQueue().getNumberOfNodes()); } public void testMaxNode2() { Fqn fqn1 = Fqn.fromString("/a/b/c"); Fqn fqn2 = Fqn.fromString("/a/b/d"); Fqn fqn3 = Fqn.fromString("/a/b/e"); Region region = regionManager.getRegion("/a/b", true); LFUConfiguration config = new LFUConfiguration(); config.setMaxNodes(1); config.setMinNodes(20); region.setEvictionPolicy(config); region.putNodeEvent(new EvictedEventNode(fqn1, NodeEventType.ADD_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn2, NodeEventType.ADD_NODE_EVENT)); try { algo.process(region); } catch (EvictionException e) { fail("testMaxNode: process failed " + e); e.printStackTrace(); } assertEquals("Queue size should be ", 1, algo.getEvictionQueue().getNumberOfNodes()); region.putNodeEvent(new EvictedEventNode(fqn2, NodeEventType.ADD_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn3, NodeEventType.ADD_NODE_EVENT)); try { algo.process(region); } catch (EvictionException e) { fail("testMaxNode: process failed " + e); e.printStackTrace(); } assertEquals("Queue size should be ", 1, algo.getEvictionQueue().getNumberOfNodes()); } // What's this doing here? This should be a stress test, not a functional test. There are no assertions, for // example. :S - Manik, Nov 06 // public void testMaxNode3() throws Exception // { // Region region = regionManager.getRegion("/a/b", true); // LFUConfiguration config = new LFUConfiguration(); // // config.setMaxNodes(15000); // config.setMinNodes(15000); // // region.setEvictionPolicy(config); // for (int i = 0; i < 20000; i++) // { // Fqn fqn = Fqn.fromString("/a/b/" + Integer.toString(i)); // region.putNodeEvent(new EvictedEventNode(fqn, NodeEventType.ADD_NODE_EVENT)); // // if ((i % 2) == 0) // { // region.putNodeEvent(new EvictedEventNode(fqn, NodeEventType.VISIT_NODE_EVENT)); // } // } // // algo.invokeRemote(region); //// LFUQueue queue = (LFUQueue) algo.evictionQueue; //// Iterator it = queue.iterate(); // // } public void testMinNode1() throws Exception { Fqn fqn1 = Fqn.fromString("/a/b/c"); Fqn fqn2 = Fqn.fromString("/a/b/c/d"); Fqn fqn3 = Fqn.fromString("/a/b/c/d/e"); Fqn fqn4 = Fqn.fromString("/a/b/c/d/e/f"); Region region = regionManager.getRegion("/a/b", true); LFUConfiguration config = (LFUConfiguration) region.getEvictionPolicyConfig(); config.setMaxNodes(0); config.setMinNodes(2); region.putNodeEvent(new EvictedEventNode(fqn1, NodeEventType.ADD_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn2, NodeEventType.ADD_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn3, NodeEventType.ADD_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn4, NodeEventType.ADD_NODE_EVENT)); algo.process(region); assertEquals("Queue size should be ", 2, algo.getEvictionQueue().getNumberOfNodes()); } public void testMinNode2() throws Exception { Fqn fqn1 = Fqn.fromString("/a/b/c"); Fqn fqn2 = Fqn.fromString("/a/b/d"); Region region = regionManager.getRegion("/a/b", true); LFUConfiguration config = (LFUConfiguration) region.getEvictionPolicyConfig(); config.setMaxNodes(0); config.setMinNodes(0); region.putNodeEvent(new EvictedEventNode(fqn1, NodeEventType.ADD_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn2, NodeEventType.ADD_NODE_EVENT)); algo.process(region); assertEquals("Queue size should be ", 0, algo.getEvictionQueue().getNumberOfNodes()); } public void testEvictionQueueSortOrder1() throws Exception { Fqn fqn1 = Fqn.fromString("/a/b/c"); Fqn fqn2 = Fqn.fromString("/a/b/c/d"); Fqn fqn3 = Fqn.fromString("/a/b/c/d/e"); Fqn fqn4 = Fqn.fromString("/a/b/c/d/e/f"); Fqn fqn5 = Fqn.fromString("/a/b/c/d/e/f/g/h"); Fqn fqn6 = Fqn.fromString("/a/b/c/d/e/f/g/h/i"); Fqn fqn7 = Fqn.fromString("/a/b/c/d/e/f/g/h/i/j"); Fqn fqn8 = Fqn.fromString("/a/b/c/d/e/f/g/h/i/j/k"); Fqn fqn9 = Fqn.fromString("/a/b/c/d/e/f/g/h/i/j/k/l"); Fqn fqn10 = Fqn.fromString("/a/b/c/d/e/f/g/h/i/j/k/l/m"); Region region = regionManager.getRegion("/a/b", true); LFUConfiguration config = (LFUConfiguration) region.getEvictionPolicyConfig(); config.setMaxNodes(0); config.setMinNodes(100); region.putNodeEvent(new EvictedEventNode(fqn1, NodeEventType.ADD_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn2, NodeEventType.ADD_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn3, NodeEventType.ADD_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn4, NodeEventType.ADD_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn5, NodeEventType.ADD_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn6, NodeEventType.ADD_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn7, NodeEventType.ADD_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn8, NodeEventType.ADD_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn9, NodeEventType.ADD_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn10, NodeEventType.ADD_NODE_EVENT)); algo.process(region); LFUQueue queue = (LFUQueue) algo.evictionQueue; assertEquals(10, algo.getEvictionQueue().getNumberOfNodes()); Iterator it = queue.iterate(); while (it.hasNext()) { NodeEntry ne = (NodeEntry) it.next(); System.out.println("Fqn: " + ne.getFqn() + " NodeVisits: " + ne.getNumberOfNodeVisits()); assertEquals(1, ne.getNumberOfNodeVisits()); } // fqn1 visited 4 additional times. region.putNodeEvent(new EvictedEventNode(fqn1, NodeEventType.VISIT_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn1, NodeEventType.VISIT_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn1, NodeEventType.VISIT_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn1, NodeEventType.VISIT_NODE_EVENT)); // fqn2 visited 3 additional times. region.putNodeEvent(new EvictedEventNode(fqn2, NodeEventType.VISIT_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn2, NodeEventType.VISIT_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn2, NodeEventType.VISIT_NODE_EVENT)); // fqn3 visited 1 additional time. region.putNodeEvent(new EvictedEventNode(fqn3, NodeEventType.VISIT_NODE_EVENT)); // fqn4 visited 2 additional times. region.putNodeEvent(new EvictedEventNode(fqn4, NodeEventType.VISIT_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn4, NodeEventType.VISIT_NODE_EVENT)); // fqn9 visited 1 additional time. region.putNodeEvent(new EvictedEventNode(fqn9, NodeEventType.VISIT_NODE_EVENT)); // fqn10 visited 2 additional times. region.putNodeEvent(new EvictedEventNode(fqn10, NodeEventType.VISIT_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn10, NodeEventType.VISIT_NODE_EVENT)); algo.process(region); System.out.println(); System.out.println(); it = queue.iterate(); int count = 0; while (it.hasNext()) { count++; NodeEntry ne = (NodeEntry) it.next(); System.out.println("Fqn: " + ne.getFqn() + " NodeVisits: " + ne.getNumberOfNodeVisits()); if (count == 5 || count == 6) { assertEquals(2, ne.getNumberOfNodeVisits()); } else if (count == 7 || count == 8) { assertEquals(3, ne.getNumberOfNodeVisits()); } else if (count == 9) { assertEquals(4, ne.getNumberOfNodeVisits()); } else if (count == 10) { assertEquals(5, ne.getNumberOfNodeVisits()); } else { assertEquals(1, ne.getNumberOfNodeVisits()); } } assertEquals(10, algo.getEvictionQueue().getNumberOfNodes()); Fqn fqn11 = Fqn.fromString("/a"); Fqn fqn12 = Fqn.fromString("/a/b"); region.putNodeEvent(new EvictedEventNode(fqn11, NodeEventType.ADD_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn12, NodeEventType.ADD_NODE_EVENT)); algo.process(region); System.out.println(); System.out.println(); it = queue.iterate(); count = 0; while (it.hasNext()) { count++; NodeEntry ne = (NodeEntry) it.next(); System.out.println("Fqn: " + ne.getFqn() + " NodeVisits: " + ne.getNumberOfNodeVisits()); if (count == 7 || count == 8) { assertEquals(2, ne.getNumberOfNodeVisits()); } else if (count == 9 || count == 10) { assertEquals(3, ne.getNumberOfNodeVisits()); } else if (count == 11) { assertEquals(4, ne.getNumberOfNodeVisits()); } else if (count == 12) { assertEquals(5, ne.getNumberOfNodeVisits()); } else { assertEquals(1, ne.getNumberOfNodeVisits()); } } assertEquals(12, algo.getEvictionQueue().getNumberOfNodes()); region.putNodeEvent(new EvictedEventNode(fqn1, NodeEventType.REMOVE_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn11, NodeEventType.REMOVE_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn12, NodeEventType.REMOVE_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn10, NodeEventType.REMOVE_NODE_EVENT)); algo.process(region); System.out.println(); System.out.println(); it = queue.iterate(); count = 0; while (it.hasNext()) { count++; NodeEntry ne = (NodeEntry) it.next(); System.out.println("Fqn: " + ne.getFqn() + " NodeVisits: " + ne.getNumberOfNodeVisits()); if (count == 5 || count == 6) { assertEquals(2, ne.getNumberOfNodeVisits()); } else if (count == 7) { assertEquals(3, ne.getNumberOfNodeVisits()); } else if (count == 8) { assertEquals(4, ne.getNumberOfNodeVisits()); } else { assertEquals(1, ne.getNumberOfNodeVisits()); } } assertEquals(8, algo.getEvictionQueue().getNumberOfNodes()); //test add/visit/remove combination region.putNodeEvent(new EvictedEventNode(fqn11, NodeEventType.ADD_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn11, NodeEventType.VISIT_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn11, NodeEventType.VISIT_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn11, NodeEventType.VISIT_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn11, NodeEventType.VISIT_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn11, NodeEventType.VISIT_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn4, NodeEventType.VISIT_NODE_EVENT)); // purposefully revisit a node that has been removed. assert that it is readded. region.putNodeEvent(new EvictedEventNode(fqn1, NodeEventType.VISIT_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn1, NodeEventType.VISIT_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn3, NodeEventType.REMOVE_NODE_EVENT)); algo.process(region); System.out.println(); System.out.println(); it = queue.iterate(); count = 0; while (it.hasNext()) { count++; NodeEntry ne = (NodeEntry) it.next(); System.out.println("Fqn: " + ne.getFqn() + " NodeVisits: " + ne.getNumberOfNodeVisits()); if (count == 5 || count == 6) { assertEquals(2, ne.getNumberOfNodeVisits()); } else if (count == 7 || count == 8) { assertEquals(4, ne.getNumberOfNodeVisits()); } else if (count == 9) { assertEquals(6, ne.getNumberOfNodeVisits()); } else { assertEquals(1, ne.getNumberOfNodeVisits()); } } assertEquals(9, algo.getEvictionQueue().getNumberOfNodes()); } public void testEvictionQueueSortOrder2() throws Exception { Region region = regionManager.getRegion("/a/b", true); LFUConfiguration config = (LFUConfiguration) region.getEvictionPolicyConfig(); config.setMaxNodes(0); config.setMinNodes(10000); for (int i = 0; i < 10000; i++) { Fqn fqn = Fqn.fromString("/a/b/" + Integer.toString(i)); region.putNodeEvent(new EvictedEventNode(fqn, NodeEventType.ADD_NODE_EVENT)); } algo.process(region); LFUQueue queue = (LFUQueue) algo.evictionQueue; Iterator it = queue.iterate(); long lastModifiedTimestamp = 0; while (it.hasNext()) { NodeEntry ne = (NodeEntry) it.next(); assertTrue(lastModifiedTimestamp <= ne.getModifiedTimeStamp()); lastModifiedTimestamp = ne.getModifiedTimeStamp(); } for (int i = 0; i < 10000; i++) { if ((i % 2) == 0) { Fqn fqn = Fqn.fromString("/a/b/" + Integer.toString(i)); region.putNodeEvent(new EvictedEventNode(fqn, NodeEventType.VISIT_NODE_EVENT)); } } algo.process(region); it = queue.iterate(); int count = 0; lastModifiedTimestamp = 0; while (it.hasNext()) { NodeEntry ne = (NodeEntry) it.next(); assertTrue(lastModifiedTimestamp <= ne.getModifiedTimeStamp()); lastModifiedTimestamp = ne.getModifiedTimeStamp(); if (count < 5000) { assertEquals(1, ne.getNumberOfNodeVisits()); } else { assertEquals(2, ne.getNumberOfNodeVisits()); } count++; } assertEquals(10000, algo.getEvictionQueue().getNumberOfNodes()); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/eviction/ElementSizeQueueTest.java0000644000175000017500000001360210665031725031217 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.eviction; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertFalse; import static org.testng.AssertJUnit.assertNotNull; import static org.testng.AssertJUnit.assertNull; import static org.testng.AssertJUnit.assertTrue; import java.util.Iterator; import java.util.List; import java.util.Set; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * @author Daniel Huang * @version $Revision: 4444 $ */ @Test(groups = {"functional"}) public class ElementSizeQueueTest { private ElementSizeQueue queue; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { queue = new ElementSizeQueue(); } public void testQueue() throws Exception { NodeEntry ne; for (int i = 0; i < 500; i++) { ne = new NodeEntry("/a/b/c/" + Integer.toString(i)); queue.addNodeEntry(ne); } queue.resortEvictionQueue(); assertEquals(500, queue.getNumberOfNodes()); assertTrue(queue.containsNodeEntry(new NodeEntry("/a/b/c/250"))); NodeEntry ne275 = queue.getNodeEntry("/a/b/c/275"); assertEquals("/a/b/c/275", ne275.getFqn().toString()); // now make sure the ordering is correct. Iterator it = queue.iterate(); int k = 0; while (it.hasNext()) { ne = (NodeEntry) it.next(); assertEquals("/a/b/c/" + Integer.toString(k), ne.getFqn().toString()); if (k % 2 == 0) { ne.setNumberOfElements(k); } k++; } queue.resortEvictionQueue(); k = 0; while (it.hasNext()) { ne = (NodeEntry) it.next(); System.out.println(ne.toString()); if (k < 250) { assertEquals(k, ne.getNumberOfElements()); assertEquals(0, k % 2); } else { assertTrue(k % 2 != 0); assertEquals(0, ne.getNumberOfElements()); } k++; } } public void testPrune() throws Exception { for (int i = 0; i < 5000; i++) { queue.addNodeEntry(new NodeEntry("/a/b/c/" + Integer.toString(i))); } NodeEntry ne; Iterator it = queue.iterate(); int i = 0; while (it.hasNext()) { ne = (NodeEntry) it.next(); if (i % 2 == 0) { queue.removeNodeEntry(ne); } i++; } assertEquals(2500, queue.getNumberOfNodes()); Set removalQueue = queue.getRemovalQueue(); List evictionList = queue.getEvictionList(); assertEquals(2500, removalQueue.size()); it = removalQueue.iterator(); while (it.hasNext()) { ne = (NodeEntry) it.next(); int currentIndex = Integer.parseInt((String) ne.getFqn().get(3)); assertEquals(0, currentIndex % 2); assertFalse(queue.containsNodeEntry(ne)); assertNull(queue.getNodeEntry(ne.getFqn())); assertTrue(evictionList.contains(ne)); } assertEquals(5000, evictionList.size()); queue.prune(); assertEquals(0, removalQueue.size()); assertEquals(2500, evictionList.size()); } public void testGetFirstNodeEntry() throws Exception { for (int i = 0; i < 500; i++) { NodeEntry ne = new NodeEntry("/a/b/c/" + Integer.toString(i)); queue.addNodeEntry(ne); if (i % 2 == 0) { ne.setNumberOfElements(2); } } queue.resortEvictionQueue(); NodeEntry ne; int count = 0; while ((ne = queue.getFirstNodeEntry()) != null) { if (count < 250) { assertEquals(2, ne.getNumberOfElements()); } else { assertEquals(0, ne.getNumberOfNodeVisits()); } queue.removeNodeEntry(ne); count++; } assertEquals(0, queue.getNumberOfNodes()); } public void testNumElements() throws Exception { ElementSizeQueue queue = new ElementSizeQueue(); NodeEntry ne = new NodeEntry("/a/b/c"); ne.setNumberOfElements(50); queue.addNodeEntry(ne); queue.resortEvictionQueue(); assertEquals(50, queue.getNumberOfElements()); assertEquals(1, queue.getNumberOfNodes()); queue.removeNodeEntry(ne); assertEquals(0, queue.getNumberOfElements()); for (int i = 0; i < 10; i++) { ne = new NodeEntry("/a/b/c/" + Integer.toString(i)); ne.setNumberOfElements(i); queue.addNodeEntry(ne); } queue.resortEvictionQueue(); assertEquals(45, queue.getNumberOfElements()); assertEquals(10, queue.getNumberOfNodes()); ne = queue.getNodeEntry("/a/b/c/0"); assertNotNull(ne); assertEquals(0, ne.getNumberOfElements()); ne.setNumberOfElements(500); assertEquals(545, queue.getNumberOfElements()); ne = queue.getNodeEntry("/a/b/c/0"); assertEquals(500, ne.getNumberOfElements()); queue.resortEvictionQueue(); ne = queue.getNodeEntry("/a/b/c/1"); assertNotNull(ne); assertEquals(1, ne.getNumberOfElements()); queue.resortEvictionQueue(); ne.setNumberOfElements(2); queue.resortEvictionQueue(); assertEquals(546, queue.getNumberOfElements()); queue.removeNodeEntry(ne); assertEquals(544, queue.getNumberOfElements()); assertEquals(9, queue.getNumberOfNodes()); queue.removeNodeEntry(queue.getNodeEntry("/a/b/c/0")); for (int i = 2; i < 10; i++) { ne = queue.getNodeEntry("/a/b/c/" + Integer.toString(i)); assertEquals(i, ne.getNumberOfElements()); queue.removeNodeEntry(ne); } assertEquals(0, queue.getNumberOfNodes()); assertEquals(0, queue.getNumberOfElements()); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/eviction/NullEvictionConfigTest.java0000644000175000017500000000456710741757474031554 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.eviction; import static org.testng.AssertJUnit.fail; import org.jboss.cache.factories.XmlConfigurationParser; import org.jboss.cache.xml.XmlHelper; import org.testng.annotations.Test; import org.w3c.dom.Element; /** * Unit tests for NullEvictionPolicyConfig. * * @author Daniel Huang (dhuang@jboss.org) * @version $Revision: 4444 $ */ @Test(groups = {"functional"}) public class NullEvictionConfigTest { /** * Creates a bunch of region elements with LRU configs and confirms * that NullEvictionPolicyConfig doesn't barf. * * @throws Exception */ public void testXMLParsing() throws Exception { String xml = "\n" + "5000\n" + "1000\n" + ""; testConfigBlock(xml); xml = "\n" + "10000\n" + "8\n" + "10\n" + ""; testConfigBlock(xml); xml = "\n" + "10000\n" + "10\n" + ""; testConfigBlock(xml); xml = "\n" + "8\n" + "10\n" + ""; testConfigBlock(xml); xml = "\n"; testConfigBlock(xml); } /** * FIXME Comment this * * @param xml * @throws Exception */ private void testConfigBlock(String xml) throws Exception { Element element = XmlHelper.stringToElement(xml); NullEvictionPolicyConfig config = new NullEvictionPolicyConfig(); try { XmlConfigurationParser.parseEvictionPolicyConfig(element, config); } catch (Exception e) { fail(e.getMessage()); } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/eviction/MRUConfigurationTest.java0000644000175000017500000000463510665031725031167 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.eviction; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertTrue; import org.jboss.cache.config.ConfigurationException; import org.jboss.cache.factories.XmlConfigurationParser; import org.jboss.cache.xml.XmlHelper; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import org.w3c.dom.Element; /** * Unit tests for MRUConfiguration. * * @author Daniel Huang (dhuang@jboss.org) * @version $Revision: 4444 $ */ @Test(groups = {"functional"}) public class MRUConfigurationTest { MRUConfiguration config = null; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { config = new MRUConfiguration(); } public void testXMLParsing() throws Exception { String xml = "\n" + "5000\n" + ""; Element element = XmlHelper.stringToElement(xml); XmlConfigurationParser.parseEvictionPolicyConfig(element, config); assertEquals(5000, config.getMaxNodes()); } public void testXMLParsing2() throws Exception { String xml = "\n" + "10000\n" + ""; Element element = XmlHelper.stringToElement(xml); XmlConfigurationParser.parseEvictionPolicyConfig(element, config); assertEquals(10000, config.getMaxNodes()); } public void testXMLParsing3() throws Exception { String xml = "\n" + ""; Element element = XmlHelper.stringToElement(xml); boolean caught = false; try { XmlConfigurationParser.parseEvictionPolicyConfig(element, config); } catch (ConfigurationException ce) { caught = true; } assertTrue("Configure exception should have been caught, maxNodes is required", caught); xml = "\n" + "10000\n" + ""; element = XmlHelper.stringToElement(xml); XmlConfigurationParser.parseEvictionPolicyConfig(element, config); assertEquals(10000, config.getMaxNodes()); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/eviction/ConcurrentEvictionTest.java0000644000175000017500000001134611017455042031606 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.eviction; import org.jboss.cache.Cache; import org.jboss.cache.CacheFactory; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.EvictionConfig; import org.jboss.cache.config.EvictionRegionConfig; import org.jboss.cache.factories.UnitTestCacheConfigurationFactory; import org.jboss.cache.util.TestingUtil; import static org.testng.AssertJUnit.assertNotNull; import static org.testng.AssertJUnit.fail; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Properties; /** * Tests cache behavior in the presence of concurrent passivation. * * @author Brian Stansberry * @version $Revision: 5906 $ */ @Test(groups = {"functional"}) public class ConcurrentEvictionTest { private Cache cache_; private int wakeupIntervalMillis_ = 0; private String tmpDir = System.getProperty("java.io.tmpdir", "/tmp"); private String cacheLoaderDir = "/JBossCacheFileCacheLoader"; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { initCaches(); wakeupIntervalMillis_ = cache_.getConfiguration().getEvictionConfig().getWakeupIntervalSeconds() * 1000; if (wakeupIntervalMillis_ < 0) { fail("testEviction(): eviction thread wake up interval is illegal " + wakeupIntervalMillis_); } } void initCaches() throws Exception { TestingUtil.recursiveFileRemove(tmpDir + cacheLoaderDir); CacheFactory factory = new DefaultCacheFactory(); Configuration conf = UnitTestCacheConfigurationFactory.createConfiguration(Configuration.CacheMode.LOCAL, true); conf.setEvictionConfig(buildEvictionConfig()); conf.setCacheLoaderConfig(buildCacheLoaderConfig()); conf.setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); cache_ = factory.createCache(conf, true);// read in generic local xml } private CacheLoaderConfig buildCacheLoaderConfig() throws IOException { CacheLoaderConfig cacheLoaderConfig = new CacheLoaderConfig(); cacheLoaderConfig.setPassivation(false); cacheLoaderConfig.setPreload("/"); cacheLoaderConfig.setShared(false); CacheLoaderConfig.IndividualCacheLoaderConfig iclc = new CacheLoaderConfig.IndividualCacheLoaderConfig(); iclc.setClassName("org.jboss.cache.loader.FileCacheLoader"); Properties p = new Properties(); p.put("location", tmpDir + cacheLoaderDir); iclc.setProperties(p); iclc.setAsync(false); iclc.setFetchPersistentState(true); iclc.setIgnoreModifications(false); cacheLoaderConfig.addIndividualCacheLoaderConfig(iclc); return cacheLoaderConfig; } private EvictionConfig buildEvictionConfig() { EvictionConfig ec = new EvictionConfig("org.jboss.cache.eviction.LRUPolicy"); ec.setDefaultEvictionPolicyClass("org.jboss.cache.eviction.LRUPolicy"); ec.setWakeupIntervalSeconds(5); EvictionRegionConfig erc = UnitTestCacheConfigurationFactory.buildLruEvictionRegionConfig("_default_", 5000, 1000); List erConfigs = new ArrayList(); erConfigs.add(erc); ec.setEvictionRegionConfigs(erConfigs); return ec; } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { TestingUtil.recursiveFileRemove(tmpDir + cacheLoaderDir); cache_.stop(); cache_ = null; } public void testConcurrentEviction() throws Exception { Fqn base = Fqn.fromString("/org/jboss/test/data/concurrent/eviction"); // Create a bunch of nodes; more than the /org/jboss/test/data // region's maxNodes so we know eviction will kick in for (int i = 0; i < 1000; i++) { cache_.put(Fqn.fromRelativeElements(base, i / 100), i, "value"); } // Loop for long enough to have 5 runs of the eviction thread long loopDone = System.currentTimeMillis() + (5 * wakeupIntervalMillis_); while (System.currentTimeMillis() < loopDone) { // If any get returns null, that's a failure for (int i = 0; i < 1000; i++) { Fqn fqn = Fqn.fromRelativeElements(base, i / 100); Integer key = i; assertNotNull("found value under Fqn " + fqn + " and key " + key, cache_.get(fqn, key)); } } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/eviction/DummyEvictionPolicy.java0000755000175000017500000000143210631150566031101 0ustar twernertwernerpackage org.jboss.cache.eviction; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.config.EvictionPolicyConfig; /** * @author Ben Feb 13, 2004 */ public class DummyEvictionPolicy extends BaseEvictionPolicy { public void evict(Fqn fqn) throws Exception { // no-op // throw new RuntimeException("Testing only"); } public int getWakeupIntervalSeconds() { return 0; //To change body of implemented methods use File | Settings | File Templates. } public void setCache(CacheSPI cache) { // no op } public EvictionAlgorithm getEvictionAlgorithm() { // no op return null; } public Class getEvictionConfigurationClass() { return null; } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/eviction/NullEvictionPolicyTest.java0000644000175000017500000000734311017455042031560 0ustar twernertwernerpackage org.jboss.cache.eviction; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.factories.UnitTestCacheConfigurationFactory; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.EvictionConfig; import org.jboss.cache.config.EvictionRegionConfig; import org.jboss.cache.lock.IsolationLevel; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.List; import java.util.ArrayList; /** * Unit tests for LRU Policy. * * @author Ben Wang, Feb 11, 2004 * @author Daniel Huang - dhuang@jboss.org * @version $Revision: 4880 $ */ @Test(groups = {"functional"}) public class NullEvictionPolicyTest { CacheSPI cache_; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { cache_ = null; } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { if (cache_ != null) { cache_.stop(); cache_.destroy(); } } /** * Builds a cache was null eviction by default and in "/test" region, * LRU in "/lru" region. Does a mix of puts/reads/removes, wakes for * eviction thread to kick in, checks that nothing was evicted from the * null policy regions but was from lru region. */ public void testEviction() { Configuration config = UnitTestCacheConfigurationFactory.createConfiguration(Configuration.CacheMode.LOCAL, true); EvictionConfig evConfig = config.getEvictionConfig(); evConfig.setWakeupIntervalSeconds(1); evConfig.setDefaultEventQueueSize(200000); evConfig.setDefaultEvictionPolicyClass("org.jboss.cache.eviction.NullEvictionPolicy"); List regionConfigs = new ArrayList(); regionConfigs.add(buildEvictionRegionConfig("/_default_")); regionConfigs.add(buildEvictionRegionConfig("/test")); regionConfigs.add(UnitTestCacheConfigurationFactory.buildLruEvictionRegionConfig("/lru", 10000,1)); evConfig.setEvictionRegionConfigs(regionConfigs); config.setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); config.setIsolationLevel(IsolationLevel.SERIALIZABLE); cache_ = (CacheSPI)new DefaultCacheFactory().createCache(config); String dfltRootStr = "/a/"; String testRootStr = "/test/"; String lruRootStr = "/lru/"; for (int i = 0; i < 20; i++) { Fqn dflt = Fqn.fromString(dfltRootStr + i); Fqn test = Fqn.fromString(testRootStr + i); Fqn lru = Fqn.fromString(lruRootStr + i); cache_.put(dflt, "key", "value"); cache_.put(test, "key", "value"); cache_.put(lru, "key", "value"); } TestingUtil.sleepThread(3500); for (int i = 0; i < 20; i++) { Fqn dflt = Fqn.fromString(dfltRootStr + i); Fqn test = Fqn.fromString(testRootStr + i); Fqn lru = Fqn.fromString(lruRootStr + i); assertEquals("value", cache_.get(dflt, "key")); assertEquals("value", cache_.get(test, "key")); assertNull(cache_.get(lru, "key")); } } private EvictionRegionConfig buildEvictionRegionConfig(String regionName) { EvictionRegionConfig evRegConfig = new EvictionRegionConfig(); evRegConfig.setRegionName(regionName); NullEvictionPolicyConfig nullEvictionPolicyConfig = new NullEvictionPolicyConfig(); evRegConfig.setEvictionPolicyConfig(nullEvictionPolicyConfig); return evRegConfig; } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/eviction/FIFOQueueTest.java0000644000175000017500000001360710665031725027523 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.eviction; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertFalse; import static org.testng.AssertJUnit.assertNotNull; import static org.testng.AssertJUnit.assertNull; import static org.testng.AssertJUnit.assertTrue; import org.jboss.cache.config.EvictionConfig; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * Unit tests for FIFOQueue. * * @author Daniel Huang (dhuang@jboss.org) * @version $Revision: 4444 $ */ @Test(groups = {"functional"}) public class FIFOQueueTest { private static final int CAPACITY = EvictionConfig.EVENT_QUEUE_SIZE_DEFAULT; private FIFOQueue queue; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { queue = new FIFOQueue(); } public void testQueue() throws Exception { for (int i = 0; i < 50000; i++) { NodeEntry ne = new NodeEntry("/a/b/c/" + Integer.toString(i)); queue.addNodeEntry(ne); } assertEquals(queue.getFirstNodeEntry().getFqn().toString(), "/a/b/c/0"); assertEquals(50000, queue.getNumberOfNodes()); assertTrue(queue.containsNodeEntry(new NodeEntry("/a/b/c/250"))); NodeEntry ne27500 = queue.getNodeEntry("/a/b/c/27500"); assertEquals("/a/b/c/27500", ne27500.getFqn().toString()); // now make sure the ordering is correct. int k = 0; NodeEntry ne; while ((ne = queue.getFirstNodeEntry()) != null) { assertEquals("/a/b/c/" + Integer.toString(k), ne.getFqn().toString()); queue.removeNodeEntry(ne); k++; if (k == 25000) { break; } } assertEquals(queue.getFirstNodeEntry().getFqn().toString(), "/a/b/c/25000"); assertEquals(25000, queue.getNumberOfNodes()); k = 25000; while ((ne = queue.getFirstNodeEntry()) != null) { assertEquals(ne.getFqn().toString(), "/a/b/c/" + Integer.toString(k)); queue.removeNodeEntry(ne); k++; } assertEquals(0, queue.getNumberOfNodes()); assertFalse(queue.containsNodeEntry(new NodeEntry("/a/b/c/27500"))); assertNull(queue.getNodeEntry("/a/b/c/27500")); } public void testGetFirstNodeEntry() throws Exception { for (int i = 0; i < 50000; i++) { NodeEntry ne = new NodeEntry("/a/b/c/" + Integer.toString(i)); queue.addNodeEntry(ne); } NodeEntry ne; int count = 0; while ((ne = queue.getFirstNodeEntry()) != null) { assertEquals("/a/b/c/" + Integer.toString(count), ne.getFqn().toString()); queue.removeNodeEntry(ne); count++; } } public void testLargeAddAndRemoval() throws Exception { long begin = System.currentTimeMillis(); for (int i = 0; i < CAPACITY; i++) { queue.addNodeEntry(new NodeEntry("/test/" + Integer.toString(i))); } System.out.println("Took " + (System.currentTimeMillis() - begin) + "ms to add " + CAPACITY + " entries to queue"); assertEquals(CAPACITY, queue.getNumberOfNodes()); begin = System.currentTimeMillis(); for (int i = CAPACITY - 1; i >= 0; i--) { // pop it backwards for worse case scenario if O(n) = n queue.removeNodeEntry(new NodeEntry("/test/" + Integer.toString(i))); } System.out.println("Took " + (System.currentTimeMillis() - begin) + "ms to remove " + CAPACITY + " entries to queue"); assertEquals(0, queue.getNumberOfNodes()); begin = System.currentTimeMillis(); for (int i = 0; i < CAPACITY; i++) { queue.addNodeEntry(new NodeEntry("/test/" + Integer.toString(i))); } System.out.println("Took " + (System.currentTimeMillis() - begin) + "ms to readd " + CAPACITY + " entries to queue"); assertEquals(CAPACITY, queue.getNumberOfNodes()); NodeEntry ne; begin = System.currentTimeMillis(); while ((ne = queue.getFirstNodeEntry()) != null) { queue.removeNodeEntry(ne); } System.out.println("Took " + (System.currentTimeMillis() - begin) + "ms to iterate via getFirstNodeEntry() and remove (pop from top of queue) " + CAPACITY + " entries from queue"); assertEquals(0, queue.getNumberOfNodes()); } public void testNumElements() throws Exception { FIFOQueue queue = new FIFOQueue(); NodeEntry ne = new NodeEntry("/a/b/c"); ne.setNumberOfElements(50); queue.addNodeEntry(ne); assertEquals(50, queue.getNumberOfElements()); assertEquals(1, queue.getNumberOfNodes()); queue.removeNodeEntry(ne); assertEquals(0, queue.getNumberOfElements()); for (int i = 0; i < 10; i++) { ne = new NodeEntry("/a/b/c/" + Integer.toString(i)); ne.setNumberOfElements(i); queue.addNodeEntry(ne); } assertEquals(45, queue.getNumberOfElements()); assertEquals(10, queue.getNumberOfNodes()); ne = queue.getNodeEntry("/a/b/c/0"); assertNotNull(ne); assertEquals(0, ne.getNumberOfElements()); ne.setNumberOfElements(500); assertEquals(545, queue.getNumberOfElements()); ne = queue.getNodeEntry("/a/b/c/0"); assertEquals(500, ne.getNumberOfElements()); queue.removeNodeEntry(ne); assertEquals(45, queue.getNumberOfElements()); assertEquals(9, queue.getNumberOfNodes()); for (int i = 1; i < 10; i++) { ne = queue.getNodeEntry("/a/b/c/" + Integer.toString(i)); assertEquals(i, ne.getNumberOfElements()); queue.removeNodeEntry(ne); } assertEquals(0, queue.getNumberOfNodes()); assertEquals(0, queue.getNumberOfElements()); assertNull(queue.getNodeEntry("/a/b/c/0")); assertNull(queue.getFirstNodeEntry()); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/eviction/RegionManagerTest.java0000755000175000017500000001637310774204232030513 0ustar twernertwernerpackage org.jboss.cache.eviction; import org.jboss.cache.Fqn; import org.jboss.cache.Region; import org.jboss.cache.RegionManager; import org.jboss.cache.config.EvictionConfig; import org.jboss.cache.config.EvictionPolicyConfig; import org.jboss.cache.config.EvictionRegionConfig; import org.jboss.cache.factories.XmlConfigurationParser; import org.jboss.cache.xml.XmlHelper; import static org.testng.AssertJUnit.*; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import org.w3c.dom.Element; import java.util.List; /** * Region Manager unit tests. * * @author Ben Wang, Feb 11, 2004 * @author Daniel Huang (dhuang@jboss.org) * @version $Revision: 5478 $ */ @Test(groups = "functional") public class RegionManagerTest { private final Fqn DEFAULT_REGION = Fqn.ROOT; Fqn A_B_C = Fqn.fromString("/a/b/c"); Fqn A_B = Fqn.fromString("/a/b"); Fqn A_BC = Fqn.fromString("/a/bc"); Fqn AOP = Fqn.fromString("/aop"); EvictionPolicy policy; EvictionPolicyConfig config; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { policy = new DummyEvictionPolicy(); config = new DummyEvictionConfiguration(); } public void testCreateRegion() { RegionManager regionManager = new RegionManager(); regionManager.setUsingEvictions(true); regionManager.getRegion(DEFAULT_REGION, true).setEvictionPolicy(config); regionManager.getRegion(A_B_C, true).setEvictionPolicy(config); regionManager.getRegion(A_B, true).setEvictionPolicy(config); regionManager.getRegion(AOP, true).setEvictionPolicy(config); List regions = regionManager.getAllRegions(Region.Type.ANY); assertEquals("Region size ", 4, regions.size()); } public void testCreateRegion2() { RegionManager regionManager = new RegionManager(); regionManager.setUsingEvictions(true); regionManager.getRegion(A_B_C, true).setEvictionPolicy(config); regionManager.getRegion(A_B, true).setEvictionPolicy(config); regionManager.getRegion(DEFAULT_REGION, true).setEvictionPolicy(config); List regions = regionManager.getAllRegions(Region.Type.ANY); assertEquals("Region size ", 3, regions.size()); assertEquals("Region 0", DEFAULT_REGION, regions.get(0).getFqn()); assertEquals("Region 1 ", A_B, regions.get(1).getFqn()); assertEquals("Region 2 ", A_B_C, regions.get(2).getFqn()); Region region = regionManager.getRegion("/a/b/c/d", false); assertNotNull("Region ", region); assertEquals("Region ", A_B_C, region.getFqn()); region = regionManager.getRegion(A_B, false); assertNotNull("Region ", region); assertEquals("Region ", A_B, region.getFqn()); region = regionManager.getRegion("/a", false); // Should be default. assertNotNull("Region ", region); assertEquals("Region ", DEFAULT_REGION, region.getFqn()); } public void testNoDefaultRegion() { RegionManager regionManager = new RegionManager(); regionManager.setUsingEvictions(true); regionManager.getRegion(A_B_C, true).setEvictionPolicy(config); regionManager.getRegion(A_B, true).setEvictionPolicy(config); regionManager.getRegion(Fqn.fromString("/a"), Region.Type.EVICTION, false); } public void testGetRegion() { RegionManager regionManager = new RegionManager(); regionManager.setUsingEvictions(true); regionManager.getRegion(DEFAULT_REGION, true).setEvictionPolicy(config); regionManager.getRegion(A_BC, true).setEvictionPolicy(config); regionManager.getRegion(A_B, true).setEvictionPolicy(config); Region region = regionManager.getRegion(A_BC, true); assertNotSame("Region ", DEFAULT_REGION, region.getFqn()); } public void testConfigureWithXML() throws Exception { // test the new style configuration String xml = "" + "10" + "20" + ""; Element element = XmlHelper.stringToElement(xml); RegionManager regionManager = new RegionManager(); regionManager.setUsingEvictions(true); EvictionRegionConfig erc = XmlConfigurationParser.parseEvictionRegionConfig(element, null, EvictionConfig.EVENT_QUEUE_SIZE_DEFAULT); Region region = regionManager.getRegion(erc.getRegionFqn(), true); region.setEvictionPolicy(erc.getEvictionPolicyConfig()); assertTrue(region.getEvictionPolicy() instanceof LFUPolicy); assertTrue(region.getEvictionPolicyConfig() instanceof LFUConfiguration); LFUConfiguration config = (LFUConfiguration) region.getEvictionPolicyConfig(); assertEquals(20, config.getMaxNodes()); assertEquals(10, config.getMinNodes()); assertEquals(Fqn.fromString("/test/"), region.getFqn()); // test the 1.2.x style configuration xml = "" + "10" + "20" + ""; element = XmlHelper.stringToElement(xml); erc = XmlConfigurationParser.parseEvictionRegionConfig(element, "org.jboss.cache.eviction.LFUPolicy", EvictionConfig.EVENT_QUEUE_SIZE_DEFAULT); regionManager = new RegionManager(); regionManager.setUsingEvictions(true); region = regionManager.getRegion(erc.getRegionFqn(), true); region.setEvictionPolicy(erc.getEvictionPolicyConfig()); assertTrue(region.getEvictionPolicy() instanceof LFUPolicy); assertTrue(region.getEvictionPolicyConfig() instanceof LFUConfiguration); config = (LFUConfiguration) region.getEvictionPolicyConfig(); assertEquals(20, config.getMaxNodes()); assertEquals(10, config.getMinNodes()); assertEquals(Fqn.fromString("/abc/"), region.getFqn()); } public void testRegionOrdering() throws Exception { Fqn A_B_C_D_E = Fqn.fromString("/a/b/c/d/e/"); Fqn A_B_C_D = Fqn.fromString("/a/b/c/d/"); RegionManager rm = new RegionManager(); rm.setUsingEvictions(true); rm.getRegion(DEFAULT_REGION, true).setEvictionPolicy(config); rm.getRegion(A_B_C_D_E, true).setEvictionPolicy(config); rm.getRegion(A_B_C_D, true).setEvictionPolicy(config); rm.getRegion(A_B_C, true).setEvictionPolicy(config); Region region = rm.getRegion("/a/b/c/d/e/f", false); Region region2 = rm.getRegion("/e/f/g", false); assertEquals(A_B_C_D_E, region.getFqn()); assertEquals(DEFAULT_REGION, region2.getFqn()); List regions = rm.getAllRegions(Region.Type.ANY); for (int i = 0; i < regions.size(); i++) { switch (i) { case 0: assertEquals(DEFAULT_REGION, regions.get(i).getFqn()); break; case 1: assertEquals(A_B_C, regions.get(i).getFqn()); break; case 2: assertEquals(A_B_C_D, regions.get(i).getFqn()); break; case 3: assertEquals(A_B_C_D_E, regions.get(i).getFqn()); break; default: fail("This error condition should never be reached"); break; } } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/eviction/ReplicatedLRUPolicyTest.java0000755000175000017500000001127311044054435031605 0ustar twernertwernerpackage org.jboss.cache.eviction; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.factories.UnitTestCacheConfigurationFactory; import org.jboss.cache.notifications.annotation.CacheListener; import org.jboss.cache.notifications.annotation.NodeEvicted; import org.jboss.cache.notifications.event.Event; import org.jboss.cache.transaction.DummyTransactionManagerLookup; import org.jboss.cache.util.TestingUtil; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * @author Ben Wang, Feb 11, 2004 */ @Test(groups = {"functional"}) public class ReplicatedLRUPolicyTest { CacheSPI cache1, cache2, cache3; int wakeupIntervalMillis_ = 0; EvictionListener listener_ = new EvictionListener(); @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { cache1 = (CacheSPI) new DefaultCacheFactory().createCache(UnitTestCacheConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC, true), false); cache1.getConfiguration().setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName()); cache1.getConfiguration().setUseRegionBasedMarshalling(true); cache1.start(); cache1.getNotifier().addCacheListener(listener_); listener_.resetCounter(); cache3 = (CacheSPI) new DefaultCacheFactory().createCache(UnitTestCacheConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC), false); cache3.getConfiguration().setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); cache3.getConfiguration().setUseRegionBasedMarshalling(true); cache3.start(); wakeupIntervalMillis_ = cache1.getConfiguration().getEvictionConfig().getWakeupIntervalSeconds() * 1000; log("wakeupInterval is " + wakeupIntervalMillis_); if (wakeupIntervalMillis_ <= 0) { fail("testEviction(): eviction thread wake up interval is illegal " + wakeupIntervalMillis_); } } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { TestingUtil.killCaches(cache1, cache2, cache3); } /** * Test local eviction policy that failed for eviction event. */ public void testBasic() throws Exception { String rootStr = "/org/jboss/test/data/"; String str = rootStr + "0"; cache1.put(str, str, str); TestingUtil.sleepThread(30000); Object node = cache1.peek(Fqn.fromString(str), false); assertNull("DataNode should be evicted already ", node); assertEquals("Eviction counter ", 1, listener_.getCounter()); String val = (String) cache3.get(str, str); assertNotNull("DataNode should not be evicted here ", val); assertEquals("Eviction counter ", 1, listener_.getCounter()); } public void testEviction() throws Exception { String rootStr = "/org/jboss/test/data/"; for (int i = 0; i < 10; i++) { String str = rootStr + i; Fqn fqn = Fqn.fromString(str); cache1.put(fqn, str, str); } TestingUtil.sleepThread(2 * wakeupIntervalMillis_); String val = (String) cache1.get(rootStr + "3", rootStr + "3"); assertNull("DataNode should be evicted already ", val); val = (String) cache3.get(rootStr + "3", rootStr + "3"); assertNotNull("DataNode should not be evicted here ", val); } public void testEvictionReplication() throws Exception { String rootStr = "/org/jboss/test/data/"; for (int i = 0; i < 10; i++) { String str = rootStr + i; Fqn fqn = Fqn.fromString(str); cache1.put(fqn, str, str); } TestingUtil.sleepThread(wakeupIntervalMillis_ - 1000); String str = rootStr + "7"; Fqn fqn = Fqn.fromString(str); cache1.get(fqn, str); TestingUtil.sleepThread(wakeupIntervalMillis_); String val = (String) cache1.get(rootStr + "3", rootStr + "3"); assertNull("DataNode should be empty ", val); val = (String) cache3.get(rootStr + "7", rootStr + "7"); assertNotNull("DataNode should not be null", val); } void log(String msg) { System.out.println("-- " + msg); } @CacheListener public class EvictionListener { int counter = 0; public int getCounter() { return counter; } public void resetCounter() { counter = 0; } @NodeEvicted public void nodeEvicted(Event e) { System.out.println(e); if (e.isPre()) counter++; } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/eviction/BaseEvictionAlgorithmTest.java0000644000175000017500000001311710777165765032231 0ustar twernertwerner/* * JBoss, Home of Professional Open Source. * Copyright 2008, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.cache.eviction; import static org.testng.AssertJUnit.fail; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.Region; import org.jboss.cache.RegionManager; import org.jboss.cache.config.ConfigurationException; import org.jboss.cache.config.EvictionPolicyConfig; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * Tests BaseEvictionAlgorithm class. * * @author Galder Zamarreno */ @Test(groups = "functional") public class BaseEvictionAlgorithmTest { private static final Log log = LogFactory.getLog(BaseEvictionAlgorithmTest.class); private RegionManager regionManager; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { regionManager = new RegionManager(); } public void testFillUpRecycleQueue() throws Exception { final int recycleQueueCapacity = 10; /* override recycle queue capacity to make the test shorter */ BaseEvictionAlgorithm algorithm = new MockEvictionAlgorithm(recycleQueueCapacity); Region region = regionManager.getRegion("/a/b/c", true); region.setEvictionPolicy(new MockEvictionPolicyConfig()); for (int i = 0; i < (recycleQueueCapacity + 1); i ++) { Fqn fqn = Fqn.fromString("/a/b/c/" + Integer.toString(i + 1)); region.putNodeEvent(new EvictedEventNode(fqn, NodeEventType.ADD_NODE_EVENT)); } ExecutorService executor = Executors.newSingleThreadExecutor(); Future future = executor.submit(new ProcessEvictionRegion(region, algorithm)); try { future.get(20, TimeUnit.SECONDS); } catch(TimeoutException te) { log.error("Region eviction processing did not finish on time", te); fail("Region eviction processing should have finished by now, something is wrong. Recycle queue may have filled up."); } finally { log.info("recycle queue size: " + algorithm.recycleQueue.size()); } } /** Classes **/ public static class MockEvictionAlgorithm extends BaseEvictionAlgorithm { public MockEvictionAlgorithm(int recycleQueueCapacity) { recycleQueue = new LinkedBlockingQueue(recycleQueueCapacity); } @Override protected EvictionQueue setupEvictionQueue(Region region) throws EvictionException { return new LRUQueue(); } @Override protected boolean shouldEvictNode(NodeEntry ne) { /* all node entries need evicting */ return true; } } public static class MockEvictionPolicy extends BaseEvictionPolicy { @Override public void evict(Fqn fqn) throws Exception { throw new Exception("Unable to evict"); } @Override public void setCache(CacheSPI cache) { /* no op */ } public EvictionAlgorithm getEvictionAlgorithm() { return null; } public Class getEvictionConfigurationClass() { return MockEvictionPolicyConfig.class; } } public static class MockEvictionPolicyConfig implements EvictionPolicyConfig { public String getEvictionPolicyClass() { return MockEvictionPolicy.class.getName(); } public void reset() { /* no op */ } public void validate() throws ConfigurationException { /* no op */ } } public class ProcessEvictionRegion implements Callable { private Region region; private EvictionAlgorithm algorithm; public ProcessEvictionRegion(Region region, EvictionAlgorithm algorithm) { this.region = region; this.algorithm = algorithm; } public Void call() throws Exception { try { algorithm.process(region); } catch(EvictionException e) { log.error("Eviction exception reported", e); fail("Eviction exception reported" + e); } return null; } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/eviction/OptimisticEvictionTest.java0000644000175000017500000001357311017455042031614 0ustar twernertwernerpackage org.jboss.cache.eviction; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.EvictionConfig; import org.jboss.cache.factories.UnitTestCacheConfigurationFactory; import org.jboss.cache.factories.XmlConfigurationParser; import org.jboss.cache.interceptors.EvictionInterceptor; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.transaction.DummyTransactionManagerLookup; import org.jboss.cache.xml.XmlHelper; import static org.testng.AssertJUnit.assertFalse; import static org.testng.AssertJUnit.assertTrue; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import org.w3c.dom.Element; import javax.transaction.TransactionManager; import java.util.Iterator; import java.util.List; /** * Tests the eviction and the possible lack of locking nodes. * The configuration is with an aggressive eviction policy, 100 objects 2 seconds interval. *

* It is possible that the number needs to be changed a little, depending on the machine speed. * * @author fhenning */ @Test(groups = {"functional"}) public class OptimisticEvictionTest { //Maximum number of runs 2^20 private static final int NUMBER_OF_RUNS = 1 << 20; //Initial number of nodes private static final int NUMBER_NODES = 256; private Fqn region = Fqn.fromElements("testingRegion"); private TransactionManager txManager; private CacheSPI cache; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { txManager = new DummyTransactionManagerLookup().getTransactionManager(); Configuration config = UnitTestCacheConfigurationFactory.createConfiguration(Configuration.CacheMode.LOCAL, true); config.setNodeLockingOptimistic(true); config.setEvictionConfig(buildEvictionConfig()); cache = (CacheSPI) new DefaultCacheFactory().createCache(config); } private EvictionConfig buildEvictionConfig() throws Exception { String xml = " \n" + " \n" + " 1\n" + " \n" + " org.jboss.cache.eviction.LRUPolicy\n" + " \n" + " 10\n" + " 0\n" + " 0\n" + " \n" + " \n" + " 10\n" + " 0\n" + " 0\n" + " \n" + " \n" + " 10\n" + " 1\n" + " 1\n" + " \n" + " \n" + " "; Element element = XmlHelper.stringToElement(xml); return XmlConfigurationParser.parseEvictionConfig(element); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { if (cache != null) { // shut down any existing gtx2EntryMap try { if (cache.getTransactionManager().getTransaction() != null) { cache.getTransactionManager().rollback(); } } catch (Exception e) { // ignore } cache.stop(); cache = null; } } public void testEvictionError() throws Exception { //Initialize the cache via a map for (int i = 0; i < NUMBER_NODES; i++) { cache.put(Fqn.fromRelativeElements(region, i), i, i); } for (int i = 0; i < NUMBER_OF_RUNS; i++) { txManager.begin(); cache.get(region, i % NUMBER_NODES); txManager.commit(); } } public void testEvictionOccurence() throws Exception { cache.put("/timeBased/test", "key", "value"); assertTrue(cache.exists("/timeBased/test")); // wait for it to be evicted. TestingUtil.sleepThread(3000); assertTrue(!cache.exists("/timeBased/test")); } public void testInterceptorChain() throws Exception { List interceptors = cache.getInterceptorChain(); System.out.println(interceptors); Iterator i = interceptors.iterator(); boolean found = false; while (i.hasNext()) { Object o = i.next(); if (o instanceof EvictionInterceptor) { found = true; } } assertTrue("Eviction interceptor should be in interceptor chain.", found); } public void testCompleteRemoval() throws Exception { String rootStr = "/timeBased/"; // Add a parent, then a child. LRU will evict the parent, // then the child, leaving behind an empty parent Fqn parent = Fqn.fromString(rootStr + "parent"); cache.put(parent, "key", "value"); cache.put(Fqn.fromRelativeElements(parent, "child"), "key", "value"); // Give eviction time to run a few times, then confirm parent // is completely gone TestingUtil.sleepThread(5500); assertFalse("Parent completely removed", cache.getRoot().hasChild(parent)); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/eviction/ElementSizePolicyTest.java0000644000175000017500000001677110746355150031405 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.eviction; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.Node; import org.jboss.cache.NodeSPI; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.EvictionConfig; import org.jboss.cache.config.EvictionRegionConfig; import org.jboss.cache.factories.UnitTestCacheConfigurationFactory; import org.jboss.cache.lock.IsolationLevel; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.List; import java.util.ArrayList; /** * @author Daniel Huang * @version $Revison: $ */ @Test(groups = {"functional"}) public class ElementSizePolicyTest { CacheSPI cache; int wakeupIntervalMillis = 0; final String ROOT_STR = "/test"; Throwable t1_ex, t2_ex; final long DURATION = 10000; boolean isTrue; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { initCaches(); wakeupIntervalMillis = cache.getConfiguration().getEvictionConfig().getWakeupIntervalSeconds() * 1000; log("wakeupInterval is " + wakeupIntervalMillis); if (wakeupIntervalMillis < 0) { fail("testEviction(): eviction thread wake up interval is illegal " + wakeupIntervalMillis); } t1_ex = t2_ex = null; isTrue = true; } void initCaches() throws Exception { Configuration conf = UnitTestCacheConfigurationFactory.createConfiguration(Configuration.CacheMode.LOCAL, true); EvictionConfig evConfig = conf.getEvictionConfig(); evConfig.setDefaultEvictionPolicyClass("org.jboss.cache.eviction.ElementSizePolicy"); evConfig.setWakeupIntervalSeconds(3); evConfig.setDefaultEventQueueSize(200000); List regionConfigs = new ArrayList(); regionConfigs.add(createEvictionRegionConfig("_default_", 5000, 100)); regionConfigs.add(createEvictionRegionConfig("/org/jboss/data", 10, 20)); regionConfigs.add(createEvictionRegionConfig("/org/jboss/test/data", -1, 5)); regionConfigs.add(createEvictionRegionConfig("/test/", 5000, 1)); evConfig.setEvictionRegionConfigs(regionConfigs); cache = (CacheSPI) new DefaultCacheFactory().createCache(conf, false); cache.getConfiguration().setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); cache.getConfiguration().setIsolationLevel(IsolationLevel.SERIALIZABLE); cache.start(); } private EvictionRegionConfig createEvictionRegionConfig(String regionName, int maxNodes, int maxElementsPerNode) { EvictionRegionConfig ercDefault = new EvictionRegionConfig(); ercDefault.setRegionName(regionName); ElementSizeConfiguration esConfig = new ElementSizeConfiguration(); if (maxNodes >= 0) esConfig.setMaxNodes(maxNodes); if (maxElementsPerNode >= 0) esConfig.setMaxElementsPerNode(maxElementsPerNode); ercDefault.setEvictionPolicyConfig(esConfig); return ercDefault; } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { cache.stop(); } public void testEviction() throws Exception { String rootStr = "/org/jboss/test/data/"; for (int i = 0; i < 10; i++) { String str = rootStr + i; Fqn fqn = Fqn.fromString(str); try { cache.put(fqn, str, str); if (i % 2 == 0) { for (int k = 0; k < i; k++) { cache.put(fqn, k, Integer.toString(k)); } } } catch (Exception e) { fail("Failed to insert data" + e); e.printStackTrace(); } } System.out.println(cache); _sleep(wakeupIntervalMillis + 500); System.out.println(cache); for (int i = 0; i < 10; i++) { Node node = cache.getNode("/org/jboss/test/data/" + Integer.toString(i)); System.out.println(node); if (i % 2 == 0) { if (i < 6) { int numElements = ((NodeSPI) node).getDataDirect().size(); assertEquals(i + 1, numElements); } else { assertNull(node); } } else { assertEquals(1, ((NodeSPI) node).getDataDirect().size()); } } } public void testEviction2() throws Exception { String rootStr = "/org/jboss/data/"; for (int i = 0; i < 20; i++) { String str = rootStr + Integer.toString(i); Fqn fqn = Fqn.fromString(str); cache.put(fqn, i, str); for (int k = 0; k < i; k++) { cache.put(fqn, k, str); } } _sleep(wakeupIntervalMillis + 500); for (int i = 0; i < 20; i++) { String str = rootStr + Integer.toString(i); Fqn fqn = Fqn.fromString(str); Node node = cache.getNode(fqn); System.out.println(i + " " + node); if (i > 9) { assertNull("Testing at " + i, node); } else { assertEquals(1 + i, node.getData().size()); } } for (int i = 0; i < 17; i++) { cache.put("/org/jboss/data/" + Integer.toString(3), 100 + i, "value"); } Node node = cache.getNode("/org/jboss/data/" + Integer.toString(3)); assertEquals(21, node.getData().size()); _sleep(wakeupIntervalMillis + 500); assertNull(cache.getNode("/org/jboss/data/" + Integer.toString(3))); } class MyPutter extends Thread { public MyPutter(String name) { super(name); } public void run() { int i = 0; final String myName = ROOT_STR + "/test1/node" + getName(); while (isTrue) { try { cache.put(myName + i++, "value", i); sleep(1); } catch (Throwable e) { e.printStackTrace(); if (t1_ex == null) { t1_ex = e; } } } } } public void testConcurrentPutAndEvict() throws Exception { cache.stop(); cache.destroy(); cache.getConfiguration().setIsolationLevel(IsolationLevel.REPEATABLE_READ); cache.start(); cache.put(ROOT_STR + "/concurrentPutAndEvict", "value", 1); for (int i = 0; i < 10; i++) { new MyPutter("Putter" + i).start(); } int counter = 0; while (true) { counter++; if (t1_ex != null) { fail("Exception generated in put() " + t1_ex); } log("nodes/locks: " + cache.getNumberOfNodes() + "/" + cache.getNumberOfLocksHeld()); _sleep(1000); if (counter > 10) {// run for 10 seconds isTrue = false; break; } } } private void _sleep(long msecs) { try { Thread.sleep(msecs); } catch (InterruptedException e) { e.printStackTrace();//To change body of catch statement use File | Settings | File Templates. } } private void log(String msg) { System.out.println("-- " + msg); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/eviction/EvictionQueueListTest.java0000644000175000017500000001613310665031725031411 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.eviction; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertFalse; import static org.testng.AssertJUnit.assertTrue; import java.util.ConcurrentModificationException; import java.util.Iterator; import java.util.NoSuchElementException; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * @author Daniel Huang (dhuang@jboss.org) * @version $Revision: 4444 $ */ @Test(groups = {"functional"}) public class EvictionQueueListTest { EvictionQueueList list; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { list = new EvictionQueueList(); } public void testAddToBottom() throws Exception { for (int i = 0; i < 100; i++) { NodeEntry ne = new NodeEntry("/" + Integer.toString(i)); EvictionListEntry listEntry = new EvictionListEntry(ne); list.addToBottom(listEntry); } assertEquals(100, list.size()); for (int i = 0; i < 100; i++) { EvictionListEntry entry = list.getFirst(); assertEquals("/" + Integer.toString(i), entry.node.getFqn().toString()); list.remove(entry); } } public void testAddToTop() throws Exception { for (int i = 0; i < 100; i++) { NodeEntry ne = new NodeEntry("/" + Integer.toString(i)); EvictionListEntry listEntry = new EvictionListEntry(ne); list.addToTop(listEntry); } assertEquals(100, list.size()); for (int i = 99; i >= 0; i--) { EvictionListEntry entry = list.getFirst(); assertEquals("/" + Integer.toString(i), entry.node.getFqn().toString()); list.remove(entry); } } public void testRemoveAndClear() throws Exception { EvictionListEntry listEntry1 = new EvictionListEntry(new NodeEntry("/0")); list.addToBottom(listEntry1); assertEquals(list.getFirst(), list.getLast()); EvictionListEntry listEntry2 = new EvictionListEntry(new NodeEntry("/1")); list.addToBottom(listEntry2); EvictionListEntry listEntry3 = new EvictionListEntry(new NodeEntry("/2")); list.addToBottom(listEntry3); EvictionListEntry listEntry4 = new EvictionListEntry(new NodeEntry("/3")); list.addToBottom(listEntry4); EvictionListEntry listEntry5 = new EvictionListEntry(new NodeEntry("/4")); list.addToBottom(listEntry5); EvictionListEntry listEntry6 = new EvictionListEntry(new NodeEntry("/5")); list.addToBottom(listEntry6); assertEquals(6, list.size()); assertEquals(listEntry1, list.getFirst()); assertEquals(listEntry6, list.getLast()); // test removal from the top. list.remove(list.getFirst()); assertEquals(5, list.size()); assertEquals(listEntry2, list.getFirst()); // test removal from the bottom. list.remove(list.getLast()); assertEquals(4, list.size()); assertEquals(listEntry5, list.getLast()); // test removal from the middle list.remove(listEntry3); assertEquals(3, list.size()); assertEquals(listEntry2, list.getFirst()); assertEquals(listEntry5, list.getLast()); Iterator it = list.iterator(); int count = 0; while (it.hasNext()) { NodeEntry e = (NodeEntry) it.next(); if (count == 0) { assertEquals(listEntry2.node, e); } else if (count == 1) { assertEquals(listEntry4.node, e); } else if (count == 2) { assertEquals(listEntry5.node, e); } count++; } assertEquals(3, count); // test clear. list.clear(); assertEquals(0, list.size()); boolean caught = false; try { list.getFirst(); } catch (NoSuchElementException e) { caught = true; } assertTrue(caught); caught = false; try { list.getLast(); } catch (NoSuchElementException e) { caught = true; } assertTrue(caught); } public void testIterator() throws Exception { for (int i = 0; i < 100; i++) { NodeEntry ne = new NodeEntry("/" + Integer.toString(i)); EvictionListEntry listEntry = new EvictionListEntry(ne); list.addToBottom(listEntry); } Iterator it = list.iterator(); int count = 0; while (it.hasNext()) { NodeEntry e = (NodeEntry) it.next(); assertEquals("/" + Integer.toString(count), e.getFqn().toString()); it.remove(); count++; } assertEquals(0, list.size()); it = list.iterator(); assertFalse(it.hasNext()); for (int i = 0; i < 100; i++) { NodeEntry ne = new NodeEntry("/" + Integer.toString(i)); EvictionListEntry listEntry = new EvictionListEntry(ne); list.addToBottom(listEntry); } it = list.iterator(); boolean caught = false; try { while (it.hasNext()) { list.addToBottom(new EvictionListEntry(new NodeEntry("/a/b/c"))); } } catch (ConcurrentModificationException e) { caught = true; } assertTrue(caught); } public void testToArray() throws Exception { for (int i = 0; i < 100; i++) { NodeEntry ne = new NodeEntry("/" + Integer.toString(i)); EvictionListEntry listEntry = new EvictionListEntry(ne); list.addToTop(listEntry); } EvictionListEntry entries[] = list.toArray(); assertEquals(100, entries.length); for (int i = 0, j = 99; i < 100; i++, j--) { assertEquals("/" + Integer.toString(j), entries[i].node.getFqn().toString()); } } public void testFromArray() throws Exception { EvictionListEntry entries[] = new EvictionListEntry[100]; for (int i = 0; i < 100; i++) { entries[i] = new EvictionListEntry(new NodeEntry("/" + Integer.toString(i))); } assertEquals(0, list.size()); list.fromArray(entries); assertEquals(100, list.size()); for (int i = 0; i < 100; i++) { assertEquals(entries[i], list.getFirst()); list.remove(list.getFirst()); } assertEquals(0, list.size()); } /* public void testSort() throws Exception { Comparator lfuComp = new LFUComparator(); // this will create a reverse sorted list. LFUComparator will sort items from lowest node visits to highest // node visits. for(int i = 0, j = 9; i < 10; i++, j--) { NodeEntry ne = new NodeEntry("/" + Integer.toString(i)); ne.setNumberOfNodeVisits(j); list.addToBottom(new EvictionListEntry(ne)); } list.sort(lfuComp); EvictionListEntry entries[] = list.toArray(); for(int i = 0; i < 10; i++) { System.out.println(entries[i].node); assertEquals(i, entries[i].node.getNumberOfNodeVisits()); } } */ } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/eviction/EvictionConfigurationTest.java0000644000175000017500000002372510746355150032306 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.eviction; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.Region; import org.jboss.cache.RegionManager; import org.jboss.cache.factories.UnitTestCacheConfigurationFactory; import org.jboss.cache.config.EvictionPolicyConfig; import org.jboss.cache.config.Configuration; import org.jboss.cache.lock.IsolationLevel; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertTrue; import org.testng.annotations.Test; import java.net.URL; /** * Unit test to test Eviction configuration types. * * @author Daniel Huang (dhuang@jboss.org) * @version $Revision: 5238 $ */ @Test(groups = {"functional"}) public class EvictionConfigurationTest { CacheSPI cache; RegionManager regionManager; public void testPolicyPerRegion() throws Exception { this.setupCache("META-INF/conf-test/policyPerRegion-eviction-service.xml"); assertEquals(5, cache.getConfiguration().getEvictionConfig().getWakeupIntervalSeconds()); Region region = regionManager.getRegion("/org/jboss/data", true); EvictionPolicy policy = region.getEvictionPolicy(); EvictionPolicyConfig configuration = region.getEvictionPolicyConfig(); assertEquals(Fqn.fromString("/org/jboss/data"), region.getFqn()); assertTrue(policy instanceof LFUPolicy); assertTrue(configuration instanceof LFUConfiguration); assertEquals(5000, ((LFUConfiguration) configuration).getMaxNodes()); assertEquals(1000, ((LFUConfiguration) configuration).getMinNodes()); region = regionManager.getRegion("/org/jboss/test/data", true); policy = region.getEvictionPolicy(); configuration = region.getEvictionPolicyConfig(); assertEquals(Fqn.fromString("/org/jboss/test/data"), region.getFqn()); assertTrue(policy instanceof FIFOPolicy); assertTrue(configuration instanceof FIFOConfiguration); assertEquals(5, ((FIFOConfiguration) configuration).getMaxNodes()); region = regionManager.getRegion("/test", true); policy = region.getEvictionPolicy(); configuration = region.getEvictionPolicyConfig(); assertEquals(Fqn.fromString("/test"), region.getFqn()); assertTrue(policy instanceof MRUPolicy); assertTrue(configuration instanceof MRUConfiguration); assertEquals(10000, ((MRUConfiguration) configuration).getMaxNodes()); region = regionManager.getRegion("/maxAgeTest", true); policy = region.getEvictionPolicy(); configuration = region.getEvictionPolicyConfig(); assertEquals(Fqn.fromString("/maxAgeTest"), region.getFqn()); assertTrue(policy instanceof LRUPolicy); assertTrue(configuration instanceof LRUConfiguration); assertEquals(10000, ((LRUConfiguration) configuration).getMaxNodes()); assertEquals(8, ((LRUConfiguration) configuration).getTimeToLiveSeconds()); assertEquals(10, ((LRUConfiguration) configuration).getMaxAgeSeconds()); // test the default region. use a region name that isn't defined explicitly in conf file. region = regionManager.getRegion("/a/b/c", false); policy = region.getEvictionPolicy(); configuration = region.getEvictionPolicyConfig(); assertEquals(Fqn.ROOT, region.getFqn()); assertTrue(policy instanceof LRUPolicy); assertTrue(configuration instanceof LRUConfiguration); assertEquals(5000, ((LRUConfiguration) configuration).getMaxNodes()); assertEquals(1000, ((LRUConfiguration) configuration).getTimeToLiveSeconds()); assertEquals(0, ((LRUConfiguration) configuration).getMaxAgeSeconds()); cache.stop(); } public void testMixedPolicies() throws Exception { this.setupCache("META-INF/conf-test/mixedPolicy-eviction-service.xml"); assertEquals(5, cache.getConfiguration().getEvictionConfig().getWakeupIntervalSeconds()); Region region = regionManager.getRegion("/org/jboss/data", true); EvictionPolicy policy = region.getEvictionPolicy(); EvictionPolicyConfig configuration = region.getEvictionPolicyConfig(); assertEquals(Fqn.fromString("/org/jboss/data/"), region.getFqn()); assertTrue(policy instanceof FIFOPolicy); assertTrue(configuration instanceof FIFOConfiguration); assertEquals(5000, ((FIFOConfiguration) configuration).getMaxNodes()); region = regionManager.getRegion("/test", true); policy = region.getEvictionPolicy(); configuration = region.getEvictionPolicyConfig(); assertEquals(Fqn.fromString("/test/"), region.getFqn()); assertTrue(policy instanceof MRUPolicy); assertTrue(configuration instanceof MRUConfiguration); assertEquals(10000, ((MRUConfiguration) configuration).getMaxNodes()); // test the default region. use a region name that isn't defined explicitly in conf file. region = regionManager.getRegion("/a/b/c", false); policy = region.getEvictionPolicy(); configuration = region.getEvictionPolicyConfig(); assertEquals(Fqn.ROOT, region.getFqn()); assertTrue(policy instanceof LRUPolicy); assertTrue(configuration instanceof LRUConfiguration); assertEquals(5000, ((LRUConfiguration) configuration).getMaxNodes()); assertEquals(1000, ((LRUConfiguration) configuration).getTimeToLiveSeconds()); assertEquals(0, ((LRUConfiguration) configuration).getMaxAgeSeconds()); region = regionManager.getRegion("/maxAgeTest", false); policy = region.getEvictionPolicy(); configuration = region.getEvictionPolicyConfig(); assertEquals(Fqn.fromString("/maxAgeTest/"), region.getFqn()); assertTrue(policy instanceof LRUPolicy); assertTrue(configuration instanceof LRUConfiguration); assertEquals(10000, ((LRUConfiguration) configuration).getMaxNodes()); assertEquals(8, ((LRUConfiguration) configuration).getTimeToLiveSeconds()); assertEquals(10, ((LRUConfiguration) configuration).getMaxAgeSeconds()); cache.stop(); } public void testLegacyPolicyConfiguration() throws Exception { this.setupCache("META-INF/conf-test/local-lru-eviction-service.xml"); assertEquals(5, cache.getConfiguration().getEvictionConfig().getWakeupIntervalSeconds()); Region region = regionManager.getRegion("/org/jboss/data", false); EvictionPolicy policy = region.getEvictionPolicy(); EvictionPolicyConfig configuration = region.getEvictionPolicyConfig(); assertEquals(Fqn.fromString("/org/jboss/data/"), region.getFqn()); assertTrue(policy instanceof LRUPolicy); assertTrue(configuration instanceof LRUConfiguration); assertEquals(5000, ((LRUConfiguration) configuration).getMaxNodes()); assertEquals(1000, ((LRUConfiguration) configuration).getTimeToLiveSeconds()); region = regionManager.getRegion("/org/jboss/test/data", false); policy = region.getEvictionPolicy(); configuration = region.getEvictionPolicyConfig(); assertEquals(Fqn.fromString("/org/jboss/test/data/"), region.getFqn()); assertTrue(policy instanceof LRUPolicy); assertTrue(configuration instanceof LRUConfiguration); assertEquals(5, ((LRUConfiguration) configuration).getMaxNodes()); assertEquals(4, ((LRUConfiguration) configuration).getTimeToLiveSeconds()); region = regionManager.getRegion("/test", true); policy = region.getEvictionPolicy(); configuration = region.getEvictionPolicyConfig(); assertEquals(Fqn.fromString("/test/"), region.getFqn()); assertTrue(policy instanceof LRUPolicy); assertTrue(configuration instanceof LRUConfiguration); assertEquals(10000, ((LRUConfiguration) configuration).getMaxNodes()); assertEquals(4, ((LRUConfiguration) configuration).getTimeToLiveSeconds()); region = regionManager.getRegion("/maxAgeTest", true); policy = region.getEvictionPolicy(); configuration = region.getEvictionPolicyConfig(); assertEquals(Fqn.fromString("/maxAgeTest/"), region.getFqn()); assertTrue(policy instanceof LRUPolicy); assertTrue(configuration instanceof LRUConfiguration); assertEquals(10000, ((LRUConfiguration) configuration).getMaxNodes()); assertEquals(8, ((LRUConfiguration) configuration).getTimeToLiveSeconds()); assertEquals(10, ((LRUConfiguration) configuration).getMaxAgeSeconds()); // test the default region. use a region name that isn't defined explicitly in conf file. region = regionManager.getRegion("/a/b/c", false); policy = region.getEvictionPolicy(); configuration = region.getEvictionPolicyConfig(); assertEquals(Fqn.ROOT, region.getFqn()); assertTrue(policy instanceof LRUPolicy); assertTrue(configuration instanceof LRUConfiguration); assertEquals(5000, ((LRUConfiguration) configuration).getMaxNodes()); assertEquals(1000, ((LRUConfiguration) configuration).getTimeToLiveSeconds()); assertEquals(0, ((LRUConfiguration) configuration).getMaxAgeSeconds()); cache.stop(); } public void testTwoCacheInstanceConfiguration() throws Exception { this.setupCache("META-INF/conf-test/local-lru-eviction-service.xml"); this.setupCache("META-INF/conf-test/local-lru-eviction-service.xml"); } public void testNoEviction() throws Exception { cache = (CacheSPI) new DefaultCacheFactory().createCache(); regionManager = cache.getRegionManager(); assertEquals(0, regionManager.getAllRegions(Region.Type.ANY).size()); } private void setupCache(String configurationName) { cache = (CacheSPI) new DefaultCacheFactory().createCache(configurationName, false); cache.getConfiguration().setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); cache.getConfiguration().setIsolationLevel(IsolationLevel.SERIALIZABLE); cache.start(); regionManager = cache.getRegionManager(); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/eviction/MRUQueueTest.java0000644000175000017500000000710410665031725027436 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.eviction; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertNotNull; import static org.testng.AssertJUnit.assertNull; import static org.testng.AssertJUnit.assertTrue; import org.jboss.cache.Fqn; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * Unit tests for MRUQueue. * * @author Daniel Huang (dhuang@jboss.org) * @version $Revision: 4444 $ */ @Test(groups = {"functional"}) public class MRUQueueTest { private MRUQueue queue; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { queue = new MRUQueue(); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { queue.clear(); } public void testQueue() throws Exception { for (int i = 0; i < 100; i++) { NodeEntry ne = new NodeEntry("/a/b/c/" + Integer.toString(i)); ne.setModifiedTimeStamp(0); queue.addNodeEntry(ne); } assertEquals(queue.nodeMap.size(), queue.list.size()); for (int i = 0; i < 100; i++) { if (i % 2 == 0) { Fqn fqn = Fqn.fromString("/a/b/c/" + Integer.toString(i)); NodeEntry ne = queue.getNodeEntry(fqn); ne.setModifiedTimeStamp(System.currentTimeMillis()); queue.moveToTopOfStack(fqn); } } assertEquals(queue.nodeMap.size(), queue.list.size()); NodeEntry ne; int count = 0; while ((ne = queue.getFirstNodeEntry()) != null) { if (count < 50) { assertTrue(ne.getModifiedTimeStamp() > 0); assertEquals(100 - count, queue.getNumberOfNodes()); } else { assertEquals(0, ne.getModifiedTimeStamp()); } queue.removeNodeEntry(ne); count++; } assertEquals(queue.nodeMap.size(), queue.list.size()); } public void testNumElements() throws Exception { MRUQueue queue = new MRUQueue(); NodeEntry ne = new NodeEntry("/a/b/c"); ne.setNumberOfElements(50); queue.addNodeEntry(ne); assertEquals(50, queue.getNumberOfElements()); assertEquals(1, queue.getNumberOfNodes()); queue.removeNodeEntry(ne); assertEquals(0, queue.getNumberOfElements()); for(int i = 0; i < 10; i++) { ne = new NodeEntry("/a/b/c/" + Integer.toString(i)); ne.setNumberOfElements(i); queue.addNodeEntry(ne); } assertEquals(45, queue.getNumberOfElements()); assertEquals(10, queue.getNumberOfNodes()); ne = queue.getNodeEntry("/a/b/c/0"); assertNotNull(ne); assertEquals(0, ne.getNumberOfElements()); ne.setNumberOfElements(500); assertEquals(545, queue.getNumberOfElements()); ne = queue.getNodeEntry("/a/b/c/0"); assertEquals(500, ne.getNumberOfElements()); queue.removeNodeEntry(ne); assertEquals(45, queue.getNumberOfElements()); assertEquals(9, queue.getNumberOfNodes()); for(int i = 1; i < 10; i++) { ne = queue.getNodeEntry("/a/b/c/" + Integer.toString(i)); assertEquals(i, ne.getNumberOfElements()); queue.removeNodeEntry(ne); } assertEquals(0, queue.getNumberOfNodes()); assertEquals(0, queue.getNumberOfElements()); assertNull(queue.getNodeEntry("/a/b/c/0")); assertNull(queue.getFirstNodeEntry()); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/eviction/LFUQueueTest.java0000644000175000017500000001600410665031725027420 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.eviction; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertFalse; import static org.testng.AssertJUnit.assertNotNull; import static org.testng.AssertJUnit.assertNull; import static org.testng.AssertJUnit.assertTrue; import java.util.Iterator; import java.util.List; import java.util.Set; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * Unit tests for LFUQueue. * * @author Daniel Huang (dhuang@jboss.org) * @version $Revision: 4444 $ */ @Test(groups = {"functional"}) public class LFUQueueTest { private LFUQueue queue; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { queue = new LFUQueue(); } public void testQueue() throws Exception { NodeEntry ne; for (int i = 0; i < 500; i++) { ne = new NodeEntry("/a/b/c/" + Integer.toString(i)); queue.addNodeEntry(ne); } queue.resortEvictionQueue(); assertEquals(500, queue.getNumberOfNodes()); assertTrue(queue.containsNodeEntry(new NodeEntry("/a/b/c/250"))); NodeEntry ne275 = queue.getNodeEntry("/a/b/c/275"); assertEquals("/a/b/c/275", ne275.getFqn().toString()); // now make sure the ordering is correct. Iterator it = queue.iterate(); int k = 0; while (it.hasNext()) { ne = (NodeEntry) it.next(); assertEquals("/a/b/c/" + Integer.toString(k), ne.getFqn().toString()); if (k % 2 == 0) { ne.setNumberOfNodeVisits(ne.getNumberOfNodeVisits() + 1); } k++; } queue.resortEvictionQueue(); assertEquals("/a/b/c/1", queue.getFirstNodeEntry().getFqn().toString()); // now check the sort order. it = queue.iterate(); k = 0; while (it.hasNext()) { ne = (NodeEntry) it.next(); if (k < 250) { assertEquals(0, ne.getNumberOfNodeVisits()); } else { assertEquals(1, ne.getNumberOfNodeVisits()); } k++; } k = 0; while ((ne = queue.getFirstNodeEntry()) != null) { if (k == 250) { break; } queue.removeNodeEntry(ne); k++; } assertEquals(250, queue.getNumberOfNodes()); assertFalse(queue.containsNodeEntry(new NodeEntry("/a/b/c/275"))); assertNull(queue.getNodeEntry("/a/b/c/275")); for (int i = 0; i < 500; i++) { if (i % 2 == 0) { ne = queue.getNodeEntry("/a/b/c/" + Integer.toString(i)); assertEquals(1, ne.getNumberOfNodeVisits()); if (i > 250) { ne.setNumberOfNodeVisits(ne.getNumberOfNodeVisits() + 1); } } } queue.resortEvictionQueue(); assertEquals(250, queue.getNumberOfNodes()); k = 0; it = queue.iterate(); while (it.hasNext()) { ne = (NodeEntry) it.next(); if (k <= 125) { assertEquals(1, ne.getNumberOfNodeVisits()); } else { assertEquals(2, ne.getNumberOfNodeVisits()); } k++; } } public void testPrune() throws Exception { for (int i = 0; i < 5000; i++) { queue.addNodeEntry(new NodeEntry("/a/b/c/" + Integer.toString(i))); } NodeEntry ne; Iterator it = queue.iterate(); int i = 0; while (it.hasNext()) { ne = (NodeEntry) it.next(); if (i % 2 == 0) { queue.removeNodeEntry(ne); } i++; } assertEquals(2500, queue.getNumberOfNodes()); Set removalQueue = queue.getRemovalQueue(); List evictionList = queue.getEvictionList(); assertEquals(2500, removalQueue.size()); it = removalQueue.iterator(); while (it.hasNext()) { ne = (NodeEntry) it.next(); int currentIndex = Integer.parseInt((String) ne.getFqn().get(3)); assertEquals(0, currentIndex % 2); assertFalse(queue.containsNodeEntry(ne)); assertNull(queue.getNodeEntry(ne.getFqn())); assertTrue(evictionList.contains(ne)); } assertEquals(5000, evictionList.size()); queue.prune(); assertEquals(0, removalQueue.size()); assertEquals(2500, evictionList.size()); } public void testGetFirstNodeEntry() throws Exception { for (int i = 0; i < 500; i++) { NodeEntry ne = new NodeEntry("/a/b/c/" + Integer.toString(i)); queue.addNodeEntry(ne); if (i % 2 == 0) { ne.setNumberOfNodeVisits(2); } } queue.resortEvictionQueue(); NodeEntry ne; int count = 0; while ((ne = queue.getFirstNodeEntry()) != null) { if (count < 250) { assertEquals(0, ne.getNumberOfNodeVisits()); } else { assertEquals(2, ne.getNumberOfNodeVisits()); } queue.removeNodeEntry(ne); count++; } assertEquals(0, queue.getNumberOfNodes()); } public void testNumElements() throws Exception { LFUQueue queue = new LFUQueue(); NodeEntry ne = new NodeEntry("/a/b/c"); ne.setNumberOfElements(50); queue.addNodeEntry(ne); assertEquals(50, queue.getNumberOfElements()); assertEquals(1, queue.getNumberOfNodes()); queue.removeNodeEntry(ne); assertEquals(0, queue.getNumberOfElements()); for (int i = 0; i < 10; i++) { ne = new NodeEntry("/a/b/c/" + Integer.toString(i)); ne.setNumberOfElements(i); queue.addNodeEntry(ne); } assertEquals(45, queue.getNumberOfElements()); assertEquals(10, queue.getNumberOfNodes()); ne = queue.getNodeEntry("/a/b/c/0"); assertNotNull(ne); assertEquals(0, ne.getNumberOfElements()); ne.setNumberOfElements(500); assertEquals(545, queue.getNumberOfElements()); ne = queue.getNodeEntry("/a/b/c/0"); assertEquals(500, ne.getNumberOfElements()); queue.resortEvictionQueue(); ne = queue.getNodeEntry("/a/b/c/1"); assertNotNull(ne); assertEquals(1, ne.getNumberOfElements()); queue.resortEvictionQueue(); ne.setNumberOfElements(2); queue.resortEvictionQueue(); assertEquals(546, queue.getNumberOfElements()); queue.removeNodeEntry(ne); assertEquals(544, queue.getNumberOfElements()); assertEquals(9, queue.getNumberOfNodes()); queue.removeNodeEntry(queue.getNodeEntry("/a/b/c/0")); for (int i = 2; i < 10; i++) { ne = queue.getNodeEntry("/a/b/c/" + Integer.toString(i)); assertEquals(i, ne.getNumberOfElements()); queue.removeNodeEntry(ne); } assertEquals(0, queue.getNumberOfNodes()); assertEquals(0, queue.getNumberOfElements()); } }jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/eviction/ProgrammaticLRUPolicyTest.java0000644000175000017500000002021111017455042032142 0ustar twernertwerner/* * JBoss, Home of Professional Open Source. * Copyright 2006, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.cache.eviction; import org.jboss.cache.CacheFactory; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.RegionManager; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.EvictionConfig; import org.jboss.cache.config.EvictionRegionConfig; import org.jboss.cache.factories.UnitTestCacheConfigurationFactory; import org.jboss.cache.factories.XmlConfigurationParser; import org.jboss.cache.lock.IsolationLevel; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.xml.XmlHelper; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import org.w3c.dom.Element; /** * Unit tests for programmatic configuration of LRU policy * * @author Ben Wang, Oct, 2006 * @version $Revision: 5906 $ */ @Test(groups = {"functional"}) public class ProgrammaticLRUPolicyTest { CacheSPI cache_; int wakeupIntervalMillis_ = 0; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { initCaches(); wakeupIntervalMillis_ = cache_.getConfiguration().getEvictionConfig().getWakeupIntervalSeconds() * 1000; log("wakeupInterval is " + wakeupIntervalMillis_); if (wakeupIntervalMillis_ < 0) { fail("testEviction(): eviction thread wake up interval is illegal " + wakeupIntervalMillis_); } } private void initCaches() { Configuration conf = UnitTestCacheConfigurationFactory.createConfiguration(Configuration.CacheMode.LOCAL, true); CacheFactory instance = new DefaultCacheFactory(); cache_ = (CacheSPI) instance.createCache(conf, false); conf.getEvictionConfig().setWakeupIntervalSeconds(5); cache_.getConfiguration().setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); cache_.getConfiguration().setIsolationLevel(IsolationLevel.SERIALIZABLE); cache_.create(); cache_.start(); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { cache_.stop(); } private void addStringBasedRegion() throws Exception { // region name is ignored here. String xml = "" + "10000" + "4" + ""; Element element = XmlHelper.stringToElement(xml); RegionManager regionManager = cache_.getRegionManager(); EvictionConfig topConfig = cache_.getConfiguration().getEvictionConfig(); EvictionRegionConfig erc = XmlConfigurationParser.parseEvictionRegionConfig(element, topConfig.getDefaultEvictionPolicyClass(), topConfig.getDefaultEventQueueSize()); regionManager.setEvictionConfig(topConfig); // Fqn is the region name regionManager.getRegion("/programmatic", true).setEvictionPolicy(erc.getEvictionPolicyConfig()); } public void testStringBasedFqnEviction() throws Exception { addStringBasedRegion(); String rootStr = "/programmatic/"; for (int i = 0; i < 10; i++) { String str = rootStr + i; Fqn fqn = Fqn.fromString(str); cache_.put(fqn, str, str); } String val = (String) cache_.get(rootStr + "3", rootStr + "3"); assertNotNull("DataNode should be empty ", val); System.out.println(cache_.toString()); TestingUtil.sleepThread(2 * wakeupIntervalMillis_ + 500); System.out.println(cache_.toString()); val = (String) cache_.get(rootStr + "3", rootStr + "3"); assertNull("DataNode should be empty ", val); } private void addObjectBasedRegion() throws Exception { // region name is ignored here. String xml = "" + "10000" + "4" + ""; Element element = XmlHelper.stringToElement(xml); RegionManager regionManager = cache_.getRegionManager(); EvictionConfig topEC = cache_.getConfiguration().getEvictionConfig(); EvictionRegionConfig erc = XmlConfigurationParser.parseEvictionRegionConfig(element, topEC.getDefaultEvictionPolicyClass(), topEC.getDefaultEventQueueSize()); // Fqn is the region name Integer ii = 1; Fqn fqn = Fqn.fromElements(ii); regionManager.getRegion(fqn, true).setEvictionPolicy(erc.getEvictionPolicyConfig()); } public void testObjectBasedFqnEviction1() throws Exception { addStringBasedRegion(); String rootStr = "programmatic"; for (int i = 0; i < 10; i++) { String str = rootStr; Integer in = i; Fqn fqn = Fqn.fromElements(rootStr, in); try { cache_.put(fqn, str, str); } catch (Exception e) { fail("Failed to insert data" + e); e.printStackTrace(); } } Integer in = 3; Fqn fqn = Fqn.fromElements(rootStr, in); try { String val = (String) cache_.get(fqn, in); assertNull("DataNode should be empty ", val); } catch (Exception e) { e.printStackTrace(); fail("Failed to get" + e); } System.out.println(cache_.toString()); TestingUtil.sleepThread(2 * wakeupIntervalMillis_ + 500); System.out.println(cache_.toString()); try { String val = (String) cache_.get(fqn, in); assertNull("DataNode should be empty ", val); } catch (Exception e) { e.printStackTrace(); fail("Failed to get" + e); } } public void testObjectBasedFqnEviction2() throws Exception { addObjectBasedRegion(); Integer ii = 1; Fqn rootfqn = Fqn.fromElements(ii); for (int i = 0; i < 10; i++) { Integer in = i; Fqn fqn = Fqn.fromRelativeElements(rootfqn, in); try { cache_.put(fqn, in, in); } catch (Exception e) { fail("Failed to insert data" + e); e.printStackTrace(); } } try { Integer in = 3; Fqn fqn = Fqn.fromRelativeElements(rootfqn, in); Object val = cache_.get(fqn, in); assertNotNull("DataNode should be empty ", val); } catch (Exception e) { e.printStackTrace(); fail("Failed to get" + e); } System.out.println(cache_.toString()); TestingUtil.sleepThread(2 * wakeupIntervalMillis_ + 500); System.out.println(cache_.toString()); try { Integer in = 3; Fqn fqn = Fqn.fromRelativeElements(rootfqn, in); Object val = cache_.get(fqn, in); assertNull("DataNode should be empty ", val); } catch (Exception e) { e.printStackTrace(); fail("Failed to get" + e); } } private void log(String msg) { System.out.println("-- " + msg); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/eviction/ElementSizeConfigurationTest.java0000644000175000017500000000466210665031725032750 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.eviction; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertTrue; import static org.testng.AssertJUnit.fail; import org.jboss.cache.config.ConfigurationException; import org.jboss.cache.factories.XmlConfigurationParser; import org.jboss.cache.xml.XmlHelper; import org.testng.annotations.Test; import org.w3c.dom.Element; /** * @author Daniel Huang * @version $Revision: 4444 $ */ @Test(groups = {"functional"}) public class ElementSizeConfigurationTest { public void testXMLParse1() throws Exception { ElementSizeConfiguration config = new ElementSizeConfiguration(); String xml = "" + "1000" + "100" + ""; Element element = XmlHelper.stringToElement(xml); XmlConfigurationParser.parseEvictionPolicyConfig(element, config); assertEquals(100, config.getMaxElementsPerNode()); assertEquals(1000, config.getMaxNodes()); } public void testXMLParse2() throws Exception { ElementSizeConfiguration config = new ElementSizeConfiguration(); String xml = "" + "1000" + ""; Element element = XmlHelper.stringToElement(xml); try { XmlConfigurationParser.parseEvictionPolicyConfig(element, config); } catch (ConfigurationException ce) { assertTrue("Configure exception properly thrown", true); return; } fail("Invalid region Element Size configuration did not cause ConfigureException to be thrown with empty maxElementsPerNode attribute"); } public void testXMLParse3() throws Exception { ElementSizeConfiguration config = new ElementSizeConfiguration(); String xml = "" + "100" + ""; Element element = XmlHelper.stringToElement(xml); XmlConfigurationParser.parseEvictionPolicyConfig(element, config); assertEquals(100, config.getMaxElementsPerNode()); assertEquals(0, config.getMaxNodes()); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/eviction/LRUQueueTest.java0000644000175000017500000001545210665031725027442 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.eviction; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertFalse; import static org.testng.AssertJUnit.assertNotNull; import static org.testng.AssertJUnit.assertNull; import static org.testng.AssertJUnit.assertTrue; import java.util.Iterator; import org.jboss.cache.Fqn; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * Unit tests for LRUQueue. * * @author Daniel Huang (dhuang@jboss.org) * @version $Revision: 4444 $ */ @Test(groups = {"functional"}) public class LRUQueueTest { private LRUQueue queue; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { queue = new LRUQueue(); } public void testQueue() throws Exception { for (int i = 0; i < 500; i++) { NodeEntry ne = new NodeEntry("/a/b/c/" + Integer.toString(i)); queue.addNodeEntry(ne); } assertEquals(500, queue.getNumberOfNodes()); for (int i = 0; i < 500; i++) { Fqn fqn = Fqn.fromString("/a/b/c/" + Integer.toString(i)); if ((i < 100) || (i >= 300 && i < 400)) { // visit the nodes from 0-99 and the nodes from 300 - 399 queue.reorderByLRU(fqn); } } // visiting the nodes should have no affect ont he maxAgeQueue. Iterator maxAgeIt = queue.iterateMaxAgeQueue(); int count = 0; long lastTs = 0; while (maxAgeIt.hasNext()) { NodeEntry ne = (NodeEntry) maxAgeIt.next(); assertTrue(lastTs <= ne.getCreationTimeStamp()); lastTs = ne.getCreationTimeStamp(); count++; } assertEquals(500, count); Iterator lruIt = queue.iterateLRUQueue(); count = 0; while (lruIt.hasNext()) { NodeEntry ne = (NodeEntry) lruIt.next(); int nodeIndex = Integer.parseInt((String) ne.getFqn().get(3)); // the last 200 in the list should be the visisted LRU ones. if (count >= 300 && count < 400) { int expectedNodeIndex = count - 300; assertEquals(expectedNodeIndex, nodeIndex); } else if (count >= 400 && count < 500) { int expectedNodeIndex = count - 100; assertEquals(expectedNodeIndex, nodeIndex); } else if (count < 200) { int expectedNodeIndex = count + 100; assertEquals(expectedNodeIndex, nodeIndex); } else if (count >= 200 && count < 300) { int expectedNodeIndex = count + 200; assertEquals(expectedNodeIndex, nodeIndex); } count++; } assertEquals(500, count); NodeEntry ne = queue.getFirstMaxAgeNodeEntry(); queue.removeNodeEntry(ne); assertEquals(499, queue.getNumberOfNodes()); assertFalse(queue.containsNodeEntry(ne)); ne = queue.getFirstLRUNodeEntry(); queue.removeNodeEntry(ne); assertEquals(498, queue.getNumberOfNodes()); assertFalse(queue.containsNodeEntry(ne)); } public void testGetFirstLRUNodeEntry() throws Exception { for (int i = 0; i < 100; i++) { NodeEntry ne = new NodeEntry("/a/b/c/" + Integer.toString(i)); queue.addNodeEntry(ne); } for (int i = 0; i < 100; i++) { // this should move all the even numbered NodeEntries to the bottom of the lruQueue. // maxAgeQueue should be unaffected. if (i % 2 == 0) { Fqn fqn = Fqn.fromString("/a/b/c/" + Integer.toString(i)); queue.reorderByLRU(fqn); } } assertEquals(100, queue.getNumberOfNodes()); NodeEntry ne; int count = 0; while ((ne = queue.getFirstLRUNodeEntry()) != null) { int nodeIndex = Integer.parseInt((String) ne.getFqn().get(3)); if (count < 50) { // the top 50 should be all odds in the lruQueue/ assertTrue(nodeIndex % 2 != 0); } else { // the bottom fifty should all be even #'s (and 0) assertTrue(nodeIndex % 2 == 0); } queue.removeNodeEntry(ne); count++; } assertEquals(0, queue.getNumberOfNodes()); } public void testGetFirstMaxAgeNodeEntriy() throws Exception { for (int i = 0; i < 100; i++) { NodeEntry ne = new NodeEntry("/a/b/c/" + Integer.toString(i)); queue.addNodeEntry(ne); } for (int i = 0; i < 100; i++) { // this should move all the even numbered NodeEntries to the bottom of the lruQueue. // maxAgeQueue should be unaffected. if (i % 2 == 0) { Fqn fqn = Fqn.fromString("/a/b/c/" + Integer.toString(i)); queue.reorderByLRU(fqn); } } assertEquals(100, queue.getNumberOfNodes()); NodeEntry ne; int count = 0; while ((ne = queue.getFirstMaxAgeNodeEntry()) != null) { int nodeIndex = Integer.parseInt((String) ne.getFqn().get(3)); assertEquals(count, nodeIndex); queue.removeNodeEntry(ne); count++; } assertEquals(0, queue.getNumberOfNodes()); } public void testNumElements() throws Exception { LRUQueue queue = new LRUQueue(); NodeEntry ne = new NodeEntry("/a/b/c"); ne.setNumberOfElements(50); queue.addNodeEntry(ne); assertEquals(50, queue.getNumberOfElements()); assertEquals(1, queue.getNumberOfNodes()); queue.removeNodeEntry(ne); assertEquals(0, queue.getNumberOfElements()); for(int i = 0; i < 10; i++) { ne = new NodeEntry("/a/b/c/" + Integer.toString(i)); ne.setNumberOfElements(i); queue.addNodeEntry(ne); } assertEquals(45, queue.getNumberOfElements()); assertEquals(10, queue.getNumberOfNodes()); ne = queue.getNodeEntry("/a/b/c/0"); assertNotNull(ne); assertEquals(0, ne.getNumberOfElements()); ne.setNumberOfElements(500); assertEquals(545, queue.getNumberOfElements()); ne = queue.getNodeEntry("/a/b/c/0"); assertEquals(500, ne.getNumberOfElements()); queue.removeNodeEntry(ne); assertEquals(45, queue.getNumberOfElements()); assertEquals(9, queue.getNumberOfNodes()); for(int i = 1; i < 10; i++) { ne = queue.getNodeEntry("/a/b/c/" + Integer.toString(i)); assertEquals(i, ne.getNumberOfElements()); queue.removeNodeEntry(ne); } assertEquals(0, queue.getNumberOfNodes()); assertEquals(0, queue.getNumberOfElements()); assertNull(queue.getNodeEntry("/a/b/c/0")); assertNull(queue.getFirstNodeEntry()); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/eviction/LFUPolicyTest.java0000644000175000017500000002367211017455042027576 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.eviction; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.EvictionConfig; import org.jboss.cache.config.EvictionRegionConfig; import org.jboss.cache.factories.UnitTestCacheConfigurationFactory; import org.jboss.cache.lock.IsolationLevel; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.List; import java.util.ArrayList; /** * Unit tests for LFU Policy. * * @author Daniel Huang (dhuang@jboss.org) * @version $Revision: 5906 $ */ @Test(groups = {"functional"}) public class LFUPolicyTest { CacheSPI cache; int wakeupIntervalMillis = 0; final String ROOT_STR = "/test"; Throwable t1_ex, t2_ex; final long DURATION = 10000; boolean isTrue; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { initCaches(); wakeupIntervalMillis = cache.getConfiguration().getEvictionConfig().getWakeupIntervalSeconds() * 1000; log("wakeupInterval is " + wakeupIntervalMillis); if (wakeupIntervalMillis < 0) { fail("testEviction(): eviction thread wake up interval is illegal " + wakeupIntervalMillis); } t1_ex = t2_ex = null; isTrue = true; } void initCaches() throws Exception { Configuration config = UnitTestCacheConfigurationFactory.createConfiguration(Configuration.CacheMode.LOCAL, true); EvictionConfig evConfig = config.getEvictionConfig(); evConfig.setWakeupIntervalSeconds(3); evConfig.setDefaultEventQueueSize(200000); List erConfigs = new ArrayList(); erConfigs.add(createEvictionRegionConfig("/_default_",500,10)); erConfigs.add(createEvictionRegionConfig("/org/jboss/data",5000,4000)); erConfigs.add(createEvictionRegionConfig("/org/jboss/test/data",-1,5)); evConfig.setEvictionRegionConfigs(erConfigs); config.setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); config.setIsolationLevel(IsolationLevel.SERIALIZABLE); cache = (CacheSPI) new DefaultCacheFactory().createCache(config, true); } private EvictionRegionConfig createEvictionRegionConfig(String regionName, int maxNodes, int minNodes) { EvictionRegionConfig ercDefault = new EvictionRegionConfig(); ercDefault.setRegionName(regionName); LFUConfiguration esConfig = new LFUConfiguration(); if (maxNodes >= 0) esConfig.setMaxNodes(maxNodes); if (minNodes >= 0) esConfig.setMinNodes(minNodes); ercDefault.setEvictionPolicyConfig(esConfig); esConfig.setEvictionPolicyClassName(); return ercDefault; } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { cache.stop(); } public void testEviction() throws Exception { String rootStr = "/org/jboss/data/"; for (int i = 0; i < 8000; i++) { String str = rootStr + i; Fqn fqn = Fqn.fromString(str); try { cache.put(fqn, str, str); } catch (Exception e) { fail("Failed to insert data" + e); e.printStackTrace(); } // visit odd numbered nodes an extra time to make them get evicted last. if (i % 2 != 0) { cache.get(fqn, str); } } long period = wakeupIntervalMillis + 500; TestingUtil.sleepThread(period); for (int i = 0; i < 8000; i++) { String str = rootStr + i; Fqn fqn = Fqn.fromString(str); if (i % 2 == 0) { assertNull(cache.get(fqn, str)); } else { assertNotNull(cache.get(fqn, str)); } } } public void testNodeVisited() { String rootStr = "/org/jboss/test/data/"; for (int i = 0; i < 10; i++) { String str = rootStr + i; Fqn fqn = Fqn.fromString(str); try { cache.put(fqn, str, str); } catch (Exception e) { fail("Failed to insert data" + e); e.printStackTrace(); } } int period = wakeupIntervalMillis + 500; log("sleeping for " + period + "ms"); TestingUtil.sleepThread(period);// it really depends the eviction thread time. try { for (int i = 0; i < 5; i++) { String str = rootStr + Integer.toString(i); Fqn fqn = Fqn.fromString(str); assertNull(cache.get(fqn, str)); } for (int i = 5; i < 10; i++) { String str = rootStr + Integer.toString(i); Fqn fqn = Fqn.fromString(str); assertNotNull(cache.get(fqn, str)); } TestingUtil.sleepThread(period); // since min is 5 the cache won't deplete past 5 nodes left in the cache. for (int i = 5; i < 10; i++) { String str = rootStr + Integer.toString(i); Fqn fqn = Fqn.fromString(str); assertNotNull(cache.get(fqn, str)); } // now we add some more nodes and we selectively visit some older nodes but not all. they should not get // evicted when the thread next runs. for (int i = 5; i < 7; i++) { String str = rootStr + Integer.toString(i); Fqn fqn = Fqn.fromString(str); cache.get(fqn, str); } // add 2 more to push the limit to 5 so we cause the old unvisited nodes to get evicted. for (int i = 10; i < 13; i++) { String str = rootStr + Integer.toString(i); Fqn fqn = Fqn.fromString(str); cache.put(fqn, str, str); // now bring up their hit count for LFU purposes (cache hit count based eviction). for (int k = 0; k < 10; k++) { cache.get(fqn, str); } } TestingUtil.sleepThread(period); // now make sure we still only have 5 nodes and they are the ones we expect based on LFU for (int i = 5; i < 7; i++) { String str = rootStr + Integer.toString(i); Fqn fqn = Fqn.fromString(str); assertNotNull(cache.get(fqn, str)); } for (int i = 7; i < 10; i++) { String str = rootStr + Integer.toString(i); Fqn fqn = Fqn.fromString(str); assertNull(cache.get(fqn, str)); } for (int i = 10; i < 13; i++) { String str = rootStr + Integer.toString(i); Fqn fqn = Fqn.fromString(str); assertNotNull(cache.get(fqn, str)); } } catch (Exception e) { e.printStackTrace(); fail("Failed to evict" + e); } } public void testNodeRemoved() throws Exception { String rootStr = "/org/jboss/data/"; for (int i = 0; i < 5000; i++) { String str = rootStr + i; Fqn fqn = Fqn.fromString(str); try { cache.put(fqn, str, str); } catch (Exception e) { fail("Failed to insert data" + e); e.printStackTrace(); } } int period = (wakeupIntervalMillis + 500); log("period is " + period); TestingUtil.sleepThread(period); for (int i = 0; i < 1000; i++) { String str = rootStr + i; Fqn fqn = Fqn.fromString(str); assertNull(cache.get(fqn, str)); } for (int i = 1000; i < 5000; i++) { String str = rootStr + i; Fqn fqn = Fqn.fromString(str); assertNotNull(cache.get(fqn, str)); } for (int i = 1000; i < 5000; i++) { if (i % 2 == 0) { String str = rootStr + i; Fqn fqn = Fqn.fromString(str); cache.removeNode(fqn); } } TestingUtil.sleepThread(period); for (int i = 1000; i < 5000; i++) { if (i % 2 == 0) { String str = rootStr + i; Fqn fqn = Fqn.fromString(str); assertNull(cache.getNode(fqn)); } } } class MyPutter extends Thread { public MyPutter(String name) { super(name); } public void run() { int i = 0; final String myName = ROOT_STR + "/test1/node" + getName(); while (isTrue) { try { cache.put(myName + i++, "value", i); sleep(1); } catch (Throwable e) { e.printStackTrace(); if (t1_ex == null) { t1_ex = e; } } } } } public void testConcurrentPutAndEvict() throws Exception { cache.stop(); cache.destroy(); cache.getConfiguration().setIsolationLevel(IsolationLevel.REPEATABLE_READ); cache.start(); cache.put(ROOT_STR + "/concurrentPutAndEvict", "value", 1); for (int i = 0; i < 10; i++) { new MyPutter("Putter" + i).start(); } int counter = 0; while (true) { counter++; if (t1_ex != null) { fail("Exception generated in put() " + t1_ex); } log("nodes/locks: " + cache.getNumberOfNodes() + "/" + cache.getNumberOfLocksHeld()); TestingUtil.sleepThread(1000); if (counter > 10) {// run for 10 seconds isTrue = false; break; } } } void log(String msg) { System.out.println("-- " + msg); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/eviction/LRUPolicyTest.java0000755000175000017500000002611511017455042027610 0ustar twernertwernerpackage org.jboss.cache.eviction; import org.jboss.cache.*; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.EvictionConfig; import org.jboss.cache.config.EvictionRegionConfig; import org.jboss.cache.factories.UnitTestCacheConfigurationFactory; import org.jboss.cache.lock.IsolationLevel; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.ArrayList; import java.util.List; /** * Unit tests for LRU Policy. * * @author Ben Wang, Feb 11, 2004 * @author Daniel Huang - dhuang@jboss.org * @version $Revision: 5906 $ */ @Test(groups = "functional") public class LRUPolicyTest { CacheSPI cache; int wakeupIntervalMillis_ = 0; int dataRegionTTLMillis = 6000; int testRegionTTLMillis = 4000; final String ROOT_STR = "/test"; Throwable t1_ex, t2_ex; final long DURATION = 10000; boolean isTrue; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { Configuration conf = UnitTestCacheConfigurationFactory.createConfiguration(Configuration.CacheMode.LOCAL, true); EvictionConfig evConfig = conf.getEvictionConfig(); evConfig.setWakeupIntervalSeconds(1); List regionConfigs = new ArrayList(); regionConfigs.add(UnitTestCacheConfigurationFactory.buildLruEvictionRegionConfig("/org/jboss/test/data", 5, dataRegionTTLMillis / 1000)); regionConfigs.add(UnitTestCacheConfigurationFactory.buildLruEvictionRegionConfig("/test", 10000, testRegionTTLMillis / 1000)); evConfig.setEvictionRegionConfigs(regionConfigs); conf.setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); conf.setIsolationLevel(IsolationLevel.SERIALIZABLE); cache = (CacheSPI) new DefaultCacheFactory().createCache(conf); wakeupIntervalMillis_ = cache.getConfiguration().getEvictionConfig().getWakeupIntervalSeconds() * 1000; System.out.println("-- wakeupInterval is " + wakeupIntervalMillis_); if (wakeupIntervalMillis_ < 0) { fail("testEviction(): eviction thread wake up interval is illegal " + wakeupIntervalMillis_); } t1_ex = t2_ex = null; isTrue = true; } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { cache.stop(); } public void testInUseEviction() throws Exception { String rootStr = "/org/jboss/test/data/inuse/"; Fqn fqn; for (int i = 0; i < 10; i++) { String str = rootStr + i; fqn = Fqn.fromString(str); cache.put(fqn, str, str); } System.out.println("-- Marking as in-use"); cache.getRegionManager().getRegion(Fqn.fromString(rootStr + 5), false).markNodeCurrentlyInUse(Fqn.fromString(rootStr + 5), 0); for (int i = 10; i < 15; i++) { String str = rootStr + i; fqn = Fqn.fromString(str); cache.put(fqn, str, str); } TestingUtil.sleepThread(wakeupIntervalMillis_ + 500); for (int i = 0; i < 5; i++) { Fqn f = Fqn.fromString(rootStr + i); assert null == cache.getNode(f) : f + " should be null"; } assertNotNull(cache.getNode(Fqn.fromString(rootStr + 5))); for (int i = 6; i < 11; i++) { Fqn f = Fqn.fromString(rootStr + i); assert null == cache.getNode(f) : f + " should be null"; } for (int i = 11; i < 15; i++) { Fqn f = Fqn.fromString(rootStr + i); assert null != cache.getNode(f) : f + " should not be null"; } } public void testEviction() { String rootStr = "/org/jboss/test/data/"; for (int i = 0; i < 10; i++) { String str = rootStr + i; Fqn fqn = Fqn.fromString(str); try { cache.put(fqn, str, str); } catch (Exception e) { fail("Failed to insert data" + e); e.printStackTrace(); } } System.out.println(cache.toString()); TestingUtil.sleepThread(wakeupIntervalMillis_ + 500); System.out.println(cache.toString()); String val = (String) cache.get(rootStr + "3", rootStr + "3"); assertNull("Node should be empty ", val); } public void testNodeVisited() { String rootStr = "/org/jboss/test/data/"; System.out.println("REGIONS: " + cache.getRegionManager().dumpRegions()); for (int i = 0; i < 10; i++) { String str = rootStr + i; Fqn fqn = Fqn.fromString(str); cache.put(fqn, str, str); } int period = (wakeupIntervalMillis_ / 2 + 500); System.out.println("-- sleeping for " + period + "ms"); TestingUtil.sleepThread(period); String str = rootStr + "7"; Fqn fqn = Fqn.fromString(str); cache.get(fqn, str);// just to keep it fresh System.out.println("-- sleeping for " + period + "ms"); TestingUtil.sleepThread(period); cache.get(fqn, str);// just to keep it fresh System.out.println("-- sleeping for " + period + "ms"); TestingUtil.sleepThread(period); String val = (String) cache.get(rootStr + "3", rootStr + "3"); System.out.println("-- val=" + val); assertNull("Node should be empty ", val); val = (String) cache.get(rootStr + "7", rootStr + "7"); System.out.println("-- val=" + val); assertNotNull("Node should not be empty ", val); period = dataRegionTTLMillis + wakeupIntervalMillis_ + 500; // this is the TTL for nodes + time for the eviction thread to kick in System.out.println("-- sleeping for " + period + "ms"); TestingUtil.sleepThread(period); val = (String) cache.get(rootStr + "7", rootStr + "7"); System.out.println("-- val=" + val); assertNull("Node should be empty ", val); } public void testNodeRemoved() { String rootStr = "/org/jboss/test/data/"; for (int i = 0; i < 10; i++) { String str = rootStr + i + "/" + i; Fqn fqn = Fqn.fromString(str); cache.put(fqn, str, str); } int period = (wakeupIntervalMillis_ / 2 + 500); System.out.println("-- period is " + period); // TestingUtil.sleepThread(period); // it really depends the eviction thread time. String str1 = rootStr + "7"; Fqn fqn1 = Fqn.fromString(str1); String str2 = rootStr + "7/7"; Fqn fqn2 = Fqn.fromString(str2); cache.get(fqn1, str1);// just to keep it fresh cache.get(fqn2, str2);// just to keep it fresh TestingUtil.sleepThread(period); cache.get(fqn1, str1);// just to keep it fresh cache.get(fqn2, str2);// just to keep it fresh TestingUtil.sleepThread(period); String val = (String) cache.get(rootStr + "7/7", rootStr + "7/7"); assertNotNull("Node should not be empty ", val); cache.removeNode(fqn1); TestingUtil.sleepThread(wakeupIntervalMillis_ + 500); val = (String) cache.get(rootStr + "7/7", rootStr + "7/7"); assertNull("Node should be empty ", val); } public void testCompleteRemoval() throws Exception { String rootStr = "/test/"; // Add a parent, then a child. LRU will evict the parent, // then the child, leaving behind an empty parent Fqn parent = Fqn.fromString(rootStr + "parent"); cache.put(parent, "key", "value"); cache.put(Fqn.fromRelativeElements(parent, "child"), "key", "value"); // Give eviction time to run a few times, then confirm parent // is completely gone int period = (wakeupIntervalMillis_ + testRegionTTLMillis) * 2; System.out.println("-- Sleeping for " + period); TestingUtil.sleepThread(period); assertFalse("Parent not completely removed", cache.getRoot().hasChild(parent)); } class MyPutter extends Thread { public MyPutter(String name) { super(name); } public void run() { int i = 0; final String myName = ROOT_STR + "/test1/node" + getName(); while (isTrue) { try { cache.put(myName + i++, "value", i); sleep(1); } catch (IllegalStateException ise) { // this will happen since some threads are bound to continue running while the test stops. // do nothing. } catch (Throwable e) { e.printStackTrace(); if (t1_ex == null) { t1_ex = e; } } } } } public void testConcurrentPutAndEvict() throws Exception { cache.stop(); cache.destroy(); cache.getConfiguration().setIsolationLevel(IsolationLevel.REPEATABLE_READ); cache.create(); cache.start(); cache.put(ROOT_STR + "/concurrentPutAndEvict", "value", 1); for (int i = 0; i < 10; i++) { new MyPutter("Putter" + i).start(); } int counter = 0; while (true) { counter++; if (t1_ex != null) { fail("Exception generated in put() " + t1_ex); } System.out.println("-- nodes/locks: " + cache.getNumberOfNodes() + "/" + cache.getNumberOfLocksHeld()); TestingUtil.sleepThread(1000); if (counter > 10) {// run for 10 seconds isTrue = false; break; } } } public void testForEvictionInternalError() { String rootStr = "/test/testdata"; for (int i = 0; i < 10; i++) { String str = rootStr + i; Fqn fqn = Fqn.fromString(str); cache.put(fqn, str, str); } // wait for an eviction TestingUtil.sleepThread(2 * (wakeupIntervalMillis_ + testRegionTTLMillis)); String val = (String) cache.get(rootStr + "3", rootStr + "3"); assertNull("Node should be empty ", val); // reinsert the elements for (int i = 0; i < 10; i++) { String str = rootStr + i; Fqn fqn = Fqn.fromString(str); cache.put(fqn, str, str); } // clear the root cache.removeNode(Fqn.ROOT); // wait for an eviction TestingUtil.sleepThread(2 * wakeupIntervalMillis_ + 1000); val = (String) cache.get(rootStr + "3", rootStr + "3"); assertNull("Node should be empty ", val); } public void testOvereviction() throws Exception { Configuration c = UnitTestCacheConfigurationFactory.createConfiguration(Configuration.CacheMode.LOCAL, true); Cache cache1 = new DefaultCacheFactory().createCache(c, true); cache1.put(Fqn.fromString("/base/" + 0), "key", "base" + 0); Node node = cache1.getRoot().getChild(Fqn.fromString("/base/")); node.setResident(true); for (int i = 1; i < 5100; i++) { cache1.put(Fqn.fromString("/base/" + i), "key", "base" + i); } Thread.sleep(5000); assertEquals(5000, cache1.getRoot().getChild(Fqn.fromString("/base")).getChildren().size()); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/eviction/MRUAlgorithmTest.java0000644000175000017500000001156410665031725030305 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.eviction; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertNotNull; import static org.testng.AssertJUnit.assertNull; import org.jboss.cache.Fqn; import org.jboss.cache.Region; import org.jboss.cache.RegionManager; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * Unit tests for MRUAlgorithm. * * @author Daniel Huang (dhuang@jboss.org) * @version $Revision: 4444 $ */ @Test(groups = {"functional"}) public class MRUAlgorithmTest { MRUAlgorithm algorithm; RegionManager regionManager; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { algorithm = new MRUAlgorithm(); MRUConfiguration config = new MRUConfiguration(); // We have to setCache maxNodes!! config.setMaxNodes(0); config.setEvictionPolicyClass(DummyEvictionPolicy.class.getName()); regionManager = new RegionManager(); regionManager.getRegion("/a/b", true).setEvictionPolicy(config); } public void testMaxNodes() throws Exception { Fqn fqn1 = Fqn.fromString("/a/b/c"); Fqn fqn2 = Fqn.fromString("/a/b/d"); Fqn fqn3 = Fqn.fromString("/a/b/e"); Region region = regionManager.getRegion("/a/b", true); MRUConfiguration config = (MRUConfiguration) region.getEvictionPolicyConfig(); config.setMaxNodes(1); region.putNodeEvent(new EvictedEventNode(fqn1, NodeEventType.ADD_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn2, NodeEventType.ADD_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn3, NodeEventType.ADD_NODE_EVENT)); algorithm.process(region); assertEquals(1, algorithm.getEvictionQueue().getNumberOfNodes()); config.setMaxNodes(100); for (int i = 0; i < 150; i++) { Fqn fqn = Fqn.fromString("/a/b/c/" + Integer.toString(i)); region.putNodeEvent(new EvictedEventNode(fqn, NodeEventType.ADD_NODE_EVENT)); } algorithm.process(region); assertEquals(100, algorithm.getEvictionQueue().getNumberOfNodes()); } public void testMRU() throws Exception { Fqn fqn1 = Fqn.fromString("/a/b/c"); Fqn fqn2 = Fqn.fromString("/a/b/d"); Fqn fqn3 = Fqn.fromString("/a/b/e"); Fqn fqn4 = Fqn.fromString("/a/b/f"); Fqn fqn5 = Fqn.fromString("/a/b/g"); Fqn fqn6 = Fqn.fromString("/a/b/h"); Fqn fqn7 = Fqn.fromString("/a/b/i"); Fqn fqn8 = Fqn.fromString("/a/b/j"); Fqn fqn9 = Fqn.fromString("/a/b/k"); Fqn fqn10 = Fqn.fromString("/a/b/l"); Region region = regionManager.getRegion("/a/b", true); MRUConfiguration config = (MRUConfiguration) region.getEvictionPolicyConfig(); config.setMaxNodes(8); region.putNodeEvent(new EvictedEventNode(fqn1, NodeEventType.ADD_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn2, NodeEventType.ADD_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn3, NodeEventType.ADD_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn4, NodeEventType.ADD_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn5, NodeEventType.ADD_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn6, NodeEventType.ADD_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn7, NodeEventType.ADD_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn8, NodeEventType.ADD_NODE_EVENT)); algorithm.process(region); assertEquals(8, algorithm.getEvictionQueue().getNumberOfNodes()); region.putNodeEvent(new EvictedEventNode(fqn9, NodeEventType.ADD_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn10, NodeEventType.ADD_NODE_EVENT)); Thread.sleep(5000); assertEquals(8, algorithm.getEvictionQueue().getNumberOfNodes()); region.putNodeEvent(new EvictedEventNode(fqn2, NodeEventType.ADD_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn4, NodeEventType.ADD_NODE_EVENT)); algorithm.process(region); assertEquals(8, algorithm.getEvictionQueue().getNumberOfNodes()); assertNull(algorithm.getEvictionQueue().getNodeEntry(fqn2)); assertNull("No FQN4 " + algorithm.getEvictionQueue(), algorithm.getEvictionQueue().getNodeEntry(fqn4)); assertNotNull(algorithm.getEvictionQueue().getNodeEntry(fqn1)); assertNotNull(algorithm.getEvictionQueue().getNodeEntry(fqn3)); assertNotNull(algorithm.getEvictionQueue().getNodeEntry(fqn5)); assertNotNull(algorithm.getEvictionQueue().getNodeEntry(fqn6)); assertNotNull(algorithm.getEvictionQueue().getNodeEntry(fqn7)); assertNotNull(algorithm.getEvictionQueue().getNodeEntry(fqn8)); assertNotNull(algorithm.getEvictionQueue().getNodeEntry(fqn9)); assertNotNull(algorithm.getEvictionQueue().getNodeEntry(fqn10)); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/eviction/LRUConfigurationTest.java0000644000175000017500000000566710665031725031174 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.eviction; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertTrue; import org.jboss.cache.config.ConfigurationException; import org.jboss.cache.factories.XmlConfigurationParser; import org.jboss.cache.xml.XmlHelper; import org.testng.annotations.Test; import org.w3c.dom.Element; /** * Unit tests for LRUConfiguration. * * @author Daniel Huang (dhuang@jboss.org) * @version $Revision: 4444 $ */ @Test(groups = {"functional"}) public class LRUConfigurationTest { public void testXMLParsing() throws Exception { LRUConfiguration config = new LRUConfiguration(); String xml = "\n" + "5000\n" + "1000\n" + ""; Element element = XmlHelper.stringToElement(xml); XmlConfigurationParser.parseEvictionPolicyConfig(element, config); assertEquals(5000, config.getMaxNodes()); assertEquals(1000, config.getTimeToLiveSeconds()); } public void testXMLParsing2() throws Exception { LRUConfiguration config = new LRUConfiguration(); String xml = "\n" + "10000\n" + "8\n" + "10\n" + ""; Element element = XmlHelper.stringToElement(xml); XmlConfigurationParser.parseEvictionPolicyConfig(element, config); assertEquals(10000, config.getMaxNodes()); assertEquals(8, config.getTimeToLiveSeconds()); assertEquals(10, config.getMaxAgeSeconds()); } public void testXMLParsing3() throws Exception { LRUConfiguration config = new LRUConfiguration(); String xml = "\n" + "10000\n" + "10\n" + ""; Element element = XmlHelper.stringToElement(xml); boolean caught = false; try { XmlConfigurationParser.parseEvictionPolicyConfig(element, config); } catch (ConfigurationException ce) { caught = true; } assertTrue("Configure exception should have been caught", caught); xml = "\n" + "8\n" + "10\n" + ""; element = XmlHelper.stringToElement(xml); XmlConfigurationParser.parseEvictionPolicyConfig(element, config); assertEquals(0, config.getMaxNodes()); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/eviction/RegionTest.java0000755000175000017500000000624710665031725027223 0ustar twernertwernerpackage org.jboss.cache.eviction; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertNull; import org.jboss.cache.Fqn; import org.jboss.cache.Region; import org.jboss.cache.RegionManager; import org.jboss.cache.config.EvictionConfig; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * @author Ben Wang, Feb 11, 2004 * @author Daniel Huang (dhuang@jboss.org) */ @Test(groups = {"functional"}) public class RegionTest { RegionManager regionManager_; EvictionAlgorithm algorithm; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { algorithm = new LRUAlgorithm(); regionManager_ = new RegionManager(); regionManager_.getRegion("/a/b", true).setEvictionPolicy(new DummyEvictionConfiguration()); } public void testAddedQueue() { Fqn fqn1 = Fqn.fromString("/a/b/c"); Fqn fqn2 = Fqn.fromString("/a/b/d"); Fqn fqn3 = Fqn.fromString("/a/b/e"); Region region = regionManager_.getRegion("/a/b", true); region.putNodeEvent(new EvictedEventNode(fqn1, NodeEventType.ADD_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn2, NodeEventType.ADD_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn3, NodeEventType.ADD_NODE_EVENT)); assertEquals("AddedNode queue size ", 3, region.nodeEventQueueSize()); EvictedEventNode node = region.takeLastEventNode(); Fqn fqn = node.getFqn(); assertEquals("DataNode retrieved should be FILO ", fqn, fqn1); assertEquals("AddedNode queue size ", 2, region.nodeEventQueueSize()); fqn = region.takeLastEventNode().getFqn(); fqn = region.takeLastEventNode().getFqn(); node = region.takeLastEventNode(); assertNull("DataNode should be null", node); } public void testEventQueue() { Fqn fqn1 = Fqn.fromString("/a/b/c"); Fqn fqn2 = Fqn.fromString("/a/b/d"); Fqn fqn3 = Fqn.fromString("/a/b/e"); Region region = regionManager_.getRegion("/a/b", true); region.putNodeEvent(new EvictedEventNode(fqn1, NodeEventType.REMOVE_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn2, NodeEventType.ADD_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn3, NodeEventType.VISIT_NODE_EVENT)); assertEquals("RemovedNode queue size ", 3, region.nodeEventQueueSize()); NodeEventType event = region.takeLastEventNode().getEventType(); assertEquals("DataNode retrieved should be: ", NodeEventType.REMOVE_NODE_EVENT, event); region.takeLastEventNode(); region.takeLastEventNode(); EvictedEventNode node = region.takeLastEventNode(); assertNull("DataNode should be null", node); } public void testMassivePutOnQueue() { Fqn fqn2 = Fqn.fromString("/a/b/d"); Region region = regionManager_.getRegion("/a/b", true); // This should succeed, alhtough it will produce warning over the threshold. for (int i = 0; i < EvictionConfig.EVENT_QUEUE_SIZE_DEFAULT - 1; i++) { region.putNodeEvent(new EvictedEventNode(fqn2, NodeEventType.ADD_NODE_EVENT)); } } void log(String msg) { System.out.println("-- " + msg); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/eviction/minttl/0000755000175000017500000000000011376173734025577 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/eviction/minttl/FIFOMinTTLTest.java0000644000175000017500000000341111017455042031037 0ustar twernertwernerpackage org.jboss.cache.eviction.minttl; import org.jboss.cache.Fqn; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.eviction.EvictionPolicyConfigBase; import org.jboss.cache.eviction.FIFOConfiguration; import org.testng.annotations.AfterMethod; import org.testng.annotations.Test; /** * @author Manik Surtani * @since 2.1.0 */ @Test(groups = {"functional"}) public class FIFOMinTTLTest extends MinTTLTestBase { private Fqn fqn2 = Fqn.fromRelativeElements(region, "b"); private Thread busyThread; private boolean busyThreadRunning = true; @Override protected EvictionPolicyConfigBase getEvictionPolicyConfig() { startBusyThread(); FIFOConfiguration cfg = new FIFOConfiguration(); cfg.setMaxNodes(1); return cfg; } @AfterMethod public void stopBusyThread() { busyThreadRunning = false; try { busyThread.join(); } catch (InterruptedException e) { } } private void startBusyThread() { // start a thread to constantly put another node in the cache to make sure the maxNodes is exceeded. // this should only happen AFTER the main node is entered to guarantee FIFO. busyThreadRunning = true; busyThread = new Thread("BusyThread") { public void run() { try { cacheInitialisedLatch.await(); } catch (InterruptedException e) { // do nothing } while (busyThreadRunning) { cache.put(fqn2, "k", "v"); TestingUtil.sleepRandom(150); } } }; busyThread.setDaemon(true); busyThread.start(); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/eviction/minttl/MRUMinTTLTest.java0000644000175000017500000000307211017455042030762 0ustar twernertwernerpackage org.jboss.cache.eviction.minttl; import org.jboss.cache.CacheStatus; import org.jboss.cache.Fqn; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.eviction.EvictionPolicyConfigBase; import org.jboss.cache.eviction.MRUConfiguration; import org.testng.annotations.Test; /** * @author Manik Surtani * @since 2.1.0 */ @Test(groups = {"functional"}) public class MRUMinTTLTest extends MinTTLTestBase { private Fqn fqn2 = Fqn.fromRelativeElements(region, "b"); @Override protected EvictionPolicyConfigBase getEvictionPolicyConfig() { MRUConfiguration cfg = new MRUConfiguration(); cfg.setMaxNodes(1); startBusyThread(); return cfg; } private void startBusyThread() { // start a thread to constantly put another node in the cache to make sure the maxNodes is exceeded. // this should only happen AFTER the main node is entered to guarantee FIFO. Thread busyThread = new Thread() { public void run() { while (true) { if (cache != null) { if (cache.getCacheStatus() == CacheStatus.STARTED) { if (cache.getRoot().hasChild(fqn)) { cache.put(fqn2, "k", "v"); break; } } } TestingUtil.sleepRandom(50); } } }; busyThread.setDaemon(true); busyThread.start(); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/eviction/minttl/LRUMinTTLTest.java0000644000175000017500000000105210674222353030762 0ustar twernertwernerpackage org.jboss.cache.eviction.minttl; import org.testng.annotations.Test; import org.jboss.cache.eviction.EvictionPolicyConfigBase; import org.jboss.cache.eviction.LRUConfiguration; /** * * @author Manik Surtani * @since 2.1.0 */ @Test(groups = {"functional"}) public class LRUMinTTLTest extends MinTTLTestBase { @Override protected EvictionPolicyConfigBase getEvictionPolicyConfig() { LRUConfiguration cfg = new LRUConfiguration(); cfg.setTimeToLiveSeconds(1); return cfg; } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/eviction/minttl/LFUMinTTLTest.java0000644000175000017500000000107110674222353030747 0ustar twernertwernerpackage org.jboss.cache.eviction.minttl; import org.testng.annotations.Test; import org.jboss.cache.eviction.EvictionPolicyConfigBase; import org.jboss.cache.eviction.LRUConfiguration; import org.jboss.cache.eviction.LFUConfiguration; /** * * @author Manik Surtani * @since 2.1.0 */ @Test(groups = {"functional"}) public class LFUMinTTLTest extends MinTTLTestBase { @Override protected EvictionPolicyConfigBase getEvictionPolicyConfig() { LFUConfiguration cfg = new LFUConfiguration(); return cfg; } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/eviction/minttl/MinTTLTestBase.java0000644000175000017500000000703111017455042031170 0ustar twernertwernerpackage org.jboss.cache.eviction.minttl; import org.jboss.cache.Cache; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.config.EvictionConfig; import org.jboss.cache.config.EvictionRegionConfig; import org.jboss.cache.eviction.EvictionPolicyConfigBase; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; /** * This test exercises the minimum time to live for any element in the cache * * @author Manik Surtani * @since 2.1.0 */ @Test(groups = {"functional"}) public abstract class MinTTLTestBase { // this should ideally be in an eviction test base class so all eviction policies can be tested protected Cache cache; protected Fqn region = Fqn.fromString("/test-region"); protected Fqn fqn = Fqn.fromRelativeElements(region, "a"); // allows the test methods to notify any support threads in subclasses that data is in the cache and the test is about to begin protected CountDownLatch cacheInitialisedLatch; protected abstract EvictionPolicyConfigBase getEvictionPolicyConfig(); @BeforeMethod public void setUp() { cacheInitialisedLatch = new CountDownLatch(1); // the LRU policy cfg EvictionPolicyConfigBase cfg = getEvictionPolicyConfig(); // the region configuration EvictionRegionConfig regionCfg = new EvictionRegionConfig(); regionCfg.setRegionFqn(region); regionCfg.setRegionName(region.toString()); regionCfg.setEvictionPolicyConfig(cfg); // set regions in a list List evictionRegionConfigs = new ArrayList(); evictionRegionConfigs.add(regionCfg); // cache-wide EvictionConfig ec = new EvictionConfig(); ec.setWakeupIntervalSeconds(1); ec.setEvictionRegionConfigs(evictionRegionConfigs); cache = new DefaultCacheFactory().createCache(false); cache.getConfiguration().setEvictionConfig(ec); } @AfterMethod public void tearDown() { cache.stop(); } public void testNoMinimumTTL() { cache.start(); cache.put(fqn, "k", "v"); // in case any waiting threads in subclasses are waiting for the cache to be initialised cacheInitialisedLatch.countDown(); assert cache.get(fqn, "k") != null : "Node should be in the cache"; TestingUtil.sleepThread(3000); assert cache.get(fqn, "k") == null : "Node should have been evicted"; } public void testWithMinimumTTL() { ((EvictionPolicyConfigBase) cache.getConfiguration().getEvictionConfig().getEvictionRegionConfigs().get(0).getEvictionPolicyConfig()).setMinTimeToLiveSeconds(3); cache.start(); cache.put(fqn, "k", "v"); // in case any waiting threads in subclasses are waiting for the cache to be initialised cacheInitialisedLatch.countDown(); assert cache.get(fqn, "k") != null : "Node should be in the cache"; TestingUtil.sleepThread(3000); assert cache.get(fqn, "k") != null : "Node should still be in cache due to a minTTL of 3 secs"; // the last cache.get() would have updated the last modified tstamp so we need to wait at least 3 secs (+1 sec maybe for the eviction thread) // to make sure this is evicted. TestingUtil.sleepThread(5000); assert cache.get(fqn, "k") == null : "Node should have been evicted"; } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/eviction/MRUPolicyTest.java0000644000175000017500000001566611017455042027617 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.eviction; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.factories.UnitTestCacheConfigurationFactory; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.EvictionConfig; import org.jboss.cache.config.EvictionRegionConfig; import org.jboss.cache.lock.IsolationLevel; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.List; import java.util.ArrayList; /** * Unit tests for MRUPolicy. * * @author Daniel Huang (dhuang@jboss.org) * @version $Revision: 5906 $ */ @Test(groups = {"functional"}) public class MRUPolicyTest { CacheSPI cache; int wakeupIntervalMillis = 0; final String ROOT_STR = "/test"; Throwable t1_ex, t2_ex; final long DURATION = 10000; boolean isTrue; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { initCaches(); wakeupIntervalMillis = cache.getConfiguration().getEvictionConfig().getWakeupIntervalSeconds() * 1000; log("wakeupInterval is " + wakeupIntervalMillis); if (wakeupIntervalMillis < 0) { fail("testEviction(): eviction thread wake up interval is illegal " + wakeupIntervalMillis); } t1_ex = t2_ex = null; isTrue = true; } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { cache.stop(); } private void initCaches() { Configuration config = UnitTestCacheConfigurationFactory.createConfiguration(Configuration.CacheMode.LOCAL, true); EvictionConfig evConfig = config.getEvictionConfig(); evConfig.setWakeupIntervalSeconds(3); evConfig.setDefaultEventQueueSize(200000); evConfig.setDefaultEvictionPolicyClass("org.jboss.cache.eviction.MRUPolicy"); List evictionRegionConfigs = new ArrayList(); evictionRegionConfigs.add(buildEvictionRegionConfig("_default_",100)); evictionRegionConfigs.add(buildEvictionRegionConfig("/org/jboss/test/data",6)); evConfig.setEvictionRegionConfigs(evictionRegionConfigs); config.setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); config.setIsolationLevel(IsolationLevel.SERIALIZABLE); cache = (CacheSPI)new DefaultCacheFactory().createCache(config); } private EvictionRegionConfig buildEvictionRegionConfig(String regionName, int maxNodes) { EvictionRegionConfig erc = new EvictionRegionConfig(); erc.setRegionName(regionName); MRUConfiguration mruConfiguration = new MRUConfiguration(); mruConfiguration.setMaxNodes(maxNodes); erc.setEvictionPolicyConfig(mruConfiguration); return erc; } public void testEviction() throws Exception { cache.put("/org/jboss/test/data/a", "/org/jboss/test/data/a", "/org/jboss/test/data/a"); cache.put("/org/jboss/test/data/b", "/org/jboss/test/data/b", "/org/jboss/test/data/b"); cache.put("/org/jboss/test/data/c", "/org/jboss/test/data/c", "/org/jboss/test/data/c"); cache.put("/org/jboss/test/data/d", "/org/jboss/test/data/d", "/org/jboss/test/data/d"); cache.put("/org/jboss/test/data/e", "/org/jboss/test/data/e", "/org/jboss/test/data/e"); TestingUtil.sleepThread(wakeupIntervalMillis + 500); cache.put("/org/jboss/test/data/f", "/org/jboss/test/data/f", "/org/jboss/test/data/f"); cache.put("/org/jboss/test/data/g", "/org/jboss/test/data/g", "/org/jboss/test/data/g"); cache.put("/org/jboss/test/data/h", "/org/jboss/test/data/h", "/org/jboss/test/data/h"); assertNotNull(cache.get("/org/jboss/test/data/a", "/org/jboss/test/data/a")); assertNotNull(cache.get("/org/jboss/test/data/b", "/org/jboss/test/data/b")); TestingUtil.sleepThread(wakeupIntervalMillis + 500); assertNull(cache.get("/org/jboss/test/data/a", "/org/jboss/test/data/a")); assertNull(cache.get("/org/jboss/test/data/b", "/org/jboss/test/data/b")); } public void testNodeRemoved() throws Exception { cache.put("/org/jboss/test/data/a", "/org/jboss/test/data/a", "/org/jboss/test/data/a"); cache.put("/org/jboss/test/data/b", "/org/jboss/test/data/b", "/org/jboss/test/data/b"); cache.put("/org/jboss/test/data/c", "/org/jboss/test/data/c", "/org/jboss/test/data/c"); cache.put("/org/jboss/test/data/d", "/org/jboss/test/data/d", "/org/jboss/test/data/d"); cache.put("/org/jboss/test/data/e", "/org/jboss/test/data/e", "/org/jboss/test/data/e"); TestingUtil.sleepThread(wakeupIntervalMillis + 500); cache.removeNode("/org/jboss/test/data/d"); cache.removeNode("/org/jboss/test/data/e"); cache.put("/org/jboss/test/data/f", "/org/jboss/test/data/f", "/org/jboss/test/data/f"); cache.put("/org/jboss/test/data/g", "/org/jboss/test/data/g", "/org/jboss/test/data/g"); TestingUtil.sleepThread(wakeupIntervalMillis + 500); assertNull(cache.get("/org/jboss/test/data/d", "/org/jboss/test/data/d")); assertNull(cache.get("/org/jboss/test/data/e", "/org/jboss/test/data/e")); assertNotNull(cache.get("/org/jboss/test/data/f", "/org/jboss/test/data/f")); assertNotNull(cache.get("/org/jboss/test/data/g", "/org/jboss/test/data/g")); } class MyPutter extends Thread { public MyPutter(String name) { super(name); } public void run() { int i = 0; final String myName = ROOT_STR + "/test1/node" + getName(); while (isTrue) { try { cache.put(myName + i++, "value", i); TestingUtil.sleepThread(1); } catch (Throwable e) { e.printStackTrace(); if (t1_ex == null) { t1_ex = e; } } } } } public void testConcurrentPutAndEvict() throws Exception { cache.stop(); cache.destroy(); cache.getConfiguration().setIsolationLevel(IsolationLevel.REPEATABLE_READ); cache.start(); cache.put(ROOT_STR + "/concurrentPutAndEvict", "value", 1); for (int i = 0; i < 10; i++) { new MyPutter("Putter" + i).start(); } int counter = 0; while (true) { counter++; if (t1_ex != null) { fail("Exception generated in put() " + t1_ex); } log("nodes/locks: " + cache.getNumberOfNodes() + "/" + cache.getNumberOfLocksHeld()); TestingUtil.sleepThread(1000); if (counter > 10) {// run for 10 seconds isTrue = false; break; } } } private void log(String s) { System.out.println(s); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/eviction/DummyEvictionConfiguration.java0000644000175000017500000000151311012441070032431 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.eviction; import org.jboss.cache.config.ConfigurationException; import org.jboss.cache.config.EvictionPolicyConfig; /** * @author Daniel Huang (dhuang@jboss.org) * @version $Revision: 5836 $ */ public class DummyEvictionConfiguration implements EvictionPolicyConfig, Cloneable { public String getEvictionPolicyClass() { return DummyEvictionPolicy.class.getName(); } public void validate() throws ConfigurationException { // no-op } public void reset() { // no-op } @Override public DummyEvictionConfiguration clone() throws CloneNotSupportedException { return (DummyEvictionConfiguration) super.clone(); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/eviction/FIFOAlgorithmTest.java0000644000175000017500000001020010665031725030347 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.eviction; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertTrue; import java.util.Iterator; import org.jboss.cache.Fqn; import org.jboss.cache.Region; import org.jboss.cache.RegionManager; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * Unit tests for FIFOAlgorithm. * * @author Daniel Huang (dhuang@jboss.org) * @version $Revision: 4444 $ */ @Test(groups = {"functional"}) public class FIFOAlgorithmTest { RegionManager regionManager; FIFOAlgorithm algo; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { algo = new FIFOAlgorithm(); FIFOConfiguration config = new FIFOConfiguration(); // We have to setCache maxNodes!! config.setMaxNodes(0); regionManager = new RegionManager(); config.setEvictionPolicyClass(DummyEvictionPolicy.class.getName()); regionManager.getRegion("/a/b", true).setEvictionPolicy(config); } public void testMaxNodes1() throws Exception { Region region = regionManager.getRegion("/a/b", true); FIFOConfiguration config = (FIFOConfiguration) region.getEvictionPolicyConfig(); config.setMaxNodes(5); for (int i = 0; i < 8; i++) { Fqn fqn = Fqn.fromString("/a/b/" + Integer.toString(i)); region.putNodeEvent(new EvictedEventNode(fqn, NodeEventType.ADD_NODE_EVENT)); } algo.process(region); FIFOQueue queue = (FIFOQueue) algo.evictionQueue; assertEquals(5, algo.getEvictionQueue().getNumberOfNodes()); // now verify the order. Iterator it = queue.iterate(); int index = 3; while (it.hasNext()) { NodeEntry ne = (NodeEntry) it.next(); String fqn = ne.getFqn().toString(); assertTrue(fqn.endsWith("/" + Integer.toString(index))); index++; } // now verify the same order still exists after visiting the nodes. for (int i = 3; i < 8; i++) { Fqn fqn = Fqn.fromString("/a/b/" + Integer.toString(i)); region.putNodeEvent(new EvictedEventNode(fqn, NodeEventType.VISIT_NODE_EVENT)); } for (int i = 3; i < 5; i++) { Fqn fqn = Fqn.fromString("/a/b/" + Integer.toString(i)); region.putNodeEvent(new EvictedEventNode(fqn, NodeEventType.VISIT_NODE_EVENT)); } algo.process(region); assertEquals(5, algo.getEvictionQueue().getNumberOfNodes()); it = queue.iterate(); index = 3; while (it.hasNext()) { NodeEntry ne = (NodeEntry) it.next(); String fqn = ne.getFqn().toString(); assertTrue(fqn.endsWith("/" + Integer.toString(index))); index++; } } public void testMaxNodes2() throws Exception { Region region = regionManager.getRegion("/a/b", true); FIFOConfiguration config = (FIFOConfiguration) region.getEvictionPolicyConfig(); config.setMaxNodes(50000); for (int i = 0; i < 50000; i++) { Fqn fqn = Fqn.fromString("/a/b/" + Integer.toString(i)); region.putNodeEvent(new EvictedEventNode(fqn, NodeEventType.ADD_NODE_EVENT)); } algo.process(region); FIFOQueue queue = (FIFOQueue) algo.evictionQueue; assertEquals(50000, algo.getEvictionQueue().getNumberOfNodes()); Iterator it = queue.iterate(); int index = 0; while (it.hasNext()) { NodeEntry ne = (NodeEntry) it.next(); assertTrue(ne.getFqn().toString().endsWith("/" + Integer.toString(index))); index++; } for (int i = 50000; i < 60000; i++) { Fqn fqn = Fqn.fromString("/a/b/" + Integer.toString(i)); region.putNodeEvent(new EvictedEventNode(fqn, NodeEventType.ADD_NODE_EVENT)); } algo.process(region); it = queue.iterate(); index = 10000; while (it.hasNext()) { NodeEntry ne = (NodeEntry) it.next(); assertTrue(ne.getFqn().toString().endsWith("/" + Integer.toString(index))); index++; } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/eviction/ElementSizeAlgorithmTest.java0000644000175000017500000000757310665031725032073 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.eviction; import static org.testng.AssertJUnit.assertEquals; import java.util.Iterator; import org.jboss.cache.Fqn; import org.jboss.cache.Region; import org.jboss.cache.RegionManager; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * @author Daniel Huang * @version $Revision: 4444 $ */ @Test(groups = {"functional"}) public class ElementSizeAlgorithmTest { RegionManager regionManager; ElementSizeAlgorithm algo; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { algo = new ElementSizeAlgorithm(); regionManager = new RegionManager(); ElementSizeConfiguration config = new ElementSizeConfiguration(); // We have to setCache maxElementsPerNode!! config.setMaxElementsPerNode(0); config.setEvictionPolicyClass(DummyEvictionPolicy.class.getName()); regionManager.getRegion("/a/b", true).setEvictionPolicy(config); } public void testMaxElements() throws Exception { Region region = regionManager.getRegion("/a/b", true); ElementSizeConfiguration config = (ElementSizeConfiguration) region.getEvictionPolicyConfig(); config.setMaxNodes(10); config.setMaxElementsPerNode(6); for (int i = 0; i < 10; i++) { Fqn fqn = Fqn.fromString("/a/b/" + Integer.toString(i)); region.putNodeEvent(new EvictedEventNode(fqn, NodeEventType.ADD_NODE_EVENT)); if (i % 2 == 0) { for (int k = 0; k < i; k++) { region.putNodeEvent(new EvictedEventNode(fqn, NodeEventType.ADD_ELEMENT_EVENT)); } } } algo.process(region); ElementSizeQueue queue = (ElementSizeQueue) algo.evictionQueue; assertEquals(9, algo.getEvictionQueue().getNumberOfNodes()); assertEquals(12, algo.getEvictionQueue().getNumberOfElements()); // now verify the order. Iterator it = queue.iterate(); int count = 6; while (it.hasNext()) { NodeEntry ne = (NodeEntry) it.next(); System.out.println(ne); if (count > 0) { assertEquals(count, ne.getNumberOfElements()); } else { assertEquals(0, ne.getNumberOfElements()); } count -= 2; } for (int i = 0; i < 7; i++) { region.putNodeEvent(new EvictedEventNode(Fqn.fromString("/a/b/9"), NodeEventType.ADD_ELEMENT_EVENT)); region.putNodeEvent(new EvictedEventNode(Fqn.fromString("/a/b/7"), NodeEventType.ADD_ELEMENT_EVENT)); } algo.process(region); assertEquals(7, queue.getNumberOfNodes()); } public void testMaxNodesAndMaxElements() throws Exception { Region region = regionManager.getRegion("/a/b", true); ElementSizeConfiguration config = (ElementSizeConfiguration) region.getEvictionPolicyConfig(); config.setMaxNodes(10); config.setMaxElementsPerNode(100); for (int i = 0; i < 20; i++) { Fqn fqn = Fqn.fromString("/a/b/" + Integer.toString(i)); region.putNodeEvent(new EvictedEventNode(fqn, NodeEventType.ADD_NODE_EVENT)); for (int k = 0; k < i; k++) { region.putNodeEvent(new EvictedEventNode(fqn, NodeEventType.ADD_ELEMENT_EVENT)); } } algo.process(region); ElementSizeQueue queue = (ElementSizeQueue) algo.evictionQueue; assertEquals(10, algo.getEvictionQueue().getNumberOfNodes()); assertEquals(45, algo.getEvictionQueue().getNumberOfElements()); // now verify the order. Iterator it = queue.iterate(); int num = 9; while (it.hasNext()) { NodeEntry ne = (NodeEntry) it.next(); assertEquals(num, ne.getNumberOfElements()); num--; } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/eviction/LFUConfigurationTest.java0000644000175000017500000000400310665031725031137 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.eviction; import static org.testng.AssertJUnit.assertEquals; import org.jboss.cache.factories.XmlConfigurationParser; import org.jboss.cache.xml.XmlHelper; import org.testng.annotations.Test; import org.w3c.dom.Element; /** * LFU Configuration test. * * @author Daniel Huang (dhuang@jboss.org) * @version $Revision: 4444 $ */ @Test(groups = {"functional"}) public class LFUConfigurationTest { public void testXMLParsing() throws Exception { LFUConfiguration config = new LFUConfiguration(); String xml = "" + "10" + "20" + ""; Element element = XmlHelper.stringToElement(xml); XmlConfigurationParser.parseEvictionPolicyConfig(element, config); assertEquals(10, config.getMinNodes()); assertEquals(20, config.getMaxNodes()); } public void testXMLParsing2() throws Exception { LFUConfiguration config = new LFUConfiguration(); String xml = "" + "10" + ""; Element element = XmlHelper.stringToElement(xml); XmlConfigurationParser.parseEvictionPolicyConfig(element, config); assertEquals(10, config.getMinNodes()); assertEquals(0, config.getMaxNodes()); } public void testXMLParsing3() throws Exception { LFUConfiguration config = new LFUConfiguration(); String xml = "" + "20" + ""; Element element = XmlHelper.stringToElement(xml); XmlConfigurationParser.parseEvictionPolicyConfig(element, config); assertEquals(0, config.getMinNodes()); assertEquals(20, config.getMaxNodes()); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/eviction/LRUAlgorithmTest.java0000755000175000017500000003422011017455042030273 0ustar twernertwernerpackage org.jboss.cache.eviction; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.Fqn; import org.jboss.cache.Region; import org.jboss.cache.RegionManager; import org.jboss.cache.util.TestingUtil; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertTrue; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * Unit tests for LRUAlgorithm. * * @author Ben Wang, Feb 11, 2004 * @author Daniel Huang (dhuang@jboss.org) */ @Test(groups = "functional") public class LRUAlgorithmTest { RegionManager regionManager; LRUAlgorithm algorithm; LRUConfiguration config; Log log = LogFactory.getLog(LRUAlgorithm.class); @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { algorithm = new LRUAlgorithm(); config = new LRUConfiguration(); config.setEvictionPolicyClass(DummyEvictionPolicy.class.getName()); // We have to setCache timeToLiveSeconds!! config.setTimeToLiveSeconds(0); regionManager = new RegionManager(); regionManager.getRegion("/a/b", true).setEvictionPolicy(config); } /** * maxNodes = 1. Eception is evictFromCacheNode. Should be commented for now. */ public void testEvictException() throws EvictionException { Fqn fqn1 = Fqn.fromString("/a/b/c"); Fqn fqn2 = Fqn.fromString("/a/b/d"); Fqn fqn3 = Fqn.fromString("/a/b/e"); Region region = regionManager.getRegion("/a/b", true); LRUConfiguration config = (LRUConfiguration) region.getEvictionPolicyConfig(); config.setMaxNodes(1); region.putNodeEvent(new EvictedEventNode(fqn1, NodeEventType.ADD_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn2, NodeEventType.ADD_NODE_EVENT)); algorithm.process(region); assertEquals("Queue size should be ", 1, algorithm.getEvictionQueue().getNumberOfNodes()); region.putNodeEvent(new EvictedEventNode(fqn2, NodeEventType.ADD_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn3, NodeEventType.ADD_NODE_EVENT)); algorithm.process(region); assertEquals("Queue size should be ", 1, algorithm.getEvictionQueue().getNumberOfNodes()); } /** * maxNodes = 0 case */ public void testMaxNode1() throws EvictionException { Fqn fqn1 = Fqn.fromString("/a/b/c"); Fqn fqn2 = Fqn.fromString("/a/b/d"); Region region = regionManager.getRegion("/a/b", true); LRUConfiguration config = (LRUConfiguration) region.getEvictionPolicyConfig(); config.setMaxNodes(0); region.putNodeEvent(new EvictedEventNode(fqn1, NodeEventType.ADD_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn2, NodeEventType.ADD_NODE_EVENT)); algorithm.process(region); assertEquals("Queue size should be ", 2, algorithm.getEvictionQueue().getNumberOfNodes()); } /** * maxNodes = 1 */ public void testMaxNode2() throws EvictionException { Fqn fqn1 = Fqn.fromString("/a/b/c"); Fqn fqn2 = Fqn.fromString("/a/b/d"); Fqn fqn3 = Fqn.fromString("/a/b/e"); Region region = regionManager.getRegion("/a/b", true); LRUConfiguration config = (LRUConfiguration) region.getEvictionPolicyConfig(); config.setMaxNodes(1); region.putNodeEvent(new EvictedEventNode(fqn1, NodeEventType.ADD_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn2, NodeEventType.ADD_NODE_EVENT)); algorithm.process(region); assertEquals("Queue size should be ", 1, algorithm.getEvictionQueue().getNumberOfNodes()); region.putNodeEvent(new EvictedEventNode(fqn2, NodeEventType.ADD_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn3, NodeEventType.ADD_NODE_EVENT)); algorithm.process(region); assertEquals("Queue size should be ", 1, algorithm.getEvictionQueue().getNumberOfNodes()); } /** * TimeToIdleSeconds = 0 */ public void testIdleTimeSeconds1() throws EvictionException { Fqn fqn1 = Fqn.fromString("/a/b/c"); Fqn fqn2 = Fqn.fromString("/a/b/d"); Region region = regionManager.getRegion("/a/b", true); LRUConfiguration config = (LRUConfiguration) region.getEvictionPolicyConfig(); config.setMaxNodes(0); config.setTimeToLiveSeconds(0); region.putNodeEvent(new EvictedEventNode(fqn1, NodeEventType.ADD_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn2, NodeEventType.ADD_NODE_EVENT)); TestingUtil.sleepThread(500); algorithm.process(region); assertEquals("Queue size should be ", 2, algorithm.getEvictionQueue().getNumberOfNodes()); } /** * TimeToIdleSeconds = 1 */ public void testIdleTimeSeconds2() throws EvictionException { Fqn fqn1 = Fqn.fromString("/a/b/c"); Fqn fqn2 = Fqn.fromString("/a/b/d"); Fqn fqn3 = Fqn.fromString("/a/b/e"); Region region = regionManager.getRegion("/a/b", true); LRUConfiguration config = (LRUConfiguration) region.getEvictionPolicyConfig(); config.setMaxNodes(0); config.setTimeToLiveSeconds(1); region.putNodeEvent(new EvictedEventNode(fqn1, NodeEventType.ADD_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn2, NodeEventType.ADD_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn3, NodeEventType.ADD_NODE_EVENT)); algorithm.process(region); assertEquals("Queue size #1: ", 3, algorithm.getEvictionQueue().getNumberOfNodes()); TestingUtil.sleepThread(1100); algorithm.process(region); assertEquals("Queue size #2: ", 0, algorithm.getEvictionQueue().getNumberOfNodes()); } /** * TimeToIdleSeconds = 1 with node visited in between. */ public void testIdleTimeSeconds3() throws EvictionException { Fqn fqn1 = Fqn.fromString("/a/b/c"); Fqn fqn2 = Fqn.fromString("/a/b/d"); Fqn fqn3 = Fqn.fromString("/a/b/e"); Region region = regionManager.getRegion("/a/b", true); LRUConfiguration config = (LRUConfiguration) region.getEvictionPolicyConfig(); config.setMaxNodes(0); config.setTimeToLiveSeconds(1); region.putNodeEvent(new EvictedEventNode(fqn1, NodeEventType.ADD_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn2, NodeEventType.ADD_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn3, NodeEventType.ADD_NODE_EVENT)); algorithm.process(region); assertEquals("Queue size #1: ", 3, algorithm.getEvictionQueue().getNumberOfNodes()); TestingUtil.sleepThread(1100); region.putNodeEvent(new EvictedEventNode(fqn2, NodeEventType.VISIT_NODE_EVENT)); algorithm.process(region); assertEquals("Queue size #2: ", 1, algorithm.getEvictionQueue().getNumberOfNodes()); } /** * MaxAgeSeconds = 1 with 3 nodes. * * @throws Exception */ public void testMaxAgeSeconds1() throws Exception { Fqn fqn1 = Fqn.fromString("/a/b/c"); Fqn fqn2 = Fqn.fromString("/a/b/d"); Fqn fqn3 = Fqn.fromString("/a/b/e"); Region region = regionManager.getRegion("/a/b", true); LRUConfiguration config = (LRUConfiguration) region.getEvictionPolicyConfig(); config.setMaxNodes(0); config.setTimeToLiveSeconds(0); config.setMaxAgeSeconds(1); region.putNodeEvent(new EvictedEventNode(fqn1, NodeEventType.ADD_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn2, NodeEventType.ADD_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn3, NodeEventType.ADD_NODE_EVENT)); algorithm.process(region); assertEquals("Queue size #1: ", 3, algorithm.getEvictionQueue().getNumberOfNodes()); TestingUtil.sleepThread(1100); algorithm.process(region); assertEquals("Queue size #2: ", 0, algorithm.getEvictionQueue().getNumberOfNodes()); } /** * MaxAgeSeconds = 2 with 3 nodes. * * @throws Exception */ public void testMaxAgeSeconds2() throws EvictionException { Fqn fqn1 = Fqn.fromString("/a/b/c"); Fqn fqn2 = Fqn.fromString("/a/b/d"); Fqn fqn3 = Fqn.fromString("/a/b/e"); Region region = regionManager.getRegion("/a/b", true); LRUConfiguration config = (LRUConfiguration) region.getEvictionPolicyConfig(); config.setMaxNodes(0); config.setTimeToLiveSeconds(0); config.setMaxAgeSeconds(1); region.putNodeEvent(new EvictedEventNode(fqn1, NodeEventType.ADD_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn2, NodeEventType.ADD_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn3, NodeEventType.ADD_NODE_EVENT)); algorithm.process(region); assertEquals("Queue size #1: ", 3, algorithm.getEvictionQueue().getNumberOfNodes()); TestingUtil.sleepThread(500); algorithm.process(region); assertEquals("Queue size #2: ", 3, algorithm.getEvictionQueue().getNumberOfNodes()); TestingUtil.sleepThread(600); algorithm.process(region); assertEquals("Queue size #3: ", 0, algorithm.getEvictionQueue().getNumberOfNodes()); } /** * Generic combo case. */ public void testCombo1() throws EvictionException { Fqn fqn1 = Fqn.fromString("/a/b/c"); Fqn fqn2 = Fqn.fromString("/a/b/d"); Fqn fqn3 = Fqn.fromString("/a/b/e"); Fqn fqn4 = Fqn.fromString("/a/b/f"); Region region = regionManager.getRegion("/a/b", true); LRUConfiguration config = (LRUConfiguration) region.getEvictionPolicyConfig(); // Should have a maximum of 2 nodes. config.setMaxNodes(2); config.setTimeToLiveSeconds(1); config.setMaxAgeSeconds(3); region.putNodeEvent(new EvictedEventNode(fqn1, NodeEventType.ADD_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn2, NodeEventType.ADD_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn4, NodeEventType.ADD_NODE_EVENT)); algorithm.process(region); EvictionQueue eq = algorithm.getEvictionQueue(); int numNodesInQueue = eq.getNumberOfNodes(); assert 2 == numNodesInQueue : "Queue size #1: expected 2 but was " + numNodesInQueue; // make sure all nodes now expire TestingUtil.sleepThread(1100); region.putNodeEvent(new EvictedEventNode(fqn3, NodeEventType.ADD_NODE_EVENT)); algorithm.process(region); numNodesInQueue = eq.getNumberOfNodes(); assert 1 == numNodesInQueue : "Queue size #2: expected 1 but was " + numNodesInQueue; TestingUtil.sleepThread(3100); // visit the node now to prevent the idle time from doing the pruning - node still gets pruned but by // max age. region.putNodeEvent(new EvictedEventNode(fqn3, NodeEventType.VISIT_NODE_EVENT)); algorithm.process(region); numNodesInQueue = eq.getNumberOfNodes(); assert 0 == numNodesInQueue : "Queue size #3: expected 0 but was " + numNodesInQueue; } /** * Generic combo case with newly added node should be around. */ public void testCombo2() throws EvictionException { Fqn fqn1 = Fqn.fromString("/a/b/c"); Fqn fqn2 = Fqn.fromString("/a/b/d"); Fqn fqn3 = Fqn.fromString("/a/b/e"); Region region = regionManager.getRegion("/a/b", true); LRUConfiguration config = (LRUConfiguration) region.getEvictionPolicyConfig(); config.setMaxNodes(2); config.setTimeToLiveSeconds(1); config.setMaxAgeSeconds(3); region.putNodeEvent(new EvictedEventNode(fqn1, NodeEventType.ADD_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn2, NodeEventType.ADD_NODE_EVENT)); region.putNodeEvent(new EvictedEventNode(fqn2, NodeEventType.REMOVE_NODE_EVENT)); algorithm.process(region); EvictionQueue eq = algorithm.getEvictionQueue(); int numNodesInQueue = eq.getNumberOfNodes(); assert 1 == numNodesInQueue : "Queue size #1: expected 1 but was " + numNodesInQueue; // make sure existing events all time out TestingUtil.sleepThread(1100); region.putNodeEvent(new EvictedEventNode(fqn3, NodeEventType.ADD_NODE_EVENT)); algorithm.process(region); numNodesInQueue = eq.getNumberOfNodes(); assert 1 == numNodesInQueue : "Queue size #2: expected 1 but was " + numNodesInQueue; TestingUtil.sleepThread(3100); region.putNodeEvent(new EvictedEventNode(fqn3, NodeEventType.VISIT_NODE_EVENT)); algorithm.process(region); numNodesInQueue = eq.getNumberOfNodes(); assert 0 == numNodesInQueue : "Queue size #3: expected 0 but was " + numNodesInQueue; } public void testEvictionSortOrder() throws EvictionException { Region region = regionManager.getRegion("/a/b", true); LRUConfiguration config = (LRUConfiguration) region.getEvictionPolicyConfig(); config.setMaxAgeSeconds(1000); config.setMaxNodes(0); config.setTimeToLiveSeconds(1000); for (int i = 0; i < 100; i++) { Fqn fqn = Fqn.fromString("/a/b/" + Integer.toString(i)); region.putNodeEvent(new EvictedEventNode(fqn, NodeEventType.ADD_NODE_EVENT)); } algorithm.process(region); for (int i = 0; i < 100; i++) { Fqn fqn = Fqn.fromString("/a/b/" + Integer.toString(i)); if (i % 2 == 0) { region.putNodeEvent(new EvictedEventNode(fqn, NodeEventType.VISIT_NODE_EVENT)); } } algorithm.process(region); LRUQueue queue = (LRUQueue) algorithm.getEvictionQueue(); NodeEntry ne; int count = 0; while ((ne = queue.getFirstLRUNodeEntry()) != null) { if (count < 50) { assertEquals(1, ne.getNumberOfNodeVisits()); } else { assertEquals(2, ne.getNumberOfNodeVisits()); } queue.removeNodeEntry(ne); count++; } for (int i = 0; i < 100; i++) { Fqn fqn = Fqn.fromString("/a/b/" + Integer.toString(i)); region.putNodeEvent(new EvictedEventNode(fqn, NodeEventType.ADD_NODE_EVENT)); } algorithm.process(region); long lastCreateTimestamp = 0; while ((ne = queue.getFirstMaxAgeNodeEntry()) != null) { assertTrue(ne.getCreationTimeStamp() >= lastCreateTimestamp); lastCreateTimestamp = ne.getCreationTimeStamp(); queue.removeNodeEntry(ne); } } void log(String msg) { System.out.println("-- " + msg); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/eviction/ExpirationPolicyTest.java0000755000175000017500000001046211017455042031266 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.eviction; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.Region; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.EvictionConfig; import org.jboss.cache.config.EvictionPolicyConfig; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * Unit tests for {@link ExpirationPolicy}. * * @author Elias Ross * @version $Revision: 5906 $ */ @Test(groups = {"functional"}) public class ExpirationPolicyTest { private static final Log log = LogFactory.getLog(ExpirationPolicyTest.class); private CacheSPI cache; Fqn fqn1 = Fqn.fromString("/node/1"); Fqn fqn2 = Fqn.fromString("/node/2"); Fqn fqn3 = Fqn.fromString("/node/3"); Fqn fqn4 = Fqn.fromString("/node/4"); Long future; Long past; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { Configuration conf = new Configuration(); EvictionConfig econf = new EvictionConfig(ExpirationPolicy.class.getName()); econf.setWakeupIntervalSeconds(1); conf.setEvictionConfig(econf); cache = (CacheSPI) new DefaultCacheFactory().createCache(conf, false); cache.start(); future = System.currentTimeMillis() + 4000; past = System.currentTimeMillis() - 2000; } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { cache.stop(); } public void testEviction() throws Exception { cache.put(fqn1, ExpirationConfiguration.EXPIRATION_KEY, future); cache.put(fqn2, ExpirationConfiguration.EXPIRATION_KEY, past); cache.put(fqn3, ExpirationConfiguration.EXPIRATION_KEY, future); cache.put(fqn4, "foo", "bar"); TestingUtil.sleepThread(2000); assertNotNull(cache.getNode(fqn1)); assertNull(cache.getNode(fqn2)); assertNotNull(cache.getNode(fqn3)); assertNotNull(cache.getNode(fqn4)); log.info("should remove 1 and 3 now"); TestingUtil.sleepThread(3000); assertNull(cache.getNode(fqn1)); assertNull(cache.getNode(fqn3)); } public void testUpdate() throws Exception { log.info("update 1 from future to past"); cache.put(fqn1, ExpirationConfiguration.EXPIRATION_KEY, future); assertNotNull(cache.getNode(fqn1)); TestingUtil.sleepThread(1500); cache.put(fqn1, ExpirationConfiguration.EXPIRATION_KEY, past); TestingUtil.sleepThread(1500); assertNull(cache.getNode(fqn1)); cache.removeNode(Fqn.ROOT); } public void testUpdateToFuture() throws Exception { log.info("update 1 from future to past"); Long future2 = future + 2000; cache.put(fqn1, ExpirationConfiguration.EXPIRATION_KEY, future); TestingUtil.sleepThread(2000); assertNotNull(cache.getNode(fqn1)); cache.put(fqn1, ExpirationConfiguration.EXPIRATION_KEY, future2); TestingUtil.sleepThread(3000); assertNotNull(cache.getNode(fqn1)); TestingUtil.sleepThread(3000); assertNull(cache.getNode(fqn1)); cache.removeNode(Fqn.ROOT); } public void testMaxNodes() throws Exception { log.info("set max nodes to 2, expire soonest to expire first"); EvictionPolicyConfig epc = cache.getRegionManager().getAllRegions(Region.Type.EVICTION).get(0).getEvictionPolicyConfig(); ExpirationConfiguration ec = (ExpirationConfiguration) epc; ec.setMaxNodes(2); Long future2 = future + 500; cache.put(fqn1, ExpirationConfiguration.EXPIRATION_KEY, future2); cache.put(fqn2, ExpirationConfiguration.EXPIRATION_KEY, future2); cache.put(fqn3, ExpirationConfiguration.EXPIRATION_KEY, future); cache.put(fqn4, ExpirationConfiguration.EXPIRATION_KEY, past); assertEquals(5, cache.getNumberOfNodes()); Thread.sleep(2000); assertNotNull(cache.getNode(fqn1)); assertNotNull(cache.getNode(fqn2)); assertNull(cache.getNode(fqn3)); assertNull(cache.getNode(fqn4)); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/eviction/FIFOConfigurationTest.java0000644000175000017500000000426610665031725031247 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.eviction; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertTrue; import static org.testng.AssertJUnit.fail; import org.jboss.cache.config.ConfigurationException; import org.jboss.cache.factories.XmlConfigurationParser; import org.jboss.cache.xml.XmlHelper; import org.testng.annotations.Test; import org.w3c.dom.Element; /** * Unit test for FIFOConfiguration. * * @author Daniel Huang (dhuang@jboss.org) * @version $Revision: 4444 $ */ @Test(groups = {"functional"}) public class FIFOConfigurationTest { public void testXMLParse() throws Exception { FIFOConfiguration config = new FIFOConfiguration(); String xml = "" + "1000" + ""; Element element = XmlHelper.stringToElement(xml); XmlConfigurationParser.parseEvictionPolicyConfig(element, config); assertEquals(1000, config.getMaxNodes()); } public void testXMLParse2() throws Exception { FIFOConfiguration config = new FIFOConfiguration(); String xml = "" + ""; Element element = XmlHelper.stringToElement(xml); try { XmlConfigurationParser.parseEvictionPolicyConfig(element, config); } catch (ConfigurationException ce) { assertTrue("Configure Exception properly thrown", true); return; } fail("Invalid region FIFO configuration did not cause ConfigureException to be thrown"); } public void testXMLParse3() throws Exception { FIFOConfiguration config = new FIFOConfiguration(); String xml = "" + "1000" + ""; Element element = XmlHelper.stringToElement(xml); try { XmlConfigurationParser.parseEvictionPolicyConfig(element, config); } catch (ConfigurationException ce) { assertTrue("Configure Exception properly thrown", true); } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/options/0000755000175000017500000000000011376173751024142 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/options/TestVersion.java0000644000175000017500000000260311012441070027246 0ustar twernertwernerpackage org.jboss.cache.options; import org.jboss.cache.optimistic.DataVersion; /** * Note that this uses STRING comparisons!! */ public class TestVersion implements DataVersion { private static final long serialVersionUID = -5577530957664493161L; private String myVersion; public TestVersion(String version) { myVersion = version; } public String getInternalVersion() { return myVersion; } public void setInternalVersion(String version) { myVersion = version; } public boolean newerThan(DataVersion other) { if (other instanceof TestVersion) { return myVersion.compareTo(((TestVersion) other).getInternalVersion()) > 0; } else { throw new IllegalArgumentException("version type mismatch"); } } @Override public String toString() { return "TestVersion-" + myVersion; } @Override public boolean equals(Object other) { if (other instanceof TestVersion) { TestVersion oVersion = (TestVersion) other; if (oVersion.myVersion == null && myVersion == null) return true; if (myVersion != null) return myVersion.equals(oVersion.myVersion); } return false; } @Override public int hashCode() { return myVersion.hashCode(); } }jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/options/ExplicitVersionsTest.java0000644000175000017500000001632610732752176031166 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.options; import org.jboss.cache.CacheFactory; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.Node; import org.jboss.cache.NodeSPI; import org.jboss.cache.config.Configuration; import org.jboss.cache.optimistic.DataVersion; import org.jboss.cache.optimistic.DefaultDataVersion; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.TransactionManager; /** * Tests the passing in of explicit {@see DataVersion} instances when using optimistic locking. * * @author Manik Surtani (manik@jboss.org) */ @Test(groups = {"functional"}) public class ExplicitVersionsTest { private CacheSPI cache; private Fqn fqn = Fqn.fromString("/a"); private String key = "key"; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { if (cache != null) tearDown(); CacheFactory instance = new DefaultCacheFactory(); cache = (CacheSPI) instance.createCache(false); cache.getConfiguration().setCacheMode(Configuration.CacheMode.LOCAL); cache.getConfiguration().setNodeLockingScheme("OPTIMISTIC"); cache.getConfiguration().setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); cache.start(); } @AfterMethod(alwaysRun = true) public void tearDown() { if (cache != null) { cache.stop(); cache = null; } } public void testSimplePut() throws Exception { DataVersion version = new TestVersion("99"); cache.getInvocationContext().getOptionOverrides().setDataVersion(version); cache.put(fqn, key, "value"); //now retrieve the data from the cache. assertEquals("value", cache.get(fqn, key)); // get a hold of the node NodeSPI node = (NodeSPI) cache.getNode(fqn); DataVersion versionFromCache = node.getVersion(); assertEquals(TestVersion.class, versionFromCache.getClass()); assertEquals("99", ((TestVersion) versionFromCache).getInternalVersion()); } public void testFailingPut() throws Exception { DataVersion version = new TestVersion("99"); cache.getInvocationContext().getOptionOverrides().setDataVersion(version); cache.put(fqn, key, "value"); version = new TestVersion("25"); cache.getInvocationContext().getOptionOverrides().setDataVersion(version); TransactionManager mgr = cache.getTransactionManager(); mgr.begin(); cache.put(fqn, key, "value2"); try { mgr.commit(); assertTrue("expected to fail", false); } catch (Exception e) { // should fail. assertTrue("expected to fail", true); } } public void testIncompatibleVersionTypes() throws Exception { DataVersion version = new TestVersion("99"); cache.getInvocationContext().getOptionOverrides().setDataVersion(version); cache.put(fqn, key, "value"); TransactionManager mgr = cache.getTransactionManager(); mgr.begin(); cache.getInvocationContext().getOptionOverrides().setDataVersion(new DefaultDataVersion(777)); cache.put(fqn, key, "value2"); try { // this call will use implicit versioning and will hence fail. mgr.commit(); assertTrue("expected to fail", false); } catch (Exception e) { // should fail. assertTrue("expected to fail", true); } } public void testExplicitVersionOnLeaf() throws Exception { cache.put("/org/domain/Entity", null); assertEquals(1, ((DefaultDataVersion) ((NodeSPI) cache.getNode("/org/domain/Entity")).getVersion()).getRawVersion()); TestVersion v = new TestVersion("Arse"); cache.getInvocationContext().getOptionOverrides().setDataVersion(v); cache.put(Fqn.fromString("/org/domain/Entity/EntityInstance#1"), "k", "v"); assertEquals(1, ((DefaultDataVersion) ((NodeSPI) cache.getNode("/org/domain/Entity")).getVersion()).getRawVersion()); assertEquals(v, ((NodeSPI) cache.getNode("/org/domain/Entity/EntityInstance#1")).getVersion()); } public void testExplicitVersionOnLeafImplicitParentCreation() throws Exception { TestVersion v = new TestVersion("Arse"); cache.getInvocationContext().getOptionOverrides().setDataVersion(v); cache.put(Fqn.fromString("/org/domain/Entity/EntityInstance#1"), "k", "v"); assertEquals(0, ((DefaultDataVersion) ((NodeSPI) cache.getNode("/org/domain/Entity")).getVersion()).getRawVersion()); assertEquals(v, ((NodeSPI) cache.getNode("/org/domain/Entity/EntityInstance#1")).getVersion()); } public void testExplicitVersionsOnParents() { Node root = cache.getRoot(); TestVersion lev2V = new TestVersion("Lev2-v"); cache.getInvocationContext().getOptionOverrides().setDataVersion(lev2V); root.addChild(Fqn.fromString("LEV2")); NodeSPI lev2 = (NodeSPI) root.getChild(Fqn.fromString("LEV2")); assertNotNull(lev2); assertEquals(lev2V, lev2.getVersion()); TestVersion lev3V = new TestVersion("Lev3-v"); cache.getInvocationContext().getOptionOverrides().setDataVersion(lev3V); lev2.addChild(Fqn.fromString("LEV3")); NodeSPI lev3 = (NodeSPI) lev2.getChild(Fqn.fromString("LEV3")); assertNotNull(lev3); assertEquals(lev3V, lev3.getVersion()); TestVersion lev4V = new TestVersion("Lev4-v"); cache.getInvocationContext().getOptionOverrides().setDataVersion(lev4V); lev3.addChild(Fqn.fromString("LEV4")); NodeSPI lev4 = (NodeSPI) lev3.getChild(Fqn.fromString("LEV4")); assertNotNull(lev4); assertEquals(lev4V, lev4.getVersion()); } public void testExplicitVersionOnParentAndChild() throws Exception { TestVersion vParent = new TestVersion("Parent-Version"); cache.getTransactionManager().begin(); cache.getInvocationContext().getOptionOverrides().setDataVersion(vParent); cache.put(Fqn.fromString("/parent"), "k", "v"); cache.getTransactionManager().commit(); assertEquals(0, ((DefaultDataVersion) ((NodeSPI) cache.getNode("/")).getVersion()).getRawVersion()); assertEquals(vParent, ((NodeSPI) cache.getNode("/parent")).getVersion()); TestVersion vChild = new TestVersion("Child-Version"); cache.getTransactionManager().begin(); cache.getInvocationContext().getOptionOverrides().setDataVersion(vChild); cache.put(Fqn.fromString("/parent/child"), "k", "v"); cache.getTransactionManager().commit(); assertEquals(0, ((DefaultDataVersion) ((NodeSPI) cache.getNode("/")).getVersion()).getRawVersion()); assertEquals(vParent, ((NodeSPI) cache.getNode("/parent")).getVersion()); assertEquals(vChild, ((NodeSPI) cache.getNode("/parent/child")).getVersion()); } }jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/options/CacheModeLocalSimpleTest.java0000644000175000017500000000612411017455042031571 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.options; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Option; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertNull; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.TransactionManager; /** * @author Manik Surtani (manik@jboss.org) */ @Test(groups = {"functional"}) public class CacheModeLocalSimpleTest { private CacheSPI cache1, cache2; private Option cacheModeLocal; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { cache1 = (CacheSPI) new DefaultCacheFactory().createCache(false); Configuration c = cache1.getConfiguration(); c.setCacheMode("REPL_SYNC"); c.setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); cache2 = (CacheSPI) new DefaultCacheFactory().createCache(false); c = cache2.getConfiguration(); c.setCacheMode("REPL_SYNC"); c.setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); cacheModeLocal = new Option(); cacheModeLocal.setCacheModeLocal(true); } @AfterMethod(alwaysRun = true) public void tearDown() { TestingUtil.killCaches(cache1, cache2); } public void testCacheModeLocalWithTx() throws Exception { doTest(false); } public void testCacheModeLocalOptimisticWithTx() throws Exception { doTest(true); } private void doTest(boolean optimistic) throws Exception { if (optimistic) { cache1.getConfiguration().setNodeLockingScheme(Configuration.NodeLockingScheme.OPTIMISTIC); cache2.getConfiguration().setNodeLockingScheme(Configuration.NodeLockingScheme.OPTIMISTIC); cache1.getConfiguration().setSyncCommitPhase(true); cache1.getConfiguration().setSyncRollbackPhase(true); cache2.getConfiguration().setSyncCommitPhase(true); cache2.getConfiguration().setSyncRollbackPhase(true); } cache1.start(); cache2.start(); TestingUtil.blockUntilViewsReceived(10000, cache1, cache2); TransactionManager mgr = cache1.getTransactionManager(); mgr.begin(); cache1.put(Fqn.fromString("/replicate"), "k", "v"); cache1.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); cache1.put(Fqn.fromString("/not-replicate"), "k", "v"); mgr.commit(); Thread.sleep(3000); assertEquals("v", cache1.get("/replicate", "k")); assertEquals("v", cache1.get("/not-replicate", "k")); assertEquals("v", cache2.get("/replicate", "k")); assertNull(cache2.get("/not-replicate", "k")); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/options/cachemodelocal/0000755000175000017500000000000011376173750027064 5ustar twernertwerner././@LongLink0000000000000000000000000000015200000000000011563 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/options/cachemodelocal/CacheModeLocalTestBase.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/options/cachemodelocal/CacheModeLocalTestBase0000644000175000017500000005516411012534035033220 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.options.cachemodelocal; import org.jboss.cache.CacheFactory; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.Node; import org.jboss.cache.config.Configuration; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.SystemException; import javax.transaction.TransactionManager; import java.util.HashMap; import java.util.Map; /** * Tests the cache mode local override in various scenarios. To be subclassed to test REPL_SYNC, REPL_ASYNC, INVALIDATION_SYNC, INVALIDATION_ASYNC for Opt and Pess locking. *

* Option.setCacheModeLocal() only applies to put() and remove() methods. * * @author Manik Surtani (manik@jboss.org) */ @Test(groups = {"functional"}) public abstract class CacheModeLocalTestBase { // to be subclassed. protected Configuration.CacheMode cacheMode; protected String nodeLockingScheme; /** * set this to true if the implementing class plans to use an invalidating cache mode * */ protected boolean isInvalidation; private CacheSPI cache1; private CacheSPI cache2; private Fqn fqn = Fqn.fromString("/a"); private String key = "key"; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { // force a tear down if the test runner didn't run one before (happens in IDEA) if (cache1 != null || cache2 != null) tearDown(); CacheFactory instance = new DefaultCacheFactory(); cache1 = (CacheSPI) instance.createCache(false); cache1.getConfiguration().setClusterName("test"); cache1.getConfiguration().setStateRetrievalTimeout(1000); cache1.getConfiguration().setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); cache1.getConfiguration().setNodeLockingScheme(nodeLockingScheme); cache1.getConfiguration().setCacheMode(cacheMode); cache1.start(); cache2 = (CacheSPI) instance.createCache(false); cache2.getConfiguration().setClusterName("test"); cache2.getConfiguration().setStateRetrievalTimeout(1000); cache2.getConfiguration().setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); cache2.getConfiguration().setNodeLockingScheme(nodeLockingScheme); cache2.getConfiguration().setCacheMode(cacheMode); cache2.start(); } @AfterMethod(alwaysRun = true) public void tearDown() { if (cache1 != null) { cache1.stop(); flushTxs(cache1.getTransactionManager()); cache1 = null; } if (cache2 != null) { cache2.stop(); flushTxs(cache2.getTransactionManager()); cache2 = null; } } private void flushTxs(TransactionManager mgr) { if (mgr != null) { try { if (mgr.getTransaction() != null) { mgr.rollback(); } } catch (SystemException e) { // do nothing } } } public void testPutKeyValue() throws Exception { cache1.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); cache1.put(fqn, key, "value"); delay(); // cache1 should still have this assertEquals("value", cache1.get(fqn, key)); // cache 2 should not assertNull("Should be null", cache2.get(fqn, key)); // now try again with passing the default options cache1.getInvocationContext().getOptionOverrides().reset(); cache1.put(fqn, key, "value"); delay(); // cache1 should still have this assertEquals("value", cache1.get(fqn, key)); // cache 2 should as well if (!isInvalidation) { assertEquals("value", cache2.get(fqn, key)); } else { assertNull("should be invalidated", cache2.get(fqn, key)); } // now cache2 cache2.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); cache2.put(fqn, key, "value2"); delay(); assertEquals("value2", cache2.get(fqn, key)); assertEquals("value", cache1.get(fqn, key)); cache2.getInvocationContext().getOptionOverrides().reset(); cache2.put(fqn, key, "value2"); delay(); assertEquals("value2", cache2.get(fqn, key)); if (!isInvalidation) { assertEquals("value2", cache1.get(fqn, key)); } else { assertNull("should be invalidated", cache1.get(fqn, key)); } } public void testPutKeyValueViaNodeAPI() throws Exception { Node node1 = cache1.getRoot().addChild(fqn); cache1.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); node1.put(key, "value"); delay(); // cache1 should still have this assertEquals("value", cache1.get(fqn, key)); // cache 2 should not assertNull("Should be null", cache2.get(fqn, key)); // now try again with passing the default options cache1.getInvocationContext().getOptionOverrides().reset(); node1.put(key, "value"); delay(); // cache1 should still have this assertEquals("value", cache1.get(fqn, key)); // cache 2 should as well if (!isInvalidation) { assertEquals("value", cache2.get(fqn, key)); } else { assertNull("should be invalidated", cache2.get(fqn, key)); } // now cache2 cache2.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); Node node2 = cache2.getRoot().addChild(fqn); cache2.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); node2.put(key, "value2"); delay(); assertEquals("value2", cache2.get(fqn, key)); assertEquals("value", cache1.get(fqn, key)); cache2.getInvocationContext().getOptionOverrides().reset(); node2.put(key, "value2"); delay(); assertEquals("value2", cache2.get(fqn, key)); if (!isInvalidation) { assertEquals("value2", cache1.get(fqn, key)); } else { assertNull("should be invalidated", cache1.get(fqn, key)); } } public void testPutData() throws Exception { Map map = new HashMap(); map.put(key, "value"); cache1.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); cache1.put(fqn, map); delay(); // cache1 should still have this assertEquals("value", cache1.get(fqn, key)); // cache 2 should not assertNull("Should be null", cache2.get(fqn, key)); // now try again with passing the default options cache1.getInvocationContext().getOptionOverrides().setCacheModeLocal(false); cache1.put(fqn, map); delay(); // cache1 should still have this assertEquals("value", cache1.get(fqn, key)); // cache 2 should as well if (!isInvalidation) { assertEquals("value", cache2.get(fqn, key)); } else { assertNull("should be invalidated", cache2.get(fqn, key)); } // now cache2 map.put(key, "value2"); cache2.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); cache2.put(fqn, map); delay(); assertEquals("value2", cache2.get(fqn, key)); assertEquals("value", cache1.get(fqn, key)); cache2.getInvocationContext().getOptionOverrides().reset(); cache2.put(fqn, key, "value2"); delay(); assertEquals("value2", cache2.get(fqn, key)); if (!isInvalidation) { assertEquals("value2", cache1.get(fqn, key)); } else { assertNull("should be invalidated", cache1.get(fqn, key)); } } public void testPutDataViaNodeAPI() throws Exception { Map map = new HashMap(); map.put(key, "value"); Node node1 = cache1.getRoot().addChild(fqn); cache1.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); node1.putAll(map); delay(); // cache1 should still have this assertEquals("value", cache1.get(fqn, key)); // cache 2 should not assertNull("Should be null", cache2.get(fqn, key)); // now try again with passing the default options cache1.getInvocationContext().getOptionOverrides().setCacheModeLocal(false); node1.putAll(map); delay(); // cache1 should still have this assertEquals("value", cache1.get(fqn, key)); // cache 2 should as well if (!isInvalidation) { assertEquals("value", cache2.get(fqn, key)); } else { assertNull("should be invalidated", cache2.get(fqn, key)); } // now cache2 cache2.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); Node node2 = cache2.getRoot().addChild(fqn); map.put(key, "value2"); cache2.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); node2.putAll(map); delay(); assertEquals("value2", cache2.get(fqn, key)); assertEquals("value", cache1.get(fqn, key)); cache2.getInvocationContext().getOptionOverrides().reset(); node2.put(key, "value2"); delay(); assertEquals("value2", cache2.get(fqn, key)); if (!isInvalidation) { assertEquals("value2", cache1.get(fqn, key)); } else { assertNull("should be invalidated", cache1.get(fqn, key)); } } public void testRemoveNode() throws Exception { // put some stuff in the cache first // make sure we cleanup thread local vars. cache1.getInvocationContext().setOptionOverrides(null); cache1.put(fqn, key, "value"); delay(); assertEquals("value", cache1.get(fqn, key)); if (isInvalidation) { assertNull("Should be null", cache2.get(fqn, key)); } else { assertEquals("value", cache2.get(fqn, key)); } cache1.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); cache1.removeNode(fqn); delay(); // should be removed in cache1 assertNull("should be null", cache1.get(fqn, key)); // Not in cache2 if (isInvalidation) { assertNull("Should be null", cache2.get(fqn, key)); } else { assertEquals("value", cache2.get(fqn, key)); } // replace cache entries cache1.put(fqn, key, "value"); delay(); assertEquals("value", cache1.get(fqn, key)); if (isInvalidation) { assertNull("Should be null", cache2.get(fqn, key)); } else { assertEquals("value", cache2.get(fqn, key)); } // now try again with passing the default options cache1.getInvocationContext().getOptionOverrides().setCacheModeLocal(false); cache1.removeNode(fqn); delay(); // both should be null assertNull("should be null", cache1.get(fqn, key)); assertNull("should be null", cache2.get(fqn, key)); } public void testRemoveNodeViaNodeAPI() throws Exception { // put some stuff in the cache first // make sure we cleanup thread local vars. cache1.getInvocationContext().setOptionOverrides(null); cache1.put(fqn, key, "value"); delay(); assertEquals("value", cache1.get(fqn, key)); if (isInvalidation) { assertNull("Should be null", cache2.get(fqn, key)); } else { assertEquals("value", cache2.get(fqn, key)); } cache1.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); cache1.getRoot().removeChild(fqn); delay(); // should be removed in cache1 assertNull("should be null", cache1.get(fqn, key)); // Not in cache2 if (isInvalidation) { assertNull("Should be null", cache2.get(fqn, key)); } else { assertEquals("value", cache2.get(fqn, key)); } // replace cache entries cache1.put(fqn, key, "value"); delay(); assertEquals("value", cache1.get(fqn, key)); if (isInvalidation) { assertNull("Should be null", cache2.get(fqn, key)); } else { assertEquals("value", cache2.get(fqn, key)); } // now try again with passing the default options cache1.getInvocationContext().getOptionOverrides().setCacheModeLocal(false); cache1.getRoot().removeChild(fqn); delay(); // both should be null assertNull("should be null", cache1.get(fqn, key)); assertNull("should be null", cache2.get(fqn, key)); } public void testRemoveKey() throws Exception { // put some stuff in the cache first cache1.getInvocationContext().setOptionOverrides(null); cache1.put(fqn, key, "value"); delay(); assertEquals("value", cache1.get(fqn, key)); if (isInvalidation) { assertNull("Should be null", cache2.get(fqn, key)); } else { assertEquals("value", cache2.get(fqn, key)); } cache1.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); cache1.remove(fqn, key); delay(); // should be removed in cache1 assertNull("should be null", cache1.get(fqn, key)); // Not in cache2 if (isInvalidation) { assertNull("Should be null", cache2.get(fqn, key)); } else { assertEquals("value", cache2.get(fqn, key)); } // replace cache entries cache1.put(fqn, key, "value"); delay(); assertEquals("value", cache1.get(fqn, key)); if (isInvalidation) { assertNull("Should be null", cache2.get(fqn, key)); } else { assertEquals("value", cache2.get(fqn, key)); } // now try again with passing the default options cache1.getInvocationContext().getOptionOverrides().reset(); cache1.remove(fqn, key); delay(); // both should be null assertNull("should be null", cache1.get(fqn, key)); assertNull("should be null", cache2.get(fqn, key)); } public void testRemoveKeyViaNodeAPI() throws Exception { // put some stuff in the cache first Node node1 = cache1.getRoot().addChild(fqn); cache1.getInvocationContext().setOptionOverrides(null); node1.put(key, "value"); delay(); assertEquals("value", cache1.get(fqn, key)); if (isInvalidation) { assertNull("Should be null", cache2.get(fqn, key)); } else { assertEquals("value", cache2.get(fqn, key)); } cache1.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); node1.remove(key); delay(); // should be removed in cache1 assertNull("should be null", cache1.get(fqn, key)); // Not in cache2 if (isInvalidation) { assertNull("Should be null", cache2.get(fqn, key)); } else { assertEquals("value", cache2.get(fqn, key)); } // replace cache entries node1.put(key, "value"); delay(); assertEquals("value", cache1.get(fqn, key)); if (isInvalidation) { assertNull("Should be null", cache2.get(fqn, key)); } else { assertEquals("value", cache2.get(fqn, key)); } // now try again with passing the default options cache1.getInvocationContext().getOptionOverrides().reset(); node1.remove(key); delay(); // both should be null assertNull("should be null", cache1.get(fqn, key)); assertNull("should be null", cache2.get(fqn, key)); } public void testTransactionalBehaviourCommit() throws Exception { TransactionManager mgr = cache1.getTransactionManager(); mgr.begin(); cache1.getInvocationContext().getOptionOverrides().reset(); cache1.put(fqn, key, "value1"); cache1.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); cache1.put(fqn, key, "value2"); mgr.commit(); delay(); // cache1 should still have this assertEquals("value2", cache1.get(fqn, key)); if (!isInvalidation) { assertEquals("value1", cache2.get(fqn, key)); } else { assertNull(cache2.get(fqn, key)); } // now try again with passing the default options mgr.begin(); cache1.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); cache1.put(fqn, key, "value3"); cache1.getInvocationContext().getOptionOverrides().reset(); cache1.put(fqn, key, "value"); mgr.commit(); delay(); // cache1 should still have this assertEquals("value", cache1.get(fqn, key)); // cache 2 should as well if (!isInvalidation) { assertEquals("value", cache2.get(fqn, key)); } else { assertNull("should be invalidated", cache2.get(fqn, key)); } // now cache2 mgr = cache2.getTransactionManager(); mgr.begin(); cache2.getInvocationContext().getOptionOverrides().setCacheModeLocal(false); cache2.put(fqn, key, "value3"); cache2.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); cache2.put(fqn, key, "value2"); mgr.commit(); delay(); assertEquals("value2", cache2.get(fqn, key)); if (!isInvalidation) { assertEquals("value3", cache1.get(fqn, key)); } else { assertNull(cache1.get(fqn, key)); } mgr.begin(); cache2.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); cache2.put(fqn, key, "value2"); cache2.getInvocationContext().getOptionOverrides().reset(); cache2.put(fqn, key, "value4"); mgr.commit(); delay(); assertEquals("value4", cache2.get(fqn, key)); if (!isInvalidation) { assertEquals("value4", cache1.get(fqn, key)); } else { assertNull("should be invalidated", cache1.get(fqn, key)); } } public void testTransactionalBehaviourRollback() throws Exception { TransactionManager mgr = cache1.getTransactionManager(); // create these first ... cache1.put("/a", key, "old"); cache1.put("/b", key, "old"); delay(); mgr.begin(); cache1.getInvocationContext().getOptionOverrides().reset(); cache1.put("/a", key, "value1"); cache1.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); cache1.put("/b", key, "value2"); mgr.rollback(); delay(); // cache1 should NOT have this assert cache1.get("/a", key).equals("old"); assert cache1.get("/b", key).equals("old"); if (isInvalidation) { assert cache2.get("/a", key) == null; assert cache2.get("/b", key) == null; } else { assert cache2.get("/a", key).equals("old"); assert cache2.get("/b", key).equals("old"); } } public void testTransactionalBehaviourViaNodeAPI() throws Exception { Node node1 = cache1.getRoot().addChild(fqn); TransactionManager mgr = cache1.getTransactionManager(); mgr.begin(); cache1.getInvocationContext().getOptionOverrides().reset(); node1.put(key, "value1"); cache1.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); node1.put(key, "value2"); mgr.commit(); delay(); // cache1 should still have this assertEquals("value2", cache1.get(fqn, key)); if (!isInvalidation) { assertEquals("value1", cache2.get(fqn, key)); } else { assertNull(cache2.get(fqn, key)); } // now try again with passing the default options mgr.begin(); cache1.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); node1.put(key, "value3"); cache1.getInvocationContext().getOptionOverrides().reset(); node1.put(key, "value"); mgr.commit(); delay(); // cache1 should still have this assertEquals("value", cache1.get(fqn, key)); // cache 2 should as well if (!isInvalidation) { assertEquals("value", cache2.get(fqn, key)); } else { assertNull("should be invalidated", cache2.get(fqn, key)); } // now cache2 Node node2 = cache2.getRoot().addChild(fqn); mgr = cache2.getTransactionManager(); mgr.begin(); cache2.getInvocationContext().getOptionOverrides().setCacheModeLocal(false); node2.put(key, "value3"); cache2.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); node2.put(key, "value2"); mgr.commit(); delay(); assertEquals("value2", cache2.get(fqn, key)); if (!isInvalidation) { assertEquals("value3", cache1.get(fqn, key)); } else { assertNull(cache1.get(fqn, key)); } mgr.begin(); cache2.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); node2.put(key, "value2"); cache2.getInvocationContext().getOptionOverrides().reset(); node2.put(key, "value4"); mgr.commit(); delay(); assertEquals("value4", cache2.get(fqn, key)); if (!isInvalidation) { assertEquals("value4", cache1.get(fqn, key)); } else { assertNull("should be invalidated", cache1.get(fqn, key)); } } public void testAddChild() throws Exception { cache1.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); cache1.getRoot().addChild(fqn); delay(); // cache1 should still have this assertTrue(cache1.getRoot().hasChild(fqn)); // cache 2 should not Node node2 = cache2.getRoot().getChild(fqn); assertTrue("Should be null", node2 == null || (isInvalidation && !node2.isValid())); // now try again with passing the default options cache1.getRoot().removeChild(fqn); cache1.getInvocationContext().getOptionOverrides().setCacheModeLocal(false); cache1.getRoot().addChild(fqn); delay(); // cache1 should still have this assertTrue(cache1.getRoot().hasChild(fqn)); // cache 2 should as well if (!isInvalidation) { assertTrue(cache2.getRoot().hasChild(fqn)); } else { assertTrue("Should be null", node2 == null || !node2.isValid()); } } protected abstract void delay(); }././@LongLink0000000000000000000000000000016000000000000011562 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/options/cachemodelocal/SyncInvalidationOptLocksTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/options/cachemodelocal/SyncInvalidationOptLoc0000644000175000017500000000113610665031725033402 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.options.cachemodelocal; import org.jboss.cache.config.Configuration; import org.testng.annotations.Test; @Test(groups = {"functional", "jgroups"}) public class SyncInvalidationOptLocksTest extends CacheModeLocalTestBase { public SyncInvalidationOptLocksTest() { cacheMode = Configuration.CacheMode.INVALIDATION_SYNC; nodeLockingScheme = "OPTIMISTIC"; isInvalidation = true; } protected void delay() { } } ././@LongLink0000000000000000000000000000015100000000000011562 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/options/cachemodelocal/SyncReplPessLocksTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/options/cachemodelocal/SyncReplPessLocksTest.0000644000175000017500000000105110665031725033303 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.options.cachemodelocal; import org.jboss.cache.config.Configuration; import org.testng.annotations.Test; @Test(groups = {"functional", "jgroups"}) public class SyncReplPessLocksTest extends CacheModeLocalTestBase { public SyncReplPessLocksTest() { cacheMode = Configuration.CacheMode.REPL_SYNC; nodeLockingScheme = "PESSIMISTIC"; } protected void delay() { } }././@LongLink0000000000000000000000000000015200000000000011563 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/options/cachemodelocal/AsyncReplPessLocksTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/options/cachemodelocal/AsyncReplPessLocksTest0000644000175000017500000000117411017455042033366 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.options.cachemodelocal; import org.jboss.cache.config.Configuration; import org.jboss.cache.util.TestingUtil; import org.testng.annotations.Test; @Test(groups = {"functional", "jgroups"}) public class AsyncReplPessLocksTest extends CacheModeLocalTestBase { public AsyncReplPessLocksTest() { cacheMode = Configuration.CacheMode.REPL_ASYNC; nodeLockingScheme = "PESSIMISTIC"; } protected void delay() { TestingUtil.sleepThread(500); } } ././@LongLink0000000000000000000000000000015000000000000011561 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/options/cachemodelocal/SyncReplOptLocksTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/options/cachemodelocal/SyncReplOptLocksTest.j0000644000175000017500000000116711017455042033307 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.options.cachemodelocal; import org.jboss.cache.config.Configuration; import org.jboss.cache.util.TestingUtil; import org.testng.annotations.Test; @Test(groups = {"functional", "jgroups"}) public class SyncReplOptLocksTest extends CacheModeLocalTestBase { public SyncReplOptLocksTest() { cacheMode = Configuration.CacheMode.REPL_SYNC; nodeLockingScheme = "OPTIMISTIC"; } protected void delay() { TestingUtil.sleepThread(250); } } ././@LongLink0000000000000000000000000000016100000000000011563 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/options/cachemodelocal/AsyncInvalidationOptLocksTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/options/cachemodelocal/AsyncInvalidationOptLo0000644000175000017500000000114211017455042033367 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.options.cachemodelocal; import org.jboss.cache.config.Configuration; import org.jboss.cache.util.TestingUtil; public class AsyncInvalidationOptLocksTest extends CacheModeLocalTestBase { public AsyncInvalidationOptLocksTest() { cacheMode = Configuration.CacheMode.INVALIDATION_ASYNC; nodeLockingScheme = "OPTIMISTIC"; isInvalidation = true; } protected void delay() { TestingUtil.sleepThread(500); } } ././@LongLink0000000000000000000000000000016200000000000011564 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/options/cachemodelocal/AsyncInvalidationPessLocksTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/options/cachemodelocal/AsyncInvalidationPessL0000644000175000017500000000123011017455042033356 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.options.cachemodelocal; import org.jboss.cache.config.Configuration; import org.jboss.cache.util.TestingUtil; import org.testng.annotations.Test; @Test(groups = "functional") public class AsyncInvalidationPessLocksTest extends CacheModeLocalTestBase { public AsyncInvalidationPessLocksTest() { cacheMode = Configuration.CacheMode.INVALIDATION_ASYNC; nodeLockingScheme = "PESSIMISTIC"; isInvalidation = true; } protected void delay() { TestingUtil.sleepThread(500); } } ././@LongLink0000000000000000000000000000016100000000000011563 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/options/cachemodelocal/SyncInvalidationPessLocksTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/options/cachemodelocal/SyncInvalidationPessLo0000644000175000017500000000114110665031725033403 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.options.cachemodelocal; import org.jboss.cache.config.Configuration; import org.testng.annotations.Test; @Test(groups = {"functional", "jgroups"}) public class SyncInvalidationPessLocksTest extends CacheModeLocalTestBase { public SyncInvalidationPessLocksTest() { cacheMode = Configuration.CacheMode.INVALIDATION_SYNC; nodeLockingScheme = "PESSIMISTIC"; isInvalidation = true; } protected void delay() { } } ././@LongLink0000000000000000000000000000015100000000000011562 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/options/cachemodelocal/AsyncReplOptLocksTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/options/cachemodelocal/AsyncReplOptLocksTest.0000644000175000017500000000117111017455042033271 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.options.cachemodelocal; import org.jboss.cache.config.Configuration; import org.jboss.cache.util.TestingUtil; import org.testng.annotations.Test; @Test(groups = {"functional", "jgroups"}) public class AsyncReplOptLocksTest extends CacheModeLocalTestBase { public AsyncReplOptLocksTest() { cacheMode = Configuration.CacheMode.REPL_ASYNC; nodeLockingScheme = "OPTIMISTIC"; } protected void delay() { TestingUtil.sleepThread(500); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/options/SuppressLockingTest.java0000644000175000017500000001374210732232105030767 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.options; import org.jboss.cache.CacheFactory; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.config.Configuration; import org.jboss.cache.transaction.DummyTransactionManagerLookup; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.TransactionManager; import java.util.HashMap; import java.util.Map; /** * Tests the suppression of locking nodes * * @author Manik Surtani (manik@jboss.org) */ @Test(groups = {"functional"}) public class SuppressLockingTest { private Fqn fqn = Fqn.fromString("/blah"); private Fqn fqn1 = Fqn.fromString("/blah/1"); private CacheSPI cache; private TransactionManager m; @BeforeMethod(alwaysRun = true) public void setUp() { Configuration config = new Configuration(); config.setCacheMode(Configuration.CacheMode.LOCAL); config.setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName()); CacheFactory instance = new DefaultCacheFactory(); cache = (CacheSPI) instance.createCache(config); m = cache.getTransactionManager(); } @AfterMethod(alwaysRun = true) public void tearDown() { if (cache != null) { cache.stop(); cache = null; } m = null; } public void testSuppressionOfWriteLocks() throws Exception { TransactionManager m = cache.getTransactionManager(); m.begin(); assertFalse(cache.getInvocationContext().getOptionOverrides().isSuppressLocking()); cache.put(fqn, "x", "1"); assertFalse(cache.getInvocationContext().getOptionOverrides().isSuppressLocking()); assertEquals(2, cache.getNumberOfLocksHeld()); m.commit(); assertEquals(0, cache.getNumberOfLocksHeld()); cache.removeNode(fqn); m.begin(); cache.getInvocationContext().getOptionOverrides().setSuppressLocking(true); assertTrue(cache.getInvocationContext().getOptionOverrides().isSuppressLocking()); cache.put(fqn, "x", "2"); assertFalse(cache.getInvocationContext().getOptionOverrides().isSuppressLocking()); assertEquals(0, cache.getNumberOfLocksHeld()); m.commit(); assertEquals(0, cache.getNumberOfLocksHeld()); // test normal operation again cache.removeNode(fqn); m.begin(); assertFalse(cache.getInvocationContext().getOptionOverrides().isSuppressLocking()); cache.put(fqn, "x", "3"); assertFalse(cache.getInvocationContext().getOptionOverrides().isSuppressLocking()); assertEquals(2, cache.getNumberOfLocksHeld()); m.commit(); assertEquals(0, cache.getNumberOfLocksHeld()); } /** * This one fails now. * * @throws Exception */ public void testSuppressionOf2WriteLocks() throws Exception { TransactionManager m = cache.getTransactionManager(); m.begin(); cache.put(fqn, "x", "1"); assertEquals(2, cache.getNumberOfLocksHeld()); m.commit(); assertEquals(0, cache.getNumberOfLocksHeld()); cache.removeNode(fqn); m.begin(); cache.getInvocationContext().getOptionOverrides().setSuppressLocking(true); cache.put(fqn, "x", "2"); cache.getInvocationContext().getOptionOverrides().setSuppressLocking(true); cache.put(fqn1, "y", "3"); assertEquals(0, cache.getNumberOfLocksHeld()); m.commit(); assertEquals(0, cache.getNumberOfLocksHeld()); Map map = new HashMap(); map.put("x", "1"); m.begin(); cache.getInvocationContext().getOptionOverrides().setSuppressLocking(true); cache.put(fqn, map); cache.getInvocationContext().getOptionOverrides().setSuppressLocking(true); cache.put(fqn1, map); assertEquals(0, cache.getNumberOfLocksHeld()); m.commit(); assertEquals(0, cache.getNumberOfLocksHeld()); // test normal operation again cache.removeNode(fqn); m.begin(); cache.put(fqn, "x", "3"); assertEquals(2, cache.getNumberOfLocksHeld()); m.commit(); assertEquals(0, cache.getNumberOfLocksHeld()); } public void testSuppressionOfReadLocks() throws Exception { cache.put(fqn, "x", "y"); m.begin(); assertFalse(cache.getInvocationContext().getOptionOverrides().isSuppressLocking()); cache.get(fqn, "x"); assertFalse(cache.getInvocationContext().getOptionOverrides().isSuppressLocking()); assertEquals(2, cache.getNumberOfLocksHeld()); m.commit(); assertEquals(0, cache.getNumberOfLocksHeld()); m.begin(); cache.getInvocationContext().getOptionOverrides().setSuppressLocking(true); assertTrue(cache.getInvocationContext().getOptionOverrides().isSuppressLocking()); cache.get(fqn, "x"); assertFalse(cache.getInvocationContext().getOptionOverrides().isSuppressLocking()); assertEquals(0, cache.getNumberOfLocksHeld()); m.commit(); assertEquals(0, cache.getNumberOfLocksHeld()); // test normal operation again m.begin(); assertFalse(cache.getInvocationContext().getOptionOverrides().isSuppressLocking()); cache.get(fqn, "x"); assertFalse(cache.getInvocationContext().getOptionOverrides().isSuppressLocking()); assertEquals(2, cache.getNumberOfLocksHeld()); m.commit(); assertEquals(0, cache.getNumberOfLocksHeld()); } public void testNodeCreation() { assertNull(cache.getRoot().getChild(fqn)); cache.getInvocationContext().getOptionOverrides().setSuppressLocking(true); cache.put(fqn, "x", "y"); assertEquals(0, cache.getNumberOfLocksHeld()); assertEquals("y", cache.getRoot().getChild(fqn).get("x")); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/options/FailSilentlyTest.java0000644000175000017500000001124210732232105030224 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.options; import org.jboss.cache.CacheFactory; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.config.Configuration; import static org.testng.AssertJUnit.assertEquals; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.Transaction; import javax.transaction.TransactionManager; import java.util.HashMap; import java.util.Map; /** * Tests passing in the failSilently option in various scenarios. * * @author Manik Surtani (manik@jboss.org) */ @Test(groups = {"functional"}) public class FailSilentlyTest { private CacheSPI cache; private TransactionManager manager; private Transaction tx; private Fqn fqn = Fqn.fromString("/a"); private String key = "key"; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { if (cache != null) tearDown(); CacheFactory instance = new DefaultCacheFactory(); cache = (CacheSPI) instance.createCache(false); // very short acquisition timeout cache.getConfiguration().setLockAcquisitionTimeout(100); cache.getConfiguration().setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); cache.getConfiguration().setCacheMode(Configuration.CacheMode.LOCAL); cache.start(); manager = cache.getTransactionManager(); } @AfterMethod(alwaysRun = true) public void tearDown() { if (tx != null) { try { manager.resume(tx); manager.rollback(); } catch (Exception e) { // who cares } } if (cache != null) { cache.stop(); cache = null; } } public void testPutKeyValue() throws Exception { manager.begin(); cache.put(fqn, key, "value"); tx = manager.suspend(); cache.getInvocationContext().getOptionOverrides().setFailSilently(true); cache.put(fqn, key, "value2"); } public void testPutData() throws Exception { Map data = new HashMap(); data.put(key, "value"); manager.begin(); cache.put(fqn, data); tx = manager.suspend(); cache.getInvocationContext().getOptionOverrides().setFailSilently(true); cache.put(fqn, data); } public void testRemoveNode() throws Exception { cache.put(fqn, key, "value"); manager.begin(); // get a read lock cache.get(fqn, key); tx = manager.suspend(); cache.getInvocationContext().getOptionOverrides().setFailSilently(true); cache.removeNode(fqn); } public void testRemoveKey() throws Exception { cache.put(fqn, key, "value"); manager.begin(); // get a read lock cache.get(fqn, key); tx = manager.suspend(); cache.getInvocationContext().getOptionOverrides().setFailSilently(true); cache.remove(fqn, key); } public void testGetNode() throws Exception { cache.put(fqn, key, "value"); manager.begin(); // get a WL cache.put(fqn, key, "value2"); tx = manager.suspend(); cache.getInvocationContext().getOptionOverrides().setFailSilently(true); cache.getNode(fqn); } public void testGetKey() throws Exception { cache.put(fqn, key, "value"); manager.begin(); // get a WL cache.put(fqn, key, "value2"); tx = manager.suspend(); cache.getInvocationContext().getOptionOverrides().setFailSilently(true); cache.get(fqn, key); } public void testGetChildrenNames() throws Exception { cache.put(fqn, key, "value"); manager.begin(); // get a WL cache.put(fqn, key, "value2"); tx = manager.suspend(); cache.getInvocationContext().getOptionOverrides().setFailSilently(true); cache.getChildrenNames(fqn); } public void testPutThatWillFail() throws Exception { manager.begin(); cache.put(fqn, "k", "v");// this will get WLs on / and /a tx = manager.suspend(); assertEquals(2, cache.getNumberOfLocksHeld()); // now this call WILL fail, but should fail silently - i.e., not roll back. manager.begin(); cache.getInvocationContext().getOptionOverrides().setFailSilently(true); cache.put(fqn, "x", "y"); // should not roll back, despite the cache put call failing/timing out. manager.commit(); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/options/ExplicitVersionsReplTest.java0000644000175000017500000003276111017455042031777 0ustar twernertwernerpackage org.jboss.cache.options; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.NodeSPI; import org.jboss.cache.config.Configuration; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.optimistic.DataVersion; import org.jboss.cache.optimistic.DefaultDataVersion; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.RollbackException; import javax.transaction.TransactionManager; /** * Tests the passing in of explicit {@see DataVersion} instances when using optimistic locking + replication. * * @author Manik Surtani (manik@jboss.org) */ @Test(groups = {"functional"}) @SuppressWarnings("unchecked") public class ExplicitVersionsReplTest { private CacheSPI cache[]; private Fqn fqn = Fqn.fromString("/a"); private String key = "key"; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { if (cache != null) tearDown(); cache = new CacheSPI[2]; cache[0] = createCache(); cache[1] = createCache(); TestingUtil.blockUntilViewsReceived(cache, 20000); } private CacheSPI createCache() { Configuration c = new Configuration(); c.setCacheMode(Configuration.CacheMode.REPL_SYNC); c.setNodeLockingScheme("OPTIMISTIC"); // give us lots of time to trace and debug shit c.setSyncCommitPhase(true); c.setSyncRollbackPhase(true); c.setSyncReplTimeout(1000); c.setLockAcquisitionTimeout(1000); c.setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); return (CacheSPI) new DefaultCacheFactory().createCache(c); } @AfterMethod(alwaysRun = true) public void tearDown() { if (cache != null) { for (CacheSPI aCache : cache) destroyCache(aCache); cache = null; } } private void destroyCache(CacheSPI c) { TransactionManager tm = c.getTransactionManager(); try { if (tm != null && tm.getTransaction() != null) tm.rollback(); } catch (Exception e) { } c.stop(); } /** * This test sets a custom data version first, expects it to replicate, and then does a put on the remote * cache using an implicit data version. Should fail with a CCE. * * @throws Exception */ public void testIncompatibleVersionTypes1() throws Exception { DataVersion version = new TestVersion("99"); cache[0].getInvocationContext().getOptionOverrides().setDataVersion(version); cache[0].put(fqn, key, "value");// TestVersion-99 should be on both caches now TransactionManager mgr = cache[0].getTransactionManager(); mgr.begin(); // don't explicitly set a data version. System.out.println("************ stage 2"); // force an IC scrub //cache[1].getInvocationContext().setOptionOverrides(null); cache[1].put(fqn, key, "value2"); try { mgr.commit(); assertTrue("expected to fail", false); } catch (RollbackException e) { // should fail. assertTrue("expected to fail with a nested ClassCastException", true); } } /** * This test sets a custom data version first, expects it to replicate, and then does a put on the remote * cache using a higher custom data version. Should pass and not throw any exceptions. * * @throws Exception */ public void testCompatibleVersionTypes1() throws Exception { DataVersion version = new TestVersion("99"); cache[0].getInvocationContext().getOptionOverrides().setDataVersion(version); cache[0].put(fqn, key, "value");// TestVersion-99 should be on both caches now TransactionManager mgr = cache[0].getTransactionManager(); mgr.begin(); version = new TestVersion("999"); cache[1].getInvocationContext().getOptionOverrides().setDataVersion(version); cache[1].put(fqn, key, "value2"); mgr.commit(); } /** * This test sets a custom data version first, expects it to replicate, and then does a put on the remote * cache using a lower custom data version. Should fail. * * @throws Exception */ public void testCompatibleVersionTypesOutDatedVersion1() throws Exception { DataVersion version = new TestVersion("99"); cache[0].getInvocationContext().getOptionOverrides().setDataVersion(version); cache[0].put(fqn, key, "value");// TestVersion-99 should be on both caches now TransactionManager mgr = cache[0].getTransactionManager(); mgr.begin(); version = new TestVersion("29"); cache[1].getInvocationContext().getOptionOverrides().setDataVersion(version); cache[1].put(fqn, key, "value2"); try { mgr.commit(); assertTrue("expected to fail", false); } catch (RollbackException e) { // should fail. assertTrue("expected to fail with a CacheException to do with a versioning mismatch", true); } } /** * This test sets an implicit data version first, expects it to replicate, and then does a put on the remote * cache using a custom data version. Should fail with a CCE. * * @throws Exception */ public void testIncompatibleVersionTypes2() throws Exception { cache[0].put(fqn, key, "value");// default data version should be on both caches now TransactionManager mgr = cache[0].getTransactionManager(); mgr.begin(); // explicitly set data version DataVersion version = new TestVersion("99"); cache[1].getInvocationContext().getOptionOverrides().setDataVersion(version); try { cache[1].put(fqn, key, "value2"); mgr.commit(); assertTrue("expected to fail", false); } catch (Exception e) { // should fail. assertTrue("expected to fail", true); } } /** * This test sets an implicit data version first, expects it to replicate, and then does a put on the remote * cache using a higher implicit data version. Should pass and not throw any exceptions. * * @throws Exception */ public void testCompatibleVersionTypes2() throws Exception { cache[0].put(fqn, key, "value");// TestVersion-99 should be on both caches now TransactionManager mgr = cache[0].getTransactionManager(); mgr.begin(); DataVersion version = new DefaultDataVersion(300); cache[1].getInvocationContext().getOptionOverrides().setDataVersion(version); cache[1].put(fqn, key, "value2"); mgr.commit(); } /** * This test sets an implicit data version first, expects it to replicate, and then does a put on the remote * cache using a lower implicit data version. Should fail. * * @throws Exception */ public void testCompatibleVersionTypesOutDatedVersion2() throws Exception { DataVersion version = new DefaultDataVersion(200); cache[0].getInvocationContext().getOptionOverrides().setDataVersion(version); cache[0].put(fqn, key, "value");// TestVersion-99 should be on both caches now TransactionManager mgr = cache[0].getTransactionManager(); mgr.begin(); version = new DefaultDataVersion(100); cache[1].getInvocationContext().getOptionOverrides().setDataVersion(version); cache[1].put(fqn, key, "value2"); try { // this call will use implicit versioning and will hence fail. mgr.commit(); assertTrue("expected to fail", false); } catch (Exception e) { // should fail. assertTrue("expected to fail with a CacheException to do with a versioning mismatch", true); } } public void testPropagationOfDefaultVersions() throws Exception { DefaultDataVersion expected = new DefaultDataVersion(); expected = (DefaultDataVersion) expected.increment(); cache[0].put(fqn, key, "value"); assertEquals("value", cache[0].get(fqn, key)); assertEquals("value", cache[1].get(fqn, key)); assertEquals(expected, ((NodeSPI) cache[0].getNode(fqn)).getVersion()); assertEquals(expected, ((NodeSPI) cache[1].getNode(fqn)).getVersion()); cache[1].put(fqn, key, "value2"); expected = (DefaultDataVersion) expected.increment(); assertEquals("value2", cache[0].get(fqn, key)); assertEquals("value2", cache[1].get(fqn, key)); assertEquals(expected, ((NodeSPI) cache[0].getNode(fqn)).getVersion()); assertEquals(expected, ((NodeSPI) cache[1].getNode(fqn)).getVersion()); } public void testPropagationOfCustomVersions() throws Exception { TestVersion expected = new TestVersion("100"); cache[0].getInvocationContext().getOptionOverrides().setDataVersion(expected); cache[0].put(fqn, key, "value"); assertEquals("value", cache[0].get(fqn, key)); assertEquals("value", cache[1].get(fqn, key)); assertEquals(expected, ((NodeSPI) cache[0].getNode(fqn)).getVersion()); assertEquals(expected, ((NodeSPI) cache[1].getNode(fqn)).getVersion()); expected = new TestVersion("200"); cache[1].getInvocationContext().getOptionOverrides().setDataVersion(expected); cache[1].put(fqn, key, "value2"); assertEquals("value2", cache[0].get(fqn, key)); assertEquals("value2", cache[1].get(fqn, key)); assertEquals(expected, ((NodeSPI) cache[0].getNode(fqn)).getVersion()); assertEquals(expected, ((NodeSPI) cache[1].getNode(fqn)).getVersion()); } public void testExplicitVersionOnRoot() throws Exception { TestVersion newVersion = new TestVersion("100"); cache[0].getInvocationContext().getOptionOverrides().setDataVersion(newVersion); cache[0].getTransactionManager().begin(); cache[0].put(Fqn.ROOT, "k", "v"); try { cache[0].getTransactionManager().commit(); fail("Should have barfed"); } catch (RollbackException rbe) { // should barf since by default ROOT uses a default DV } } public void testExplicitVersionOnLeaf() throws Exception { cache[0].put("/org/domain/Entity", null); assertEquals(1, ((DefaultDataVersion) ((NodeSPI) cache[0].getNode("/org/domain/Entity")).getVersion()).getRawVersion()); assertEquals(1, ((DefaultDataVersion) ((NodeSPI) cache[1].getNode("/org/domain/Entity")).getVersion()).getRawVersion()); TestVersion v = new TestVersion("Arse"); cache[0].getInvocationContext().getOptionOverrides().setDataVersion(v); cache[0].put(Fqn.fromString("/org/domain/Entity/EntityInstance#1"), "k", "v"); assertEquals(1, ((DefaultDataVersion) ((NodeSPI) cache[0].getNode("/org/domain/Entity")).getVersion()).getRawVersion()); assertEquals(v, ((NodeSPI) cache[0].getNode("/org/domain/Entity/EntityInstance#1")).getVersion()); assertEquals(1, ((DefaultDataVersion) ((NodeSPI) cache[1].getNode("/org/domain/Entity")).getVersion()).getRawVersion()); assertEquals(v, ((NodeSPI) cache[1].getNode("/org/domain/Entity/EntityInstance#1")).getVersion()); } public void testExplicitVersionOnLeafImplicitParentCreation() throws Exception { TestVersion v = new TestVersion("Arse"); cache[0].getInvocationContext().getOptionOverrides().setDataVersion(v); cache[0].put(Fqn.fromString("/org/domain/Entity/EntityInstance#1"), "k", "v"); assertEquals(0, ((DefaultDataVersion) ((NodeSPI) cache[0].getNode("/org/domain/Entity")).getVersion()).getRawVersion()); assertEquals(v, ((NodeSPI) cache[0].getNode("/org/domain/Entity/EntityInstance#1")).getVersion()); assertEquals(0, ((DefaultDataVersion) ((NodeSPI) cache[1].getNode("/org/domain/Entity")).getVersion()).getRawVersion()); assertEquals(v, ((NodeSPI) cache[1].getNode("/org/domain/Entity/EntityInstance#1")).getVersion()); } public void testExplicitVersionOnParentAndChild() throws Exception { TestVersion vParent = new TestVersion("Parent-Version"); cache[0].getTransactionManager().begin(); cache[0].getInvocationContext().getOptionOverrides().setDataVersion(vParent); cache[0].put(Fqn.fromString("/parent"), "k", "v"); cache[0].getTransactionManager().commit(); assertEquals(0, ((DefaultDataVersion) ((NodeSPI) cache[0].getRoot()).getVersion()).getRawVersion()); assertEquals(vParent, ((NodeSPI) cache[0].getNode("/parent")).getVersion()); assertEquals(0, ((DefaultDataVersion) ((NodeSPI) cache[1].getRoot()).getVersion()).getRawVersion()); assertEquals(vParent, ((NodeSPI) cache[1].getNode("/parent")).getVersion()); TestVersion vChild = new TestVersion("Child-Version"); cache[0].getTransactionManager().begin(); cache[0].getInvocationContext().getOptionOverrides().setDataVersion(vChild); cache[0].put(Fqn.fromString("/parent/child"), "k", "v"); cache[0].getTransactionManager().commit(); assertEquals(0, ((DefaultDataVersion) ((NodeSPI) cache[0].getRoot()).getVersion()).getRawVersion()); assertEquals(vParent, ((NodeSPI) cache[0].getNode("/parent")).getVersion()); assertEquals(vChild, ((NodeSPI) cache[0].getNode("/parent/child")).getVersion()); assertEquals(0, ((DefaultDataVersion) ((NodeSPI) cache[1].getRoot()).getVersion()).getRawVersion()); assertEquals(vParent, ((NodeSPI) cache[1].getNode("/parent")).getVersion()); assertEquals(vChild, ((NodeSPI) cache[1].getNode("/parent/child")).getVersion()); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/options/ForceWriteLockTest.java0000644000175000017500000001046211017455776030534 0ustar twernertwernerpackage org.jboss.cache.options; import org.jboss.cache.CacheFactory; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.NodeSPI; import org.jboss.cache.config.Configuration; import org.jboss.cache.lock.LockManager; import org.jboss.cache.lock.LockType; import org.jboss.cache.util.TestingUtil; import static org.testng.AssertJUnit.assertTrue; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.TransactionManager; /** * Tests forcing a write lock to be obtained on a node * * @author Manik Surtani * @since 2.0.0 */ @Test(groups = {"functional"}) public class ForceWriteLockTest { private CacheSPI cache; private Fqn fqn = Fqn.fromString("/a/b"); private TransactionManager tm; @BeforeMethod(alwaysRun = true) public void setUp() { Configuration c = new Configuration(); c.setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); CacheFactory instance = new DefaultCacheFactory(); cache = (CacheSPI) instance.createCache(c); tm = cache.getTransactionManager(); } @AfterMethod(alwaysRun = true) public void tearDown() { try { tm.rollback(); } catch (Exception e) { // ignore } cache.stop(); } public void testControl() throws Exception { cache.put(fqn, "k", "v"); tm.begin(); cache.get(fqn, "k"); // parent should be read-locked!! assertLocked(cache.getInvocationContext().getGlobalTransaction(), fqn.getParent(), false); assertLocked(cache.getInvocationContext().getGlobalTransaction(), fqn, false); tm.commit(); assertNotLocked(fqn); } public void testForceWithGetTx() throws Exception { cache.put(fqn, "k", "v"); tm.begin(); cache.getInvocationContext().getOptionOverrides().setForceWriteLock(true); cache.get(fqn, "k"); // parent should be read-locked!! assertLocked(cache.getInvocationContext().getGlobalTransaction(), fqn.getParent(), false); assertLocked(cache.getInvocationContext().getGlobalTransaction(), fqn, true); tm.commit(); assertNotLocked(fqn); // now test normal operation testControl(); } public void testForceWithPutTx() throws Exception { cache.put(fqn, "k", "v"); tm.begin(); cache.getInvocationContext().getOptionOverrides().setForceWriteLock(true); cache.put(fqn, "k", "v2"); // parent should be read-locked!! assertLocked(cache.getInvocationContext().getGlobalTransaction(), fqn.getParent(), false); assertLocked(cache.getInvocationContext().getGlobalTransaction(), fqn, true); tm.commit(); assertNotLocked(fqn); // now test normal operation testControl(); } public void testForceWithRemoveTx() throws Exception { cache.put(fqn, "k", "v"); tm.begin(); cache.getInvocationContext().getOptionOverrides().setForceWriteLock(true); cache.remove(fqn, "k"); // parent should be read-locked!! assertLocked(cache.getInvocationContext().getGlobalTransaction(), fqn.getParent(), false); assertLocked(cache.getInvocationContext().getGlobalTransaction(), fqn, true); tm.commit(); assertNotLocked(fqn); // now test normal operation testControl(); } private void assertNotLocked(Fqn fqn) { assert !TestingUtil.extractLockManager(cache).isLocked(cache.peek(fqn, true)) : "Node " + fqn + " is locked!!"; } private void assertLocked(Object owner, Fqn fqn, boolean write_locked) { LockManager lm = TestingUtil.extractLockManager(cache); NodeSPI n = cache.peek(fqn, true); if (owner == null) owner = Thread.currentThread(); assertTrue("node " + fqn + " is not locked", lm.isLocked(n)); if (write_locked) { assertTrue("node " + fqn + " is not write-locked by owner " + owner, lm.ownsLock(fqn, LockType.WRITE, owner)); } else { assertTrue("node " + fqn + " is not read-locked by owner " + owner, lm.ownsLock(fqn, LockType.READ, owner)); } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/options/LockAcquisitionTimeoutTest.java0000644000175000017500000002253110732232105032300 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.options; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Option; import org.jboss.cache.lock.TimeoutException; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.TransactionManager; /** * Test functionality of {@link Option#setLockAcquisitionTimeout(int)}. * * @author Brian Stansberry */ @Test(groups = {"functional"}) public class LockAcquisitionTimeoutTest { private static final Log log = LogFactory.getLog(LockAcquisitionTimeoutTest.class); private static final Fqn FQNA = Fqn.fromString("/A"); private static final Fqn FQNB = Fqn.fromString("/B"); private static final String KEY = "key"; private static final String VALUE1 = "value1"; private static final String VALUE2 = "value2"; private CacheSPI cache; private Option option; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { cache = (CacheSPI) new DefaultCacheFactory().createCache(false); Configuration c = cache.getConfiguration(); c.setCacheMode("REPL_SYNC"); c.setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); cache.start(); option = new Option(); option.setLockAcquisitionTimeout(0); } @AfterMethod(alwaysRun = true) public void tearDown() { if (cache != null) { cache.stop(); cache = null; } } /** * Confirms that doing a put with a lockAcquisitionTime option set * does the put as expected. There is no other thread or tx contesting * the lock the put needs, so this is just a simple test that the option * doesn't somehow screw up the put. * * @throws Exception */ public void testSimplePut() throws Exception { log.info("++++ testSimplePut() ++++"); simplePutTest(false); } /** * Confirms that doing a put with a lockAcquisitionTime option set * does the put as expected when executed within a transaction. There is no * other thread or tx contesting the lock the put needs, so this is just a * simple test that the option doesn't somehow screw up the put. * * @throws Exception */ public void testSimplePutWithTx() throws Exception { log.info("++++ testSimplePutWithTx() ++++"); simplePutTest(true); } private void simplePutTest(boolean useTx) throws Exception { TransactionManager tm = useTx ? cache.getTransactionManager() : null; LowTimeoutSetter setter = new LowTimeoutSetter(tm); setter.start(); setter.join(2000); if (!setter.finished) { setter.interrupt(); fail("Puts failed to complete in a timely manner"); } assertNull("LowTimeoutSetter saw no TimeoutException", setter.te); assertNull("LowTimeoutSetter saw no misc Exception", setter.failure); assertEquals("Cache correct for " + FQNA, VALUE2, cache.get(FQNA, KEY)); assertEquals("Cache correct for " + FQNB, VALUE2, cache.get(FQNB, KEY)); } /** * Confirms that a put with a lockAcquisitionTimeout option set to zero * fails promptly in the presence of a lock on the target node. * * @throws Exception */ public void testContestedPut() throws Exception { log.info("++++ testContestedPut() ++++"); contestedPutTest(false); } /** * Confirms that a put with a lockAcquisitionTimeout option set to zero * is ignored if executed within a transaction. * * @throws Exception */ public void testContestedPutWithTx() throws Exception { log.info("++++ testContestedPutWithTx() ++++"); contestedPutTest(true); } private void contestedPutTest(boolean tx) throws Exception { TransactionManager mgr = cache.getTransactionManager(); mgr.begin(); LowTimeoutSetter setter = null; try { // Put a WL on /A cache.put(FQNA, KEY, VALUE1); // Launch a thread that tries to write to /A setter = new LowTimeoutSetter(tx ? mgr : null); setter.start(); setter.join(2000); if (!setter.finished) { setter.interrupt(); fail("Puts failed to complete in a timely manner"); } } finally { // always commit the tx mgr.commit(); } assertNotNull("LowTimeoutSetter saw TimeoutException", setter.te); assertNull("LowTimeoutSetter saw no misc Exception", setter.failure); assertEquals("Cache correct for " + FQNA, VALUE1, cache.get(FQNA, KEY)); assertEquals("Cache correct for " + FQNB, VALUE2, cache.get(FQNB, KEY)); } public void testSimpleRead() throws Exception { log.info("++++++ testSimpleRead() ++++++"); simpleReadTest(false); } public void testSimpleReadWithTx() throws Exception { log.info("++++++ testSimpleReadWithTx() ++++++"); simpleReadTest(true); } private void simpleReadTest(boolean useTx) throws Exception { TransactionManager tm = useTx ? cache.getTransactionManager() : null; LowTimeoutReader reader = new LowTimeoutReader(tm); cache.put(FQNA, KEY, VALUE1); reader.start(); reader.join(2000); if (!reader.finished) { reader.interrupt(); fail("Read failed to complete in a timely manner"); } assertNull("LowTimeoutSetter saw no TimeoutException", reader.te); assertNull("LowTimeoutSetter saw no misc Exception", reader.failure); assertEquals("LowTimeoutSetter correct for " + FQNA, VALUE1, reader.value); } public void testContestedRead() throws Exception { log.info("++++++ testContestedRead() ++++++"); contestedReadTest(false); } public void testContestedReadWithTx() throws Exception { log.info("++++++ testContestedReadWithTx() ++++++"); contestedReadTest(true); } private void contestedReadTest(boolean tx) throws Exception { TransactionManager mgr = cache.getTransactionManager(); mgr.begin(); LowTimeoutReader reader = null; try { // Put a WL on /A cache.put(FQNA, KEY, VALUE1); // Launch a thread that tries to read from /A reader = new LowTimeoutReader(tx ? mgr : null); reader.start(); reader.join(2000); if (!reader.finished) { reader.interrupt(); fail("Read failed to complete in a timely manner"); } } finally { // always commit the tx mgr.commit(); } assertNotNull("LowTimeoutSetter saw TimeoutException", reader.te); assertNull("LowTimeoutSetter saw no misc Exception", reader.failure); assertNull("LowTimeoutSetter unable to read " + FQNA, reader.value); } class LowTimeoutSetter extends Thread { TransactionManager tm; TimeoutException te; Throwable failure; boolean finished; LowTimeoutSetter(TransactionManager tm) { this.tm = tm; } public void run() { try { try { if (tm != null) { tm.begin(); } cache.put(FQNB, KEY, VALUE2); cache.getInvocationContext().setOptionOverrides(option); cache.put(FQNA, KEY, VALUE2); } catch (TimeoutException te) { this.te = te; } catch (Exception e) { if (tm != null) tm.setRollbackOnly(); throw e; } finally { if (tm != null) { tm.commit(); } finished = true; } } catch (Throwable t) { failure = t; } } } class LowTimeoutReader extends Thread { TransactionManager tm; TimeoutException te; Throwable failure; Object value; boolean finished; LowTimeoutReader(TransactionManager tm) { this.tm = tm; } public void run() { try { try { if (tm != null) { tm.begin(); } cache.getInvocationContext().setOptionOverrides(option); value = cache.get(FQNA, KEY); } catch (TimeoutException te) { this.te = te; } catch (Exception e) { if (tm != null) tm.setRollbackOnly(); throw e; } finally { if (tm != null) { tm.commit(); } finished = true; } } catch (Throwable t) { failure = t; } } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/options/ForceCacheModeTest.java0000644000175000017500000003454711017455042030435 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.options; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.CacheException; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.config.Option; import org.jboss.cache.notifications.annotation.CacheListener; import org.jboss.cache.notifications.annotation.NodeEvicted; import org.jboss.cache.notifications.annotation.NodeModified; import org.jboss.cache.notifications.annotation.NodeRemoved; import org.jboss.cache.notifications.event.NodeEvent; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.TransactionManager; import java.util.concurrent.CountDownLatch; /** * Tests functionality of {@link Option#setForceAsynchronous(boolean)} and * {@link Option#setForceSynchronous(boolean)}. * * @author Manik Surtani (manik@jboss.org) */ @Test(groups = {"functional"}) public class ForceCacheModeTest { private static final Log log = LogFactory.getLog(ForceCacheModeTest.class); private static final Fqn FQNA = Fqn.fromString("/A"); private static final String KEY = "key"; private static final String VALUE1 = "value1"; private static final String VALUE2 = "value2"; private CacheSPI cache1, cache2; private Option asyncOption; private Option syncOption; private static CountDownLatch latch; private BlockingListener listener; private void createCaches(NodeLockingScheme scheme, CacheMode mode) { cache1 = (CacheSPI) new DefaultCacheFactory().createCache(false); Configuration c = cache1.getConfiguration(); c.setNodeLockingScheme(scheme); c.setCacheMode(mode); c.setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); cache2 = (CacheSPI) new DefaultCacheFactory().createCache(false); c = cache2.getConfiguration(); c.setNodeLockingScheme(scheme); c.setCacheMode(mode); c.setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); cache1.start(); cache2.start(); asyncOption = new Option(); asyncOption.setForceAsynchronous(true); syncOption = new Option(); syncOption.setForceSynchronous(true); Option local = new Option(); local.setCacheModeLocal(true); cache1.getInvocationContext().setOptionOverrides(local); cache1.put(FQNA, KEY, VALUE1); assertEquals("Cache1 correct", VALUE1, cache1.get(FQNA, KEY)); local = new Option(); local.setCacheModeLocal(true); cache2.getInvocationContext().setOptionOverrides(local); cache2.put(FQNA, KEY, VALUE1); // Validate data is as expected assertEquals("Cache1 correct", VALUE1, cache1.get(FQNA, KEY)); assertEquals("Cache2 correct", VALUE1, cache2.get(FQNA, KEY)); listener = new BlockingListener(); cache2.addCacheListener(listener); } @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { latch = new CountDownLatch(1); } @AfterMethod(alwaysRun = true) public void tearDown() { if (cache1 != null) { cache1.stop(); cache1.destroy(); cache1 = null; } if (cache2 != null) { if (listener != null) cache2.removeCacheListener(listener); cache2.stop(); cache2.destroy(); cache2 = null; } latch.countDown(); } public void testPessimisticReplicationPutForceAsync() throws Exception { createCaches(NodeLockingScheme.PESSIMISTIC, CacheMode.REPL_SYNC); checkNoBlocking(null, asyncOption, false); } public void testPessimisticReplicationRemoveForceAsync() throws Exception { createCaches(NodeLockingScheme.PESSIMISTIC, CacheMode.REPL_SYNC); checkNoBlocking(null, asyncOption, true); } public void testPessimisticReplicationPutForceAsyncWithTx() throws Exception { createCaches(NodeLockingScheme.PESSIMISTIC, CacheMode.REPL_SYNC); checkBlocking(cache1.getTransactionManager(), asyncOption, false); } public void testPessimisticInvalidationPutForceAsync() throws Exception { createCaches(NodeLockingScheme.PESSIMISTIC, CacheMode.INVALIDATION_SYNC); checkNoBlocking(null, asyncOption, false); } public void testPessimisticInvalidationRemoveForceAsync() throws Exception { createCaches(NodeLockingScheme.PESSIMISTIC, CacheMode.INVALIDATION_SYNC); checkNoBlocking(null, asyncOption, true); } public void testPessimisticInvalidationPutForceAsyncWithTx() throws Exception { createCaches(NodeLockingScheme.PESSIMISTIC, CacheMode.INVALIDATION_SYNC); checkBlocking(cache1.getTransactionManager(), asyncOption, false); } public void testPessimisticReplicationPutForceSync() throws Exception { createCaches(NodeLockingScheme.PESSIMISTIC, CacheMode.REPL_ASYNC); checkBlocking(null, syncOption, false); } public void testPessimisticReplicationRemoveForceSync() throws Exception { createCaches(NodeLockingScheme.PESSIMISTIC, CacheMode.REPL_ASYNC); checkBlocking(null, syncOption, true); } public void testPessimisticReplicationPutForceSyncWithTx() throws Exception { createCaches(NodeLockingScheme.PESSIMISTIC, CacheMode.REPL_ASYNC); checkNoBlocking(cache1.getTransactionManager(), syncOption, false); } public void testPessimisticInvalidationPutForceSync() throws Exception { createCaches(NodeLockingScheme.PESSIMISTIC, CacheMode.INVALIDATION_ASYNC); checkBlocking(null, syncOption, false); } public void testPessimisticInvalidationRemoveForceSync() throws Exception { createCaches(NodeLockingScheme.PESSIMISTIC, CacheMode.INVALIDATION_ASYNC); checkBlocking(null, syncOption, true); } public void testPessimisticInvalidationPutForceSyncWithTx() throws Exception { createCaches(NodeLockingScheme.PESSIMISTIC, CacheMode.INVALIDATION_ASYNC); checkNoBlocking(cache1.getTransactionManager(), syncOption, false); } public void testOptimisticReplicationPutForceAsync() throws Exception { createCaches(NodeLockingScheme.OPTIMISTIC, CacheMode.REPL_SYNC); checkNoBlocking(null, asyncOption, false); } public void testOptimisticReplicationRemoveForceAsync() throws Exception { createCaches(NodeLockingScheme.OPTIMISTIC, CacheMode.REPL_SYNC); checkNoBlocking(null, asyncOption, true); } public void testOptimisticReplicationPutForceAsyncWithTx() throws Exception { createCaches(NodeLockingScheme.OPTIMISTIC, CacheMode.REPL_SYNC); checkBlocking(cache1.getTransactionManager(), asyncOption, false); } public void testOptimisticInvalidationPutForceAsync() throws Exception { createCaches(NodeLockingScheme.OPTIMISTIC, CacheMode.INVALIDATION_SYNC); checkNoBlocking(null, asyncOption, false); } public void testOptimisticInvalidationRemoveForceAsync() throws Exception { createCaches(NodeLockingScheme.OPTIMISTIC, CacheMode.INVALIDATION_SYNC); checkNoBlocking(null, asyncOption, true); } public void testOptimisticInvalidationPutForceAsyncWithTx() throws Exception { createCaches(NodeLockingScheme.OPTIMISTIC, CacheMode.INVALIDATION_SYNC); checkBlocking(cache1.getTransactionManager(), asyncOption, false); } public void testOptimisticReplicationPutForceSync() throws Exception { createCaches(NodeLockingScheme.OPTIMISTIC, CacheMode.REPL_ASYNC); checkBlocking(null, syncOption, false); } public void testOptimisticReplicationRemoveForceSync() throws Exception { createCaches(NodeLockingScheme.OPTIMISTIC, CacheMode.REPL_ASYNC); checkBlocking(null, syncOption, true); } public void testOptimisticReplicationPutForceSyncWithTx() throws Exception { createCaches(NodeLockingScheme.OPTIMISTIC, CacheMode.REPL_ASYNC); checkNoBlocking(cache1.getTransactionManager(), syncOption, false); } public void testOptimisticInvalidationPutForceSync() throws Exception { createCaches(NodeLockingScheme.OPTIMISTIC, CacheMode.INVALIDATION_ASYNC); checkBlocking(null, syncOption, false); } public void testOptimisticInvalidationRemoveForceSync() throws Exception { createCaches(NodeLockingScheme.OPTIMISTIC, CacheMode.INVALIDATION_ASYNC); checkBlocking(null, syncOption, true); } public void testOptimisticInvalidationPutForceSyncWithTx() throws Exception { createCaches(NodeLockingScheme.OPTIMISTIC, CacheMode.INVALIDATION_ASYNC); checkNoBlocking(cache1.getTransactionManager(), syncOption, false); } /** * Confirms the updater is not blocked and that the cache state is as * expected at the end. * * @param tm transction manager Updater should use. For non-transactional * tests, should be null * @param option Option to set before doing put * @param removeTest true if we're testing a remove; false if a put * @throws InterruptedException * @throws CacheException */ private void checkNoBlocking(TransactionManager tm, Option option, boolean removeTest) throws InterruptedException, CacheException { Updater updater = new Updater(tm, option, removeTest); updater.start(); updater.join(250); assertTrue("Updater didn't finish", updater.finished); assertNull("Updater failed", updater.failure); for (int i = 0; i < 50; i++) { if (listener.blocked) break; TestingUtil.sleepThread(10); } assertTrue("Listener should have blocked!", listener.blocked); assertEquals("Cache1 state incorrect!", removeTest ? null : VALUE2, cache1.get(FQNA, KEY)); latch.countDown(); for (int i = 0; i < 50; i++) { if (!listener.blocked) break; TestingUtil.sleepThread(10); } // sleep a bit more to ensure the cache2 thread completes TestingUtil.sleepThread(5); CacheMode mode = cache2.getConfiguration().getCacheMode(); boolean expectNull = (removeTest || mode == CacheMode.INVALIDATION_ASYNC || mode == CacheMode.INVALIDATION_SYNC); assertEquals("Cache2 state incorrect!", expectNull ? null : VALUE2, cache2.get(FQNA, KEY)); } /** * Confirms the updater is blocked and that the cache state is as * expected at the end. * * @param tm transction manager Updater should use. For non-transactional * tests, should be null * @param option Option to set before doing put * @param removeTest true if we're testing a remove; false if a put * @throws InterruptedException * @throws CacheException */ private void checkBlocking(TransactionManager tm, Option option, boolean removeTest) throws InterruptedException, CacheException { Updater updater = new Updater(tm, option, removeTest); updater.start(); updater.join(250); assertFalse("Updater should have blocked!", updater.finished); for (int i = 0; i < 50; i++) { if (listener.blocked) break; TestingUtil.sleepThread(10); } assertTrue("Listener should have blocked", listener.blocked); latch.countDown(); for (int i = 0; i < 50; i++) { if (updater.finished && !listener.blocked) break; TestingUtil.sleepThread(10); } assertTrue("Updater should have finished", updater.finished); assertFalse("Listener should have blocked", listener.blocked); assertNull("Updater should have succeeded", updater.failure); assertEquals("Cache1 state incorrect", removeTest ? null : VALUE2, cache1.get(FQNA, KEY)); // sleep a bit more to ensure the cache2 thread completes TestingUtil.sleepThread(500); CacheMode mode = cache2.getConfiguration().getCacheMode(); boolean expectNull = (removeTest || mode == CacheMode.INVALIDATION_ASYNC || mode == CacheMode.INVALIDATION_SYNC); assertEquals("Cache2 state incorrect", expectNull ? null : VALUE2, cache2.get(FQNA, KEY)); } class Updater extends Thread { TransactionManager tm; Option option; boolean remove; Throwable failure; boolean finished; Updater(TransactionManager tm, Option option) { this(tm, option, false); } Updater(TransactionManager tm, Option option, boolean remove) { this.tm = tm; this.option = option; this.remove = remove; } public void run() { try { try { if (tm != null) { tm.begin(); } cache1.getInvocationContext().setOptionOverrides(option); if (remove) cache1.remove(FQNA, KEY); else cache1.put(FQNA, KEY, VALUE2); } catch (Exception e) { if (tm != null) tm.setRollbackOnly(); throw e; } finally { if (tm != null) { tm.commit(); } finished = true; } } catch (Throwable t) { failure = t; } } } @CacheListener public static class BlockingListener { boolean blocked; @NodeModified @NodeRemoved @NodeEvicted public void block(NodeEvent event) { log.error("Received event notification " + event); if (event.isPre() == false && FQNA.equals(event.getFqn())) { blocked = true; try { latch.await(); } catch (InterruptedException e) { } blocked = false; } } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/config/0000755000175000017500000000000011376173760023714 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/config/ConfigurationTest.java0000644000175000017500000000463110746355150030225 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.config; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertNull; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.factories.UnitTestCacheConfigurationFactory; import org.jboss.cache.factories.XmlConfigurationParser; import org.jboss.cache.lock.IsolationLevel; import org.jboss.cache.transaction.TransactionSetup; import org.testng.annotations.Test; /** * @author Manik Surtani (manik@jboss.org) */ @Test(groups = {"functional", "jgroups", "transaction"}) public class ConfigurationTest { public void testReplSyncStack() throws Exception { Configuration conf = UnitTestCacheConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC); //now test that everything has been read in properly. assertEquals(Configuration.CacheMode.REPL_SYNC, conf.getCacheMode()); assertEquals(TransactionSetup.getManagerLookup(), conf.getTransactionManagerLookupClass()); assertEquals(IsolationLevel.REPEATABLE_READ, conf.getIsolationLevel()); assertEquals(false, conf.isUseReplQueue()); assertEquals(0, conf.getReplQueueInterval()); assertEquals(0, conf.getReplQueueMaxElements()); assertEquals("JBossCache-Cluster", conf.getClusterName()); assertEquals(true, conf.isFetchInMemoryState()); assertEquals(15000, conf.getStateRetrievalTimeout()); assertEquals(15000, conf.getSyncReplTimeout()); assertEquals(10000, conf.getLockAcquisitionTimeout()); assertNull(conf.getEvictionConfig()); assertEquals(false, conf.isUseRegionBasedMarshalling()); // not testing the JGroups configs since JGroups doesn't expose their config as a bean. // comparing an XML block is a PITA. } public void testMultiplexerStack() throws Exception { XmlConfigurationParser parser = new XmlConfigurationParser(); Configuration conf = parser.parseFile("META-INF/conf-test/mux-service.xml"); // test that multiplexer settings have been read in properly. //assertEquals("MultiplexerService", "jgroups.mux:name=Multiplexer", conf.getMultiplexerService()); assertEquals("MultiplexerStack", "tcp", conf.getMultiplexerStack()); assertNull("ClusterConfig", conf.getClusterConfig()); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/config/StringPropertyReplacementTest.java0000644000175000017500000001130110775002562032577 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.config; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertFalse; import static org.testng.AssertJUnit.assertTrue; import org.jboss.cache.config.BuddyReplicationConfig.BuddyLocatorConfig; import org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.eviction.LRUConfiguration; import org.jboss.cache.eviction.LRUPolicy; import org.jboss.cache.factories.XmlConfigurationParser; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * Tests that string property replacement works properly when parsing * a config file. JBCACHE-1218 * * @author Brian Stansberry */ @Test(groups = {"functional"}) public class StringPropertyReplacementTest { public static final String STRING_REPLACED_FILE = "META-INF/conf-test/string-property-replaced-service.xml"; private static final String PROP_BASE = "test.property."; private static final String CACHE_MODE_PROP = PROP_BASE + "CacheMode"; private static final String SYNC_COMMIT_PROP = PROP_BASE + "SyncCommitPhase"; private static final String NUM_BUDDIES_PROP = PROP_BASE + "BuddyReplicationConfig.numBuddies"; private static final String MAX_NODES_PROP = PROP_BASE + "EvictionPolicyConfig.maxNodes"; private static final String BUDDY_POOL_PROP = PROP_BASE + "BuddyReplicationConfig.buddyPoolName"; private String cacheMode; private String numBuddies; private String syncCommitPhase; private String maxNodes; private String buddyPoolName; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { cacheMode = System.getProperty(CACHE_MODE_PROP); numBuddies = System.getProperty(NUM_BUDDIES_PROP); syncCommitPhase = System.getProperty(SYNC_COMMIT_PROP); maxNodes = System.getProperty(MAX_NODES_PROP); buddyPoolName = System.getProperty(BUDDY_POOL_PROP); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { if (cacheMode == null) System.clearProperty(CACHE_MODE_PROP); else System.setProperty(CACHE_MODE_PROP, cacheMode); if (numBuddies == null) System.clearProperty(NUM_BUDDIES_PROP); else System.setProperty(NUM_BUDDIES_PROP, numBuddies); if (syncCommitPhase == null) System.clearProperty(SYNC_COMMIT_PROP); else System.setProperty(SYNC_COMMIT_PROP, syncCommitPhase); if (maxNodes == null) System.clearProperty(MAX_NODES_PROP); else System.setProperty(MAX_NODES_PROP, maxNodes); if (buddyPoolName == null) System.clearProperty(BUDDY_POOL_PROP); else System.setProperty(BUDDY_POOL_PROP, buddyPoolName); } public void testStringPropertyReplacement() throws Exception { System.setProperty(CACHE_MODE_PROP, "REPL_SYNC"); System.setProperty(NUM_BUDDIES_PROP, "3"); System.setProperty(SYNC_COMMIT_PROP, "false"); System.setProperty(MAX_NODES_PROP, "1000"); System.setProperty(BUDDY_POOL_PROP, "replaced"); Configuration cfg = new XmlConfigurationParser().parseFile(STRING_REPLACED_FILE); assertEquals(NodeLockingScheme.OPTIMISTIC, cfg.getNodeLockingScheme()); assertEquals(CacheMode.REPL_SYNC, cfg.getCacheMode()); assertFalse(cfg.isSyncCommitPhase()); assertTrue(cfg.isSyncRollbackPhase()); assertEquals(15000, cfg.getLockAcquisitionTimeout()); String clusterCfg = cfg.getClusterConfig(); assertTrue(clusterCfg == null || clusterCfg.length() == 0); EvictionConfig ec = cfg.getEvictionConfig(); assertEquals(LRUPolicy.class.getName(), ec.getDefaultEvictionPolicyClass()); EvictionRegionConfig erc = ec.getEvictionRegionConfigs().get(0); LRUConfiguration epc = (LRUConfiguration) erc.getEvictionPolicyConfig(); assertEquals(1000, epc.getMaxNodes()); CacheLoaderConfig clc = cfg.getCacheLoaderConfig(); IndividualCacheLoaderConfig iclc = clc.getFirstCacheLoaderConfig(); assertEquals(System.getProperty("java.io.tmpdir"), iclc.getProperties().get("location")); BuddyReplicationConfig brc = cfg.getBuddyReplicationConfig(); assertTrue(brc.isEnabled()); assertEquals("replaced", brc.getBuddyPoolName()); BuddyLocatorConfig blc = brc.getBuddyLocatorConfig(); assertEquals("3", blc.getBuddyLocatorProperties().get("numBuddies")); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/config/ChannelInjectionTest.java0000644000175000017500000000421511017455042030620 0ustar twernertwernerpackage org.jboss.cache.config; import org.jboss.cache.Cache; import org.jboss.cache.CacheFactory; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.util.TestingUtil; import org.jgroups.JChannel; import static org.testng.AssertJUnit.assertEquals; import org.testng.annotations.AfterMethod; import org.testng.annotations.Test; import java.util.HashSet; import java.util.Set; /** * Tests that JBC prefers an injected Channel to creating one via * a configured JChannelFactory and stack name. * * @author Brian Stansberry * @version $Revision: 5906 $ */ @Test(groups = {"functional"}) public class ChannelInjectionTest { private Set> caches = new HashSet>(); @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { for (Cache cache : caches) { try { cache.stop(); } catch (Exception e) { e.printStackTrace(System.out); } } } public void testChannelInjectionPreference() throws Exception { Cache cache1 = createCache(); Cache cache2 = createCache(); JChannel ch1 = new JChannel(JChannel.DEFAULT_PROTOCOL_STACK); cache1.getConfiguration().getRuntimeConfig().setChannel(ch1); JChannel ch2 = new JChannel(JChannel.DEFAULT_PROTOCOL_STACK); cache2.getConfiguration().getRuntimeConfig().setChannel(ch2); cache1.start(); cache2.start(); TestingUtil.blockUntilViewsReceived(new Cache[]{cache1, cache2}, 10000); Fqn fqn = Fqn.fromString("/a"); cache1.put(fqn, "key", "value"); assertEquals("Value replicated", "value", cache2.get(fqn, "key")); } private Cache createCache() { Configuration config = new Configuration(); config.setCacheMode(Configuration.CacheMode.REPL_SYNC); CacheFactory factory = new DefaultCacheFactory(); Cache cache = factory.createCache(config, false); caches.add(cache); return cache; } }jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/config/EvictionRegionConfigurationTest.java0000644000175000017500000000345710752654427033105 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.config; import static org.testng.AssertJUnit.fail; import java.util.HashSet; import java.util.List; import java.util.Set; import org.jboss.cache.Fqn; import org.jboss.cache.RegionManager; import org.jboss.cache.eviction.EvictionPolicy; import org.jboss.cache.eviction.LRUPolicy; import org.testng.annotations.Test; /** * @author Brian Stansberry */ @Test(groups = {"functional", "jgroups", "transaction"}) public class EvictionRegionConfigurationTest { /** * This test duplicates the way the JBoss Microcontainer goes about * building up an eviction config, and checks that at the * end of the process there is only one _default_ region configured. * * @throws Exception */ public void testDuplicateDefaultRegion() throws Exception { EvictionConfig ec = new EvictionConfig(); ec.setDefaultEvictionPolicyClass(LRUPolicy.class.getName()); List ercs = ec.getEvictionRegionConfigs(); EvictionRegionConfig erc = new EvictionRegionConfig(); erc.setRegionFqn(RegionManager.DEFAULT_REGION); EvictionPolicy policy = LRUPolicy.class.newInstance(); erc.setEvictionPolicyConfig(policy.getEvictionConfigurationClass().newInstance()); ercs.add(erc); ec.setEvictionRegionConfigs(ercs); ercs = ec.getEvictionRegionConfigs(); Set fqns = new HashSet(); for (EvictionRegionConfig cfg : ercs) { if (fqns.contains(cfg.getRegionFqn())) fail("duplicate region fqn " + cfg.getRegionFqn()); fqns.add(cfg.getRegionFqn()); } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/config/ConfigurationCloningTest.java0000644000175000017500000001322110747470040031527 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.config; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertFalse; import static org.testng.AssertJUnit.assertTrue; import static org.testng.AssertJUnit.fail; import java.util.List; import java.util.Properties; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.config.BuddyReplicationConfig.BuddyLocatorConfig; import org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig; import org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig.SingletonStoreConfig; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.config.Configuration.NodeLockingScheme; import org.jboss.cache.eviction.LRUPolicy; import org.jboss.cache.factories.XmlConfigurationParser; import org.testng.annotations.Test; /** * Tests the ability to clone Configuration elements and end up with * independently modifiable configurations. * * @author Brian Stansberry */ @Test(groups = {"functional"}) public class ConfigurationCloningTest { /** A file that includes every configuration element I could think of */ public static final String DEFAULT_CONFIGURATION_FILE = "META-INF/conf-test/clonable-config-service.xml"; private static final Log log = LogFactory.getLog(ConfigurationCloningTest.class); public void testClone() throws Exception { XmlConfigurationParser parser = new XmlConfigurationParser(); Configuration c = parser.parseFile(DEFAULT_CONFIGURATION_FILE); try { Configuration clone = c.clone(); // Test a few simple properties assertEquals(NodeLockingScheme.OPTIMISTIC, clone.getNodeLockingScheme()); assertEquals(CacheMode.INVALIDATION_SYNC, clone.getCacheMode()); assertEquals("CloneCluster", clone.getClusterName()); assertEquals(c.getClusterConfig(), clone.getClusterConfig()); assertEquals(3, clone.getStateRetrievalTimeout()); // Buddy replication config BuddyReplicationConfig brc1 = c.getBuddyReplicationConfig(); BuddyReplicationConfig brc2 = clone.getBuddyReplicationConfig(); assertFalse(brc1 == brc2); assertEquals(7, brc2.getBuddyCommunicationTimeout()); assertEquals("cloneGroup", brc2.getBuddyPoolName()); BuddyLocatorConfig blc1 = brc1.getBuddyLocatorConfig(); BuddyLocatorConfig blc2 = brc2.getBuddyLocatorConfig(); assertFalse(blc1 == blc2); Properties p1 = blc1.getBuddyLocatorProperties(); Properties p2 = blc2.getBuddyLocatorProperties(); assertFalse(p1 == p2); assertEquals(p1, p2); // Eviction EvictionConfig ec1 = c.getEvictionConfig(); EvictionConfig ec2 = clone.getEvictionConfig(); assertFalse(ec1 == ec2); assertEquals(4, ec2.getDefaultEventQueueSize()); assertEquals(45, ec2.getWakeupIntervalSeconds()); assertEquals(LRUPolicy.class.getName(), ec2.getDefaultEvictionPolicyClass()); List ercs1 = ec1.getEvictionRegionConfigs(); List ercs2 = ec2.getEvictionRegionConfigs(); assertEquals(ercs1.size(), ercs2.size()); for (int i = 0; i < ercs1.size(); i++) { compareEvictionRegionConfigs(ercs1.get(i), ercs2.get(i)); } // Cache loading CacheLoaderConfig clc1 = c.getCacheLoaderConfig(); CacheLoaderConfig clc2 = clone.getCacheLoaderConfig(); assertFalse(clc1 == clc2); assertFalse(clc2.isPassivation()); assertTrue(clc2.isShared()); List clcs1 = clc1.getIndividualCacheLoaderConfigs(); List clcs2 = clc2.getIndividualCacheLoaderConfigs(); assertEquals(clcs1.size(), clcs2.size()); for (int i = 0; i < clcs1.size(); i++) { compareCacheLoaderConfigs(clcs1.get(i), clcs2.get(i)); } RuntimeConfig rc1 = c.getRuntimeConfig(); RuntimeConfig rc2 = clone.getRuntimeConfig(); assertFalse(rc1 == rc2); assertEquals(rc1, rc2); } catch (CloneNotSupportedException e) { log.error(e.getMessage(), e); fail("Cloning failed -- " + e.getMessage()); } } private void compareEvictionRegionConfigs(EvictionRegionConfig erc1, EvictionRegionConfig erc2) { assertEquals(erc1.getRegionName(), erc2.getRegionName()); assertEquals(erc1.getRegionFqn(), erc2.getRegionFqn()); assertEquals(erc1.getEventQueueSize(), erc2.getEventQueueSize()); EvictionPolicyConfig epc1 = erc1.getEvictionPolicyConfig(); EvictionPolicyConfig epc2 = erc2.getEvictionPolicyConfig(); assertFalse(epc1 == epc2); assertEquals(epc1, epc2); } private void compareCacheLoaderConfigs(IndividualCacheLoaderConfig clc1, IndividualCacheLoaderConfig clc2) { assertFalse(clc1 == clc2); assertEquals(clc1, clc2); Properties p1 = clc1.getProperties(); Properties p2 = clc2.getProperties(); assertFalse(p1 == p2); assertEquals(p1, p2); SingletonStoreConfig ssc1 = clc1.getSingletonStoreConfig(); SingletonStoreConfig ssc2 = clc2.getSingletonStoreConfig(); assertFalse(ssc1 == ssc2); assertEquals(ssc1, ssc2); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/notifications/0000755000175000017500000000000011376173744025322 5ustar twernertwerner././@LongLink0000000000000000000000000000015400000000000011565 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/notifications/RemoteCacheListenerOptimisticTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/notifications/RemoteCacheListenerOptimisticTe0000644000175000017500000000070310665031725033460 0ustar twernertwernerpackage org.jboss.cache.notifications; import org.testng.annotations.Test; /** * optimistic counterpart of {@link org.jboss.cache.notifications.RemoteCacheListenerTest} * * @author Manik Surtani * @since 2.0.0 */ @Test(groups = {"functional"}) public class RemoteCacheListenerOptimisticTest extends RemoteCacheListenerTest { public RemoteCacheListenerOptimisticTest() { optLocking = true; } }././@LongLink0000000000000000000000000000014600000000000011566 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/notifications/CacheListenerOptimisticTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/notifications/CacheListenerOptimisticTest.jav0000644000175000017500000000057210740541456033437 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.notifications; import org.testng.annotations.Test; @Test(groups = "functional") public class CacheListenerOptimisticTest extends CacheListenerTest { public CacheListenerOptimisticTest() { optLocking = true; } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/notifications/NotificationThreadTest.java0000644000175000017500000001520711017455042032572 0ustar twernertwernerpackage org.jboss.cache.notifications; import org.jboss.cache.Cache; import org.jboss.cache.CacheFactory; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.config.Configuration; import org.jboss.cache.loader.AbstractCacheLoaderTestBase; import org.jboss.cache.loader.DummyInMemoryCacheLoader; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.notifications.annotation.*; import org.jboss.cache.notifications.event.Event; import org.jboss.cache.transaction.DummyTransactionManagerLookup; import static org.testng.AssertJUnit.assertNotSame; import static org.testng.AssertJUnit.assertSame; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import javax.transaction.TransactionManager; import java.util.LinkedList; import java.util.List; /** * Tests the threading model used when calling notifications * * @author Manik Surtani * @since 2.0.0 */ public class NotificationThreadTest extends AbstractCacheLoaderTestBase { private Cache cache1, cache2; private TestCacheListener listener; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { // need 2 caches to test viewChange notifications CacheFactory instance = new DefaultCacheFactory(); cache1 = instance.createCache(false); cache2 = instance.createCache(false); cache1.getConfiguration().setCacheMode(Configuration.CacheMode.REPL_SYNC); cache2.getConfiguration().setCacheMode(Configuration.CacheMode.REPL_SYNC); cache1.getConfiguration().setSyncCommitPhase(true); cache2.getConfiguration().setSyncCommitPhase(true); cache1.getConfiguration().setSyncRollbackPhase(true); cache2.getConfiguration().setSyncRollbackPhase(true); cache1.getConfiguration().setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName()); cache2.getConfiguration().setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName()); cache1.getConfiguration().setCacheLoaderConfig(getSingleCacheLoaderConfig("", DummyInMemoryCacheLoader.class.getName(), null, false, false, false)); listener = new TestCacheListener(); cache1.addCacheListener(listener); } @AfterMethod(alwaysRun = true) public void tearDown() { cache1.stop(); cache2.stop(); } public void testPessimisticWithCacheLoader() throws Throwable { doTest(false); } public void testOptimisticWithCacheLoader() throws Throwable { cache1.getConfiguration().setNodeLockingScheme(Configuration.NodeLockingScheme.OPTIMISTIC); cache2.getConfiguration().setNodeLockingScheme(Configuration.NodeLockingScheme.OPTIMISTIC); doTest(false); } public void testPessimisticWithPassivation() throws Throwable { cache1.getConfiguration().getCacheLoaderConfig().setPassivation(true); doTest(false); } public void testOptimisticWithPassivation() throws Throwable { cache1.getConfiguration().getCacheLoaderConfig().setPassivation(true); cache1.getConfiguration().setNodeLockingScheme(Configuration.NodeLockingScheme.OPTIMISTIC); cache2.getConfiguration().setNodeLockingScheme(Configuration.NodeLockingScheme.OPTIMISTIC); doTest(false); } public void testPessimisticWithCacheLoaderTx() throws Throwable { doTest(true); } public void testOptimisticWithCacheLoaderTx() throws Throwable { cache1.getConfiguration().setNodeLockingScheme(Configuration.NodeLockingScheme.OPTIMISTIC); cache2.getConfiguration().setNodeLockingScheme(Configuration.NodeLockingScheme.OPTIMISTIC); doTest(true); } public void testPessimisticWithPassivationTx() throws Throwable { cache1.getConfiguration().getCacheLoaderConfig().setPassivation(true); doTest(true); } public void testOptimisticWithPassivationTx() throws Throwable { cache1.getConfiguration().getCacheLoaderConfig().setPassivation(true); cache1.getConfiguration().setNodeLockingScheme(Configuration.NodeLockingScheme.OPTIMISTIC); cache2.getConfiguration().setNodeLockingScheme(Configuration.NodeLockingScheme.OPTIMISTIC); doTest(true); } private void doTest(boolean tx) throws Throwable { // stop and start events cache1.stop(); cache1.start(); cache2.start(); TransactionManager tm = cache1.getConfiguration().getRuntimeConfig().getTransactionManager(); listener.sameThreadExpected = true; listener.mainThread = Thread.currentThread(); Fqn fqn = Fqn.fromString("/a/b/c"); // basic node manipulation events if (tx) tm.begin(); cache1.put(fqn, "k", "v"); if (tx) tm.commit(); if (tx) tm.begin(); cache1.get(fqn, "k"); if (tx) tm.commit(); if (tx) tm.begin(); cache1.put(fqn, "k", "v2"); if (tx) tm.commit(); if (tx) tm.begin(); cache1.removeNode(fqn); if (tx) tm.commit(); if (tx) tm.begin(); cache1.put(fqn, "k", "v3"); if (tx) tm.commit(); if (tx) tm.begin(); // eviction cache1.evict(fqn, true); if (tx) tm.commit(); if (tx) tm.begin(); // and cache loading or activation cache1.get(fqn, "k"); if (tx) tm.commit(); if (tx) tm.begin(); // move event cache1.move(fqn, Fqn.ROOT); if (tx) tm.commit(); // now a view-change - will be in a different thread listener.sameThreadExpected = false; cache2.stop(); // short sleep in case some events are in different threads TestingUtil.sleepThread(500); // now test for exceptions for (Throwable e : listener.exceptions) throw e; } @CacheListener public class TestCacheListener { boolean sameThreadExpected; Thread mainThread; List exceptions = new LinkedList(); @NodeCreated @NodeModified @NodeRemoved @NodeVisited @NodeEvicted @NodeLoaded @NodeMoved @NodeActivated @NodePassivated @CacheStarted @CacheStopped @ViewChanged @TransactionCompleted @TransactionRegistered public void testCallbackThread(Event e) { try { if (sameThreadExpected) assertSame(mainThread, Thread.currentThread()); else assertNotSame(mainThread, Thread.currentThread()); } catch (Throwable t) { exceptions.add(t); } } } }jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/notifications/NotifierAnnotationsTest.java0000644000175000017500000002076011015552370033011 0ustar twernertwernerpackage org.jboss.cache.notifications; import org.jboss.cache.Cache; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.notifications.annotation.CacheListener; import org.jboss.cache.notifications.annotation.CacheStarted; import org.jboss.cache.notifications.annotation.CacheStopped; import org.jboss.cache.notifications.annotation.NodeMoved; import org.jboss.cache.notifications.event.Event; import org.jboss.cache.notifications.event.NodeMovedEvent; import static org.testng.AssertJUnit.*; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.List; /** * Tests both correct and incorrect annotations for listeners * * @author Manik Surtani * @since 2.0.0 */ @Test(groups = {"functional"}) public class NotifierAnnotationsTest { private NotifierImpl n; @BeforeMethod(alwaysRun = true) public void setUp() { Cache c = new DefaultCacheFactory().createCache(false); n = new NotifierImpl(c); } public void testControl() { Object l = new TestControlListener(); n.addCacheListener(l); assertEquals(1, n.getCacheListeners().size()); } public void testCacheListenerNoMethods() { Object l = new TestCacheListenerNoMethodsListener(); n.addCacheListener(l); assertEquals("Hello", l.toString()); assertTrue("No listeners should be registered.", n.getCacheListeners().isEmpty()); // since the valid listener has no methods to listen } public void testNonAnnotatedListener() { Object l = new TestNonAnnotatedListener(); try { n.addCacheListener(l); fail("Should not accept an un-annotated cache listener"); } catch (IncorrectCacheListenerException icle) { // expected } assertTrue("No listeners should be registered.", n.getCacheListeners().isEmpty()); } public void testNonPublicListener() { Object l = new TestNonPublicListener(); try { n.addCacheListener(l); fail("Should not accept a private callback class"); } catch (IncorrectCacheListenerException icle) { // expected } assertTrue("No listeners should be registered.", n.getCacheListeners().isEmpty()); } public void testNonPublicListenerMethod() { Object l = new TestNonPublicListenerMethodListener(); n.addCacheListener(l); // should not fail, should just not register anything assertTrue("No listeners should be registered.", n.getCacheListeners().isEmpty()); } public void testNonVoidReturnTypeMethod() { Object l = new TestNonVoidReturnTypeMethodListener(); try { n.addCacheListener(l); fail("Should not accept a listener method with a return type"); } catch (IncorrectCacheListenerException icle) { // expected } assertTrue("No listeners should be registered.", n.getCacheListeners().isEmpty()); } public void testIncorrectMethodSignature1() { Object l = new TestIncorrectMethodSignature1Listener(); try { n.addCacheListener(l); fail("Should not accept a cache listener with a bad method signature"); } catch (IncorrectCacheListenerException icle) { // expected } assertTrue("No listeners should be registered.", n.getCacheListeners().isEmpty()); } public void testIncorrectMethodSignature2() { Object l = new TestIncorrectMethodSignature2Listener(); try { n.addCacheListener(l); fail("Should not accept a cache listener with a bad method signature"); } catch (IncorrectCacheListenerException icle) { // expected } assertTrue("No listeners should be registered.", n.getCacheListeners().isEmpty()); } public void testIncorrectMethodSignature3() { Object l = new TestIncorrectMethodSignature3Listener(); try { n.addCacheListener(l); fail("Should not accept a cache listener with a bad method signature"); } catch (IncorrectCacheListenerException icle) { // expected } assertTrue("No listeners should be registered.", n.getCacheListeners().isEmpty()); } public void testUnassignableMethodSignature() { Object l = new TestUnassignableMethodSignatureListener(); try { n.addCacheListener(l); fail("Should not accept a cache listener with a bad method signature"); } catch (IncorrectCacheListenerException icle) { // expected } assertTrue("No listeners should be registered.", n.getCacheListeners().isEmpty()); } public void testPartlyUnassignableMethodSignature() { Object l = new TestPartlyUnassignableMethodSignatureListener(); try { n.addCacheListener(l); fail("Should not accept a cache listener with a bad method signature"); } catch (IncorrectCacheListenerException icle) { // expected } } public void testMultipleMethods() { Object l = new TestMultipleMethodsListener(); n.addCacheListener(l); List invocations = n.getCacheStartedListeners(); assertEquals(1, invocations.size()); invocations = n.getCacheStoppedListeners(); assertEquals(1, invocations.size()); assertEquals(1, n.getCacheListeners().size()); } public void testMultipleAnnotationsOneMethod() { Object l = new TestMultipleAnnotationsOneMethodListener(); n.addCacheListener(l); List invocations = n.getCacheStartedListeners(); assertEquals(1, invocations.size()); invocations = n.getCacheStoppedListeners(); assertEquals(1, invocations.size()); assertEquals(1, n.getCacheListeners().size()); } public void testMultipleMethodsOneAnnotation() { Object l = new TestMultipleMethodsOneAnnotationListener(); n.addCacheListener(l); List invocations = n.getCacheStartedListeners(); assertEquals(2, invocations.size()); assertEquals(1, n.getCacheListeners().size()); } @CacheListener public class TestControlListener { @CacheStarted @CacheStopped public void callback(Event e) { System.out.println("Hello"); } } @CacheListener public class TestCacheListenerNoMethodsListener { public String toString() { return "Hello"; } } public class TestNonAnnotatedListener { public String toString() { return "Hello"; } } @CacheListener protected class TestNonPublicListener { @CacheStarted public void callback() { } } @CacheListener public class TestNonPublicListenerMethodListener { @CacheStarted protected void callback(Event e) { } } @CacheListener public class TestNonVoidReturnTypeMethodListener { @CacheStarted public String callback(Event e) { return "Hello"; } } @CacheListener public class TestIncorrectMethodSignature1Listener { @CacheStarted public void callback() { } } @CacheListener public class TestIncorrectMethodSignature2Listener { @CacheStarted public void callback(Event e, String s) { } } @CacheListener public class TestIncorrectMethodSignature3Listener { @CacheStarted public void callback(Event e, String... s) { } } @CacheListener public class TestUnassignableMethodSignatureListener { @CacheStarted public void callback(NodeMovedEvent nme) { } } @CacheListener public class TestPartlyUnassignableMethodSignatureListener { @NodeMoved @CacheStarted public void callback(NodeMovedEvent nme) // sig valid for NodeMoved but not CacheStarted { } } @CacheListener public class TestMultipleMethodsListener { @CacheStarted public void callback1(Event e) { } @CacheStopped public void callback2(Event e) { } } @CacheListener public class TestMultipleAnnotationsOneMethodListener { @CacheStopped @CacheStarted public void callback(Event nme) { } } @CacheListener public class TestMultipleMethodsOneAnnotationListener { @CacheStarted public void callback1(Event e) { } @CacheStarted public void callback2(Event e) { } } } ././@LongLink0000000000000000000000000000014700000000000011567 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/notifications/CacheListenerPassivationTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/notifications/CacheListenerPassivationTest.ja0000644000175000017500000002512111027757573033432 0ustar twernertwernerpackage org.jboss.cache.notifications; import org.jboss.cache.Cache; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig; import org.jboss.cache.config.Configuration; import org.jboss.cache.loader.DummyInMemoryCacheLoader; import org.jboss.cache.lock.IsolationLevel; import org.jboss.cache.notifications.event.Event; import static org.jboss.cache.notifications.event.Event.Type.*; import org.jboss.cache.notifications.event.EventImpl; import static org.jboss.cache.notifications.event.NodeModifiedEvent.ModificationType.PUT_DATA; import org.jboss.cache.transaction.DummyTransactionManagerLookup; import org.jboss.cache.util.TestingUtil; import static org.testng.AssertJUnit.assertEquals; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.Transaction; import javax.transaction.TransactionManager; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @Test(groups = "functional") public class CacheListenerPassivationTest { private Cache cache; private EventLog eventLog = new EventLog(); private TransactionManager tm; private Fqn fqn = Fqn.fromString("/test"); @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { Configuration c = new Configuration(); c.setIsolationLevel(IsolationLevel.REPEATABLE_READ); c.setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName()); CacheLoaderConfig clc = new CacheLoaderConfig(); IndividualCacheLoaderConfig iclc = new IndividualCacheLoaderConfig(); iclc.setClassName(DummyInMemoryCacheLoader.class.getName()); clc.addIndividualCacheLoaderConfig(iclc); clc.setPassivation(true); c.setCacheLoaderConfig(clc); cache = new DefaultCacheFactory().createCache(c); eventLog.events.clear(); cache.addCacheListener(eventLog); tm = cache.getConfiguration().getRuntimeConfig().getTransactionManager(); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { TestingUtil.killCaches(cache); } public void testActivationAndPassivation() throws Exception { assertEquals("Event log should be empty", Collections.emptyList(), eventLog.events); cache.put(fqn, "key", "value"); Map data = new HashMap(); data.put("key", "value"); List expected = new ArrayList(); // now evict the node - which should cause a passivation eventLog.events.clear(); cache.evict(fqn); expected.add(new EventImpl(true, cache, null, data, fqn, null, true, null, false, null, NODE_PASSIVATED)); expected.add(new EventImpl(false, cache, null, Collections.emptyMap(), fqn, null, true, null, false, null, NODE_PASSIVATED)); expected.add(new EventImpl(true, cache, null, null, fqn, null, true, null, false, null, NODE_EVICTED)); expected.add(new EventImpl(false, cache, null, null, fqn, null, true, null, false, null, NODE_EVICTED)); assertEquals(expected, eventLog.events); // now load the node. expected.clear(); eventLog.events.clear(); cache.get(fqn, "DOES_NOT_EXIST"); expected.add(new EventImpl(true, cache, null, Collections.emptyMap(), fqn, null, true, null, false, null, NODE_ACTIVATED)); expected.add(new EventImpl(true, cache, null, null, fqn, null, true, null, false, null, NODE_CREATED)); expected.add(new EventImpl(false, cache, null, null, fqn, null, true, null, false, null, NODE_CREATED)); expected.add(new EventImpl(false, cache, null, data, fqn, null, true, null, false, null, NODE_ACTIVATED)); expected.add(new EventImpl(true, cache, null, null, fqn, null, true, null, false, null, NODE_VISITED)); expected.add(new EventImpl(false, cache, null, null, fqn, null, true, null, false, null, NODE_VISITED)); assertEquals(expected, eventLog.events); } public void testActivationAndPassivationTxNoMods() throws Exception { assertEquals("Event log should be empty", Collections.emptyList(), eventLog.events); cache.put(fqn, "key", "value"); Map data = new HashMap(); data.put("key", "value"); List expected = new ArrayList(); // now evict the node - which should cause a passivation eventLog.events.clear(); cache.evict(fqn); expected.add(new EventImpl(true, cache, null, data, fqn, null, true, null, false, null, NODE_PASSIVATED)); expected.add(new EventImpl(false, cache, null, Collections.emptyMap(), fqn, null, true, null, false, null, NODE_PASSIVATED)); expected.add(new EventImpl(true, cache, null, null, fqn, null, true, null, false, null, NODE_EVICTED)); expected.add(new EventImpl(false, cache, null, null, fqn, null, true, null, false, null, NODE_EVICTED)); assertEquals(expected, eventLog.events); // now load the node. expected.clear(); eventLog.events.clear(); tm.begin(); Transaction tx = tm.getTransaction(); cache.get(fqn, "DOES_NOT_EXIST"); tm.commit(); expected.add(new EventImpl(false, cache, null, null, null, tx, true, null, false, null, TRANSACTION_REGISTERED)); expected.add(new EventImpl(true, cache, null, Collections.emptyMap(), fqn, tx, true, null, false, null, NODE_ACTIVATED)); expected.add(new EventImpl(true, cache, null, null, fqn, tx, true, null, false, null, NODE_CREATED)); expected.add(new EventImpl(false, cache, null, null, fqn, tx, true, null, false, null, NODE_CREATED)); expected.add(new EventImpl(false, cache, null, data, fqn, tx, true, null, false, null, NODE_ACTIVATED)); expected.add(new EventImpl(true, cache, null, null, fqn, tx, true, null, false, null, NODE_VISITED)); expected.add(new EventImpl(false, cache, null, null, fqn, tx, true, null, false, null, NODE_VISITED)); expected.add(new EventImpl(false, cache, null, null, null, tx, true, null, true, null, TRANSACTION_COMPLETED)); assertEquals(expected, eventLog.events); } public void testActivationAndPassivationTxMods() throws Exception { assertEquals("Event log should be empty", Collections.emptyList(), eventLog.events); cache.put(fqn, "key", "value"); Map data = new HashMap(); data.put("key", "value"); List expected = new ArrayList(); // now evict the node - which should cause a passivation eventLog.events.clear(); cache.evict(fqn); expected.add(new EventImpl(true, cache, null, data, fqn, null, true, null, false, null, NODE_PASSIVATED)); expected.add(new EventImpl(false, cache, null, Collections.emptyMap(), fqn, null, true, null, false, null, NODE_PASSIVATED)); expected.add(new EventImpl(true, cache, null, null, fqn, null, true, null, false, null, NODE_EVICTED)); expected.add(new EventImpl(false, cache, null, null, fqn, null, true, null, false, null, NODE_EVICTED)); assertEquals(expected, eventLog.events); // now load the node. expected.clear(); eventLog.events.clear(); tm.begin(); Transaction tx = tm.getTransaction(); cache.put(fqn, "key2", "value2"); tm.commit(); expected.add(new EventImpl(false, cache, null, null, null, tx, true, null, false, null, TRANSACTION_REGISTERED)); expected.add(new EventImpl(true, cache, null, null, fqn, tx, true, null, false, null, NODE_CREATED)); expected.add(new EventImpl(false, cache, null, null, fqn, tx, true, null, false, null, NODE_CREATED)); expected.add(new EventImpl(true, cache, null, Collections.emptyMap(), fqn, tx, true, null, false, null, NODE_ACTIVATED)); expected.add(new EventImpl(false, cache, null, data, fqn, tx, true, null, false, null, NODE_ACTIVATED)); expected.add(new EventImpl(true, cache, PUT_DATA, data, fqn, tx, true, null, false, null, NODE_MODIFIED)); expected.add(new EventImpl(false, cache, PUT_DATA, Collections.singletonMap("key2", "value2"), fqn, tx, true, null, false, null, NODE_MODIFIED)); expected.add(new EventImpl(false, cache, null, null, null, tx, true, null, true, null, TRANSACTION_COMPLETED)); assertEquals(expected, eventLog.events); assert cache.get(fqn, "key").equals("value"); assert cache.get(fqn, "key2").equals("value2"); } public void testActivationAndPassivationTxModsWithChildren() throws Exception { assertEquals("Event log should be empty", Collections.emptyList(), eventLog.events); cache.put(fqn, "key", "value"); cache.put(Fqn.fromRelativeElements(fqn, "child"), "key3", "value3"); Map data = new HashMap(); data.put("key", "value"); List expected = new ArrayList(); // now evict the node - which should cause a passivation eventLog.events.clear(); cache.evict(fqn); expected.add(new EventImpl(true, cache, null, data, fqn, null, true, null, false, null, NODE_PASSIVATED)); expected.add(new EventImpl(false, cache, null, Collections.emptyMap(), fqn, null, true, null, false, null, NODE_PASSIVATED)); expected.add(new EventImpl(true, cache, null, null, fqn, null, true, null, false, null, NODE_EVICTED)); expected.add(new EventImpl(false, cache, null, null, fqn, null, true, null, false, null, NODE_EVICTED)); assertEquals(expected, eventLog.events); // now load the node. expected.clear(); eventLog.events.clear(); tm.begin(); Transaction tx = tm.getTransaction(); cache.put(fqn, "key2", "value2"); tm.commit(); expected.add(new EventImpl(false, cache, null, null, null, tx, true, null, false, null, TRANSACTION_REGISTERED)); expected.add(new EventImpl(true, cache, null, Collections.emptyMap(), fqn, tx, true, null, false, null, NODE_ACTIVATED)); expected.add(new EventImpl(false, cache, null, data, fqn, tx, true, null, false, null, NODE_ACTIVATED)); expected.add(new EventImpl(true, cache, PUT_DATA, data, fqn, tx, true, null, false, null, NODE_MODIFIED)); expected.add(new EventImpl(false, cache, PUT_DATA, Collections.singletonMap("key2", "value2"), fqn, tx, true, null, false, null, NODE_MODIFIED)); expected.add(new EventImpl(false, cache, null, null, null, tx, true, null, true, null, TRANSACTION_COMPLETED)); assertEquals(expected, eventLog.events); assert cache.get(fqn, "key").equals("value"); assert cache.get(fqn, "key2").equals("value2"); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/notifications/CacheListenerTest.java0000644000175000017500000004715610777521744031554 0ustar twernertwerner/***************************************** * * * JBoss Portal: The OpenSource Portal * * * * Distributable under LGPL license. * * See terms of license at gnu.org. * * * *****************************************/ package org.jboss.cache.notifications; import org.jboss.cache.Cache; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.Node; import org.jboss.cache.config.Configuration; import org.jboss.cache.lock.IsolationLevel; import org.jboss.cache.notifications.event.Event; import static org.jboss.cache.notifications.event.Event.Type.*; import org.jboss.cache.notifications.event.EventImpl; import static org.jboss.cache.notifications.event.NodeModifiedEvent.ModificationType.*; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertNull; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.Transaction; import javax.transaction.TransactionManager; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Note that this is significantly different from the old TreeCacheListenerTest of the JBoss Cache 1.x series, and * exercises the new CacheListener annotation. * * @since 2.0.0 */ @Test(groups = "functional") public class CacheListenerTest { protected boolean optLocking = false; private Cache cache; private TransactionManager tm; private EventLog eventLog = new EventLog(); private Fqn fqn = Fqn.fromString("/test"); @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { Configuration c = new Configuration(); c.setCacheMode(Configuration.CacheMode.LOCAL); c.setIsolationLevel(IsolationLevel.REPEATABLE_READ); if (optLocking) c.setNodeLockingScheme(Configuration.NodeLockingScheme.OPTIMISTIC); c.setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); cache = new DefaultCacheFactory().createCache(c); tm = cache.getConfiguration().getRuntimeConfig().getTransactionManager(); eventLog.events.clear(); cache.addCacheListener(eventLog); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { Transaction t = tm.getTransaction(); if (t != null) tm.rollback(); cache.stop(); cache.destroy(); } // simple tests first public void testCreation() throws Exception { assertEquals("Event log should be empty", Collections.emptyList(), eventLog.events); cache.put(fqn, "key", "value"); Map data = new HashMap(); data.put("key", "value"); //expected List expected = new ArrayList(); if (optLocking) expected.add(new EventImpl(false, cache, null, null, null, null, true, null, false, null, TRANSACTION_REGISTERED)); expected.add(new EventImpl(true, cache, null, null, fqn, null, true, null, false, null, NODE_CREATED)); expected.add(new EventImpl(false, cache, null, null, fqn, null, true, null, false, null, NODE_CREATED)); expected.add(new EventImpl(true, cache, PUT_DATA, Collections.emptyMap(), fqn, null, true, null, false, null, NODE_MODIFIED)); expected.add(new EventImpl(false, cache, PUT_DATA, data, fqn, null, true, null, false, null, NODE_MODIFIED)); if (optLocking) { expected.add(new EventImpl(false, cache, null, null, null, null, true, null, true, null, TRANSACTION_COMPLETED)); eventLog.scrubImplicitTransactions(); } assertEquals(expected, eventLog.events); assertEquals("value", cache.get(fqn, "key")); } public void testOnlyModification() throws Exception { assertEquals("Event log should be empty", Collections.emptyList(), eventLog.events); cache.put(fqn, "key", "value"); Map oldData = new HashMap(); oldData.put("key", "value"); // clear Event log eventLog.events.clear(); assertEquals("Event log should be empty", Collections.emptyList(), eventLog.events); // modify existing node cache.put(fqn, "key", "value2"); Map newData = new HashMap(); newData.put("key", "value2"); //expected List expected = new ArrayList(); if (optLocking) expected.add(new EventImpl(false, cache, null, null, null, null, true, null, false, null, TRANSACTION_REGISTERED)); expected.add(new EventImpl(true, cache, PUT_DATA, oldData, fqn, null, true, null, false, null, NODE_MODIFIED)); expected.add(new EventImpl(false, cache, PUT_DATA, newData, fqn, null, true, null, false, null, NODE_MODIFIED)); if (optLocking) { expected.add(new EventImpl(false, cache, null, null, null, null, true, null, true, null, TRANSACTION_COMPLETED)); eventLog.scrubImplicitTransactions(); } assertEquals(expected.size(), eventLog.events.size()); assertEquals(expected, eventLog.events); } public void testOnlyRemoval() throws Exception { assertEquals("Event log should be empty", Collections.emptyList(), eventLog.events); cache.put(fqn, "key", "value"); Map oldData = new HashMap(); oldData.put("key", "value"); assertEquals("value", cache.get(fqn, "key")); // clear Event log eventLog.events.clear(); assertEquals("Event log should be empty", Collections.emptyList(), eventLog.events); // modify existing node cache.removeNode(fqn); //expected List expected = new ArrayList(); if (optLocking) expected.add(new EventImpl(false, cache, null, null, null, null, true, null, false, null, TRANSACTION_REGISTERED)); expected.add(new EventImpl(true, cache, null, oldData, fqn, null, true, null, false, null, NODE_REMOVED)); expected.add(new EventImpl(false, cache, null, null, fqn, null, true, null, false, null, NODE_REMOVED)); if (optLocking) { expected.add(new EventImpl(false, cache, null, null, null, null, true, null, true, null, TRANSACTION_COMPLETED)); eventLog.scrubImplicitTransactions(); } assertEquals(expected, eventLog.events); // test that the node has in fact been removed. assertNull("Should be null", cache.getRoot().getChild(fqn)); } public void testNonexistentRemove() throws Exception { cache.removeNode("/does/not/exist"); List expected = new ArrayList(); if (optLocking) { expected.add(new EventImpl(false, cache, null, null, null, null, true, null, false, null, TRANSACTION_REGISTERED)); expected.add(new EventImpl(false, cache, null, null, null, null, true, null, true, null, TRANSACTION_COMPLETED)); eventLog.scrubImplicitTransactions(); } assertEquals(expected, eventLog.events); } public void testRemoveData() throws Exception { assertEquals("Event log should be empty", Collections.emptyList(), eventLog.events); cache.put(fqn, "key", "value"); cache.put(fqn, "key2", "value2"); Map oldData = new HashMap(); oldData.put("key", "value"); oldData.put("key2", "value2"); // clear Event log eventLog.events.clear(); assertEquals("Event log should be empty", Collections.emptyList(), eventLog.events); // modify existing node cache.remove(fqn, "key2"); Map removedData = new HashMap(); removedData.put("key2", "value2"); //expected List expected = new ArrayList(); if (optLocking) expected.add(new EventImpl(false, cache, null, null, null, null, true, null, false, null, TRANSACTION_REGISTERED)); expected.add(new EventImpl(true, cache, REMOVE_DATA, oldData, fqn, null, true, null, false, null, NODE_MODIFIED)); expected.add(new EventImpl(false, cache, REMOVE_DATA, removedData, fqn, null, true, null, false, null, NODE_MODIFIED)); if (optLocking) { expected.add(new EventImpl(false, cache, null, null, null, null, true, null, true, null, TRANSACTION_COMPLETED)); eventLog.scrubImplicitTransactions(); } assertEquals(expected, eventLog.events); } public void testPutMap() throws Exception { assertEquals("Event log should be empty", Collections.emptyList(), eventLog.events); Map oldData = new HashMap(); oldData.put("key", "value"); oldData.put("key2", "value2"); // clear Event log eventLog.events.clear(); assertEquals("Event log should be empty", Collections.emptyList(), eventLog.events); // modify existing node cache.put(fqn, oldData); //expected List expected = new ArrayList(); if (optLocking) expected.add(new EventImpl(false, cache, null, null, null, null, true, null, false, null, TRANSACTION_REGISTERED)); expected.add(new EventImpl(true, cache, null, null, fqn, null, true, null, false, null, NODE_CREATED)); expected.add(new EventImpl(false, cache, null, null, fqn, null, true, null, false, null, NODE_CREATED)); expected.add(new EventImpl(true, cache, PUT_MAP, Collections.emptyMap(), fqn, null, true, null, false, null, NODE_MODIFIED)); expected.add(new EventImpl(false, cache, PUT_MAP, oldData, fqn, null, true, null, false, null, NODE_MODIFIED)); if (optLocking) { expected.add(new EventImpl(false, cache, null, null, null, null, true, null, true, null, TRANSACTION_COMPLETED)); eventLog.scrubImplicitTransactions(); } assertEquals(expected, eventLog.events); } public void testMove() { assertEquals("Event log should be empty", Collections.emptyList(), eventLog.events); Fqn newParent = Fqn.fromString("/a"); cache.put(fqn, "key", "value"); cache.put(newParent, "key", "value"); Node n1 = cache.getRoot().getChild(fqn); Node n2 = cache.getRoot().getChild(newParent); eventLog.events.clear();// clear events assertEquals("Event log should be empty", Collections.emptyList(), eventLog.events); cache.move(n1.getFqn(), n2.getFqn()); //expected Fqn newFqn = Fqn.fromRelativeElements(newParent, fqn.getLastElement()); List expected = new ArrayList(); if (optLocking) expected.add(new EventImpl(false, cache, null, null, null, null, true, null, false, null, TRANSACTION_REGISTERED)); expected.add(new EventImpl(true, cache, null, null, fqn, null, true, newFqn, false, null, NODE_MOVED)); expected.add(new EventImpl(false, cache, null, null, fqn, null, true, newFqn, false, null, NODE_MOVED)); if (optLocking) { expected.add(new EventImpl(false, cache, null, null, null, null, true, null, true, null, TRANSACTION_COMPLETED)); eventLog.scrubImplicitTransactions(); } assertEquals(expected, eventLog.events); } // -- now the transactional ones public void testTxNonexistentRemove() throws Exception { tm.begin(); Transaction tx = tm.getTransaction(); cache.removeNode("/does/not/exist"); tm.commit(); List expected = new ArrayList(); expected.add(new EventImpl(false, cache, null, null, null, tx, true, null, false, null, TRANSACTION_REGISTERED)); expected.add(new EventImpl(false, cache, null, null, null, tx, true, null, true, null, TRANSACTION_COMPLETED)); assertEquals(expected, eventLog.events); } public void testTxCreationCommit() throws Exception { assertEquals("Event log should be empty", Collections.emptyList(), eventLog.events); tm.begin(); Transaction tx = tm.getTransaction(); cache.put(fqn, "key", "value"); //expected Map data = new HashMap(); data.put("key", "value"); List expected = new ArrayList(); expected.add(new EventImpl(false, cache, null, null, null, tx, true, null, false, null, TRANSACTION_REGISTERED)); expected.add(new EventImpl(true, cache, null, null, fqn, tx, true, null, false, null, NODE_CREATED)); expected.add(new EventImpl(false, cache, null, null, fqn, tx, true, null, false, null, NODE_CREATED)); expected.add(new EventImpl(true, cache, PUT_DATA, Collections.emptyMap(), fqn, tx, true, null, false, null, NODE_MODIFIED)); expected.add(new EventImpl(false, cache, PUT_DATA, data, fqn, tx, true, null, false, null, NODE_MODIFIED)); assertEquals(expected, eventLog.events); tm.commit(); expected.add(new EventImpl(false, cache, null, null, null, tx, true, null, true, null, TRANSACTION_COMPLETED)); assertEquals(expected, eventLog.events); assertEquals("value", cache.get(fqn, "key")); } public void testTxCreationRollback() throws Exception { assertEquals("Event log should be empty", Collections.emptyList(), eventLog.events); tm.begin(); Transaction tx = tm.getTransaction(); cache.put(fqn, "key", "value"); //expected Map data = new HashMap(); data.put("key", "value"); List expected = new ArrayList(); expected.add(new EventImpl(false, cache, null, null, null, tx, true, null, false, null, TRANSACTION_REGISTERED)); expected.add(new EventImpl(true, cache, null, null, fqn, tx, true, null, false, null, NODE_CREATED)); expected.add(new EventImpl(false, cache, null, null, fqn, tx, true, null, false, null, NODE_CREATED)); expected.add(new EventImpl(true, cache, PUT_DATA, Collections.emptyMap(), fqn, tx, true, null, false, null, NODE_MODIFIED)); expected.add(new EventImpl(false, cache, PUT_DATA, data, fqn, tx, true, null, false, null, NODE_MODIFIED)); assertEquals(expected, eventLog.events); tm.rollback(); expected.add(new EventImpl(false, cache, null, null, null, tx, true, null, false, null, TRANSACTION_COMPLETED)); assertEquals(expected, eventLog.events); } public void testTxOnlyModification() throws Exception { assertEquals("Event log should be empty", Collections.emptyList(), eventLog.events); cache.put(fqn, "key", "value"); Map oldData = new HashMap(); oldData.put("key", "value"); // clear Event log eventLog.events.clear(); assertEquals("Event log should be empty", Collections.emptyList(), eventLog.events); // modify existing node tm.begin(); Transaction tx = tm.getTransaction(); cache.put(fqn, "key", "value2"); Map newData = new HashMap(); newData.put("key", "value2"); //expected List expected = new ArrayList(); expected.add(new EventImpl(false, cache, null, null, null, tx, true, null, false, null, TRANSACTION_REGISTERED)); expected.add(new EventImpl(true, cache, PUT_DATA, oldData, fqn, tx, true, null, false, null, NODE_MODIFIED)); expected.add(new EventImpl(false, cache, PUT_DATA, newData, fqn, tx, true, null, false, null, NODE_MODIFIED)); assertEquals(expected, eventLog.events); tm.commit(); expected.add(new EventImpl(false, cache, null, null, null, tx, true, null, true, null, TRANSACTION_COMPLETED)); assertEquals(expected, eventLog.events); } public void testTxOnlyRemoval() throws Exception { assertEquals("Event log should be empty", Collections.emptyList(), eventLog.events); cache.put(fqn, "key", "value"); Map oldData = new HashMap(); oldData.put("key", "value"); assertEquals("value", cache.get(fqn, "key")); // clear Event log eventLog.events.clear(); assertEquals("Event log should be empty", Collections.emptyList(), eventLog.events); // modify existing node tm.begin(); Transaction tx = tm.getTransaction(); cache.removeNode(fqn); //expected List expected = new ArrayList(); expected.add(new EventImpl(false, cache, null, null, null, tx, true, null, false, null, TRANSACTION_REGISTERED)); expected.add(new EventImpl(true, cache, null, oldData, fqn, tx, true, null, false, null, NODE_REMOVED)); expected.add(new EventImpl(false, cache, null, null, fqn, tx, true, null, false, null, NODE_REMOVED)); assertEquals(expected, eventLog.events); tm.commit(); expected.add(new EventImpl(false, cache, null, null, null, tx, true, null, true, null, TRANSACTION_COMPLETED)); assertEquals(expected, eventLog.events); // test that the node has in fact been removed. assertNull("Should be null", cache.getRoot().getChild(fqn)); } public void testTxRemoveData() throws Exception { assertEquals("Event log should be empty", Collections.emptyList(), eventLog.events); cache.put(fqn, "key", "value"); cache.put(fqn, "key2", "value2"); Map oldData = new HashMap(); oldData.put("key", "value"); oldData.put("key2", "value2"); // clear Event log eventLog.events.clear(); assertEquals("Event log should be empty", Collections.emptyList(), eventLog.events); // modify existing node tm.begin(); Transaction tx = tm.getTransaction(); cache.remove(fqn, "key2"); Map removedData = new HashMap(); removedData.put("key2", "value2"); //expected List expected = new ArrayList(); expected.add(new EventImpl(false, cache, null, null, null, tx, true, null, false, null, TRANSACTION_REGISTERED)); expected.add(new EventImpl(true, cache, REMOVE_DATA, oldData, fqn, tx, true, null, false, null, NODE_MODIFIED)); expected.add(new EventImpl(false, cache, REMOVE_DATA, removedData, fqn, tx, true, null, false, null, NODE_MODIFIED)); tm.commit(); expected.add(new EventImpl(false, cache, null, null, null, tx, true, null, true, null, TRANSACTION_COMPLETED)); assertEquals(expected, eventLog.events); assertEquals(expected, eventLog.events); } public void testTxMove() throws Exception { assertEquals("Event log should be empty", Collections.emptyList(), eventLog.events); Fqn newParent = Fqn.fromString("/a"); cache.put(fqn, "key", "value"); cache.put(newParent, "key", "value"); Node n1 = cache.getRoot().getChild(fqn); Node n2 = cache.getRoot().getChild(newParent); eventLog.events.clear();// clear events assertEquals("Event log should be empty", Collections.emptyList(), eventLog.events); tm.begin(); Transaction tx = tm.getTransaction(); cache.move(n1.getFqn(), n2.getFqn()); //expected Fqn newFqn = Fqn.fromRelativeElements(newParent, fqn.getLastElement()); List expected = new ArrayList(); expected.add(new EventImpl(false, cache, null, null, null, tx, true, null, false, null, TRANSACTION_REGISTERED)); expected.add(new EventImpl(true, cache, null, null, fqn, tx, true, newFqn, false, null, NODE_MOVED)); expected.add(new EventImpl(false, cache, null, null, fqn, tx, true, newFqn, false, null, NODE_MOVED)); assertEquals(expected, eventLog.events); tm.commit(); expected.add(new EventImpl(false, cache, null, null, null, tx, true, null, true, null, TRANSACTION_COMPLETED)); assertEquals(expected, eventLog.events); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/notifications/NodeMapDiffTest.java0000644000175000017500000001052210665031725031131 0ustar twernertwernerpackage org.jboss.cache.notifications; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.fail; import java.util.HashMap; import java.util.Map; import org.jboss.cache.util.Util; import org.testng.annotations.Test; /** * Tests the diffs between maps. * * @author Manik Surtani * @since 2.0.0 */ @Test(groups = {"functional"}) public class NodeMapDiffTest { public void testDataAdded() { Util.MapModifications expected = new Util.MapModifications(); expected.addedEntries.put("key", "value"); expected.addedEntries.put("key1", "value1"); Map pre = new HashMap(); pre.put("oldKey", "oldValue"); Map post = new HashMap(); post.putAll(pre); post.put("key", "value"); post.put("key1", "value1"); assertEquals(expected, Util.diffNodeData(pre, post)); } public void testDataRemoved() { Util.MapModifications expected = new Util.MapModifications(); expected.removedEntries.put("key", "value"); expected.removedEntries.put("key1", "value1"); Map post = new HashMap(); post.put("oldKey", "oldValue"); Map pre = new HashMap(); pre.putAll(post); pre.put("key", "value"); pre.put("key1", "value1"); assertEquals(expected, Util.diffNodeData(pre, post)); } public void testDataChanged() { Util.MapModifications expected = new Util.MapModifications(); expected.modifiedEntries.put("key", "value"); expected.modifiedEntries.put("key1", "value1"); Map pre = new HashMap(); pre.put("oldKey", "oldValue"); pre.put("key", "valueOLD"); pre.put("key1", "value1OLD"); Map post = new HashMap(); post.putAll(pre); post.put("key", "value"); post.put("key1", "value1"); assertEquals(expected, Util.diffNodeData(pre, post)); } public void testNullMaps() { try { Util.diffNodeData(null, null); fail("Expected NPE"); } catch (NullPointerException npe) { // expected } try { Util.diffNodeData(new HashMap(), null); fail("Expected NPE"); } catch (NullPointerException npe) { // expected } try { Util.diffNodeData(null, new HashMap()); fail("Expected NPE"); } catch (NullPointerException npe) { // expected } } public void testEmptyMaps() { Util.MapModifications expected = new Util.MapModifications(); Map pre = new HashMap(); Map post = new HashMap(); assertEquals(expected, Util.diffNodeData(pre, post)); } public void testNoChange() { Util.MapModifications expected = new Util.MapModifications(); Map pre = new HashMap(); pre.put("a", "b"); pre.put("c", "d"); pre.put("e", "f"); Map post = new HashMap(); post.put("a", "b"); post.put("c", "d"); post.put("e", "f"); assertEquals(expected, Util.diffNodeData(pre, post)); } public void testMultipleChanges() { Util.MapModifications expected = new Util.MapModifications(); expected.modifiedEntries.put("key", "value"); expected.modifiedEntries.put("key1", "value1"); expected.addedEntries.put("key2", "value2"); expected.addedEntries.put("key3", "value3"); expected.removedEntries.put("key4", "value4"); expected.removedEntries.put("key5", "value5"); Map pre = new HashMap(); pre.put("oldKey", "oldValue"); pre.put("key", "valueOLD"); pre.put("key1", "value1OLD"); pre.put("key4", "value4"); pre.put("key5", "value5"); Map post = new HashMap(); post.put("oldKey", "oldValue"); post.put("key", "value"); post.put("key1", "value1"); post.put("key2", "value2"); post.put("key3", "value3"); assertEquals(expected, Util.diffNodeData(pre, post)); } } ././@LongLink0000000000000000000000000000015300000000000011564 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/notifications/BuddyGroupChangeNotificationTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/notifications/BuddyGroupChangeNotificationTes0000644000175000017500000000743011017455042033450 0ustar twernertwernerpackage org.jboss.cache.notifications; import org.jboss.cache.Cache; import org.jboss.cache.CacheFactory; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.buddyreplication.BuddyGroup; import org.jboss.cache.buddyreplication.BuddyReplicationTestsBase; import org.jboss.cache.config.BuddyReplicationConfig; import org.jboss.cache.config.Configuration; import org.jboss.cache.notifications.annotation.BuddyGroupChanged; import org.jboss.cache.notifications.annotation.CacheListener; import org.jboss.cache.notifications.annotation.ViewChanged; import org.jboss.cache.notifications.event.BuddyGroupChangedEvent; import org.jboss.cache.notifications.event.ViewChangedEvent; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.concurrent.CountDownLatch; /** * @author Manik Surtani (manik@jboss.org) * @since 2.1.0 */ @Test(groups = "functional") public class BuddyGroupChangeNotificationTest extends BuddyReplicationTestsBase { Cache c1, c2, c3; Listener listener; static CountDownLatch latch1 = new CountDownLatch(1); static CountDownLatch latch2 = new CountDownLatch(1); static boolean stage2 = false; static boolean notificationsReceived = true; @BeforeMethod public void setUp() throws CloneNotSupportedException { CacheFactory cf = new DefaultCacheFactory(); c1 = cf.createCache(false); c1.getConfiguration().setCacheMode(Configuration.CacheMode.REPL_SYNC); BuddyReplicationConfig brc = new BuddyReplicationConfig(); brc.setEnabled(true); c1.getConfiguration().setBuddyReplicationConfig(brc); c2 = cf.createCache(c1.getConfiguration().clone(), false); c3 = cf.createCache(c1.getConfiguration().clone(), false); c1.start(); c2.start(); c3.start(); // make sure views are received and groups are formed first TestingUtil.blockUntilViewsReceived(60000, c1, c2, c3); Cache[] caches = new Cache[]{c1, c2, c3}; listener = new Listener(caches); c2.addCacheListener(listener); } @AfterMethod public void tearDown() { TestingUtil.killCaches(c1, c2, c3); } @Test(timeOut = 60000) public void testChangingGroups() throws InterruptedException { // initial state assertIsBuddy(c1, c2, true); assertIsBuddy(c2, c3, true); assertIsBuddy(c3, c1, true); // kill c3 c3.stop(); latch1.await(); assertIsBuddy(c1, c2, true); assertIsBuddy(c2, c1, true); stage2 = true; c3.start(); latch2.await(); assertIsBuddy(c1, c2, true); assertIsBuddy(c2, c3, true); assertIsBuddy(c3, c1, true); assert notificationsReceived; } @CacheListener public static class Listener { Cache[] caches; int numActiveCaches; public Listener(Cache[] caches) { this.caches = caches; } @ViewChanged public void viewChanged(ViewChangedEvent e) { numActiveCaches = e.getNewView().getMembers().size(); } @BuddyGroupChanged public void buddyChanged(BuddyGroupChangedEvent e) { System.out.println("Received event " + e); if (!e.isPre()) { BuddyGroup bg = e.getBuddyGroup(); boolean passed = bg.getDataOwner().equals(caches[1].getLocalAddress()) && bg.getBuddies().size() == 1 && bg.getBuddies().contains(caches[(numActiveCaches == 3) ? 2 : 0].getLocalAddress()); notificationsReceived = notificationsReceived && passed; if (stage2) latch2.countDown(); else latch1.countDown(); } } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/notifications/RemoteCacheListenerTest.java0000644000175000017500000007345711026047152032713 0ustar twernertwerner/* * JBoss, Home of Professional Open Source. * Copyright 2006, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.cache.notifications; import org.jboss.cache.Cache; import org.jboss.cache.CacheFactory; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.Node; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.ComponentRegistry; import org.jboss.cache.lock.IsolationLevel; import org.jboss.cache.notifications.event.Event; import static org.jboss.cache.notifications.event.Event.Type.*; import org.jboss.cache.notifications.event.EventImpl; import org.jboss.cache.notifications.event.NodeModifiedEvent; import static org.jboss.cache.notifications.event.NodeModifiedEvent.ModificationType.*; import org.jboss.cache.util.TestingUtil; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.Transaction; import javax.transaction.TransactionManager; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; /** * Remote conterpart of CacheListenerTest. Main difference is event is originating as local. * * @since 2.0.0 */ @Test(groups = "functional") public class RemoteCacheListenerTest { protected boolean optLocking = false; private Cache cache1, cache2; @SuppressWarnings("unused") private TransactionManager tm1, tm2; private EventLog eventLog1 = new EventLog(), eventLog2 = new EventLog(); private Fqn fqn = Fqn.fromString("/test"); @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { Configuration c = new Configuration(); c.setCacheMode(Configuration.CacheMode.REPL_SYNC); c.setIsolationLevel(IsolationLevel.REPEATABLE_READ); if (optLocking) c.setNodeLockingScheme(Configuration.NodeLockingScheme.OPTIMISTIC); c.setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); // we need this because notifications emitted by the notification interceptor are done during the commit call. // If we want to check notifications on remote nodes we need to make sure the commit completes before we test anything. c.setSyncCommitPhase(true); // more time to help with debugging c.setSyncReplTimeout(60000); CacheFactory instance = new DefaultCacheFactory(); cache1 = instance.createCache(c); cache2 = instance.createCache(c.clone()); eventLog1.events.clear(); eventLog2.events.clear(); cache1.addCacheListener(eventLog1); cache2.addCacheListener(eventLog2); tm1 = cache1.getConfiguration().getRuntimeConfig().getTransactionManager(); tm2 = cache2.getConfiguration().getRuntimeConfig().getTransactionManager(); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { TestingUtil.killCaches(cache1, cache2); } /** * Make sure the 2 caches have different component instances */ public void testSeparateNotifiersAndListeners() { assert cache1 != cache2; ComponentRegistry cr1, cr2; cr1 = TestingUtil.extractComponentRegistry(cache1); cr2 = TestingUtil.extractComponentRegistry(cache2); assert cr1 != cr2; assert cr1.getComponent(Notifier.class) != cr2.getComponent(Notifier.class); assert eventLog1 != eventLog2; assert cache1.getLocalAddress() != cache2.getLocalAddress(); CacheSPI spi1, spi2; spi1 = (CacheSPI) cache1; spi2 = (CacheSPI) cache2; assert spi1.getRPCManager() != spi2.getRPCManager(); assert TestingUtil.extractField(spi1.getRPCManager(), "channel") != TestingUtil.extractField(spi2.getRPCManager(), "channel"); } // simple tests first public void testCreation() throws Exception { assertEquals("Event log should be empty", Collections.emptyList(), eventLog2.events); cache1.put(fqn, "key", "value"); Map data = new HashMap(); data.put("key", "value"); //expectedRemote List expected = new ArrayList(); if (optLocking) expected.add(new EventImpl(false, cache1, null, null, null, null, true, null, false, null, TRANSACTION_REGISTERED)); expected.add(new EventImpl(true, cache1, null, null, fqn, null, true, null, false, null, NODE_CREATED)); expected.add(new EventImpl(false, cache1, null, null, fqn, null, true, null, false, null, NODE_CREATED)); expected.add(new EventImpl(true, cache1, PUT_DATA, Collections.emptyMap(), fqn, null, true, null, false, null, NODE_MODIFIED)); expected.add(new EventImpl(false, cache1, PUT_DATA, data, fqn, null, true, null, false, null, NODE_MODIFIED)); if (optLocking) { expected.add(new EventImpl(false, cache1, null, null, null, null, true, null, true, null, TRANSACTION_COMPLETED)); eventLog1.scrubImplicitTransactions(); eventLog2.scrubImplicitTransactions(); } assertEquals("Local events not as expected", expected, eventLog1.events); //expectedRemote setCache(cache2, expected); markOriginRemote(expected); assertEquals("Remote events not as expected", expected, eventLog2.events); assertEquals("value", cache1.get(fqn, "key")); } public void testNonexistentRemove() throws Exception { cache1.removeNode("/does/not/exist"); List expected = new ArrayList(); if (optLocking) { expected.add(new EventImpl(false, cache1, null, null, null, null, true, null, false, null, TRANSACTION_REGISTERED)); expected.add(new EventImpl(false, cache1, null, null, null, null, true, null, true, null, TRANSACTION_COMPLETED)); eventLog1.scrubImplicitTransactions(); eventLog2.scrubImplicitTransactions(); } assertEquals(expected, eventLog1.events); setCache(cache2, expected); markOriginRemote(expected); assertEquals(expected, eventLog2.events); } public void testOnlyModification() throws Exception { assertNull(cache1.get(fqn, "key")); assertNull(cache2.get(fqn, "key")); cache1.put(fqn, "key", "value"); Map oldData = new HashMap(); oldData.put("key", "value"); assertEquals("value", cache1.get(fqn, "key")); assertEquals("value", cache2.get(fqn, "key")); // clear event log eventLog1.events.clear(); eventLog2.events.clear(); assertEquals("Event log should be empty", Collections.emptyList(), eventLog1.events); assertEquals("Event log should be empty", Collections.emptyList(), eventLog2.events); // modify existing node cache1.put(fqn, "key", "value2"); Map newData = new HashMap(); newData.put("key", "value2"); List expected = new ArrayList(); if (optLocking) expected.add(new EventImpl(false, cache1, null, null, null, null, true, null, false, null, TRANSACTION_REGISTERED)); expected.add(new EventImpl(true, cache1, PUT_DATA, oldData, fqn, null, true, null, false, null, NODE_MODIFIED)); expected.add(new EventImpl(false, cache1, PUT_DATA, newData, fqn, null, true, null, false, null, NODE_MODIFIED)); if (optLocking) { expected.add(new EventImpl(false, cache1, null, null, null, null, true, null, true, null, TRANSACTION_COMPLETED)); eventLog1.scrubImplicitTransactions(); eventLog2.scrubImplicitTransactions(); } assertEquals("Local events not as expected", expected, eventLog1.events); //expectedRemote setCache(cache2, expected); markOriginRemote(expected); assertEquals("Remote events not as expected", expected, eventLog2.events); } public void testOnlyRemoval() throws Exception { assertEquals("Event log should be empty", Collections.emptyList(), eventLog2.events); cache1.put(fqn, "key", "value"); Map oldData = new HashMap(); oldData.put("key", "value"); assertEquals("value", cache1.get(fqn, "key")); assertEquals("value", cache2.get(fqn, "key")); // clear event log eventLog1.events.clear(); eventLog2.events.clear(); assertEquals("Event log should be empty", Collections.emptyList(), eventLog1.events); assertEquals("Event log should be empty", Collections.emptyList(), eventLog2.events); // modify existing node cache1.removeNode(fqn); List expected = new ArrayList(); if (optLocking) expected.add(new EventImpl(false, cache1, null, null, null, null, true, null, false, null, TRANSACTION_REGISTERED)); expected.add(new EventImpl(true, cache1, null, oldData, fqn, null, true, null, false, null, NODE_REMOVED)); expected.add(new EventImpl(false, cache1, null, null, fqn, null, true, null, false, null, NODE_REMOVED)); if (optLocking) { expected.add(new EventImpl(false, cache1, null, null, null, null, true, null, true, null, TRANSACTION_COMPLETED)); eventLog1.scrubImplicitTransactions(); eventLog2.scrubImplicitTransactions(); } assertEquals("Local events not as expected", expected, eventLog1.events); //expectedRemote setCache(cache2, expected); markOriginRemote(expected); assertEquals("Remote events not as expected", expected, eventLog2.events); // test that the node has in fact been removed. assertNull("Should be null", cache1.getRoot().getChild(fqn)); assertNull("Should be null", cache2.getRoot().getChild(fqn)); } public void testRemoveData() throws Exception { assertEquals("Event log should be empty", Collections.emptyList(), eventLog2.events); cache1.put(fqn, "key", "value"); cache1.put(fqn, "key2", "value2"); Map oldData = new HashMap(); oldData.put("key", "value"); oldData.put("key2", "value2"); // clear event log eventLog1.events.clear(); eventLog2.events.clear(); assertEquals("Event log should be empty", Collections.emptyList(), eventLog1.events); assertEquals("Event log should be empty", Collections.emptyList(), eventLog2.events); // modify existing node cache1.remove(fqn, "key2"); Map removed = new HashMap(); removed.put("key2", "value2"); List expected = new ArrayList(); if (optLocking) expected.add(new EventImpl(false, cache1, null, null, null, null, true, null, false, null, TRANSACTION_REGISTERED)); expected.add(new EventImpl(true, cache1, REMOVE_DATA, oldData, fqn, null, true, null, false, null, NODE_MODIFIED)); expected.add(new EventImpl(false, cache1, REMOVE_DATA, removed, fqn, null, true, null, false, null, NODE_MODIFIED)); if (optLocking) { expected.add(new EventImpl(false, cache1, null, null, null, null, true, null, true, null, TRANSACTION_COMPLETED)); eventLog1.scrubImplicitTransactions(); eventLog2.scrubImplicitTransactions(); } assertEquals("Local events not as expected", expected, eventLog1.events); //expectedRemote setCache(cache2, expected); markOriginRemote(expected); assertEquals("Remote events not as expected", expected, eventLog2.events); } public void testPutMap() throws Exception { assertEquals("Event log should be empty", Collections.emptyList(), eventLog2.events); Map oldData = new HashMap(); oldData.put("key", "value"); oldData.put("key2", "value2"); assertNull(cache1.getRoot().getChild(fqn)); assertNull(cache2.getRoot().getChild(fqn)); // clear event log eventLog1.events.clear(); eventLog2.events.clear(); assertEquals("Event log should be empty", Collections.emptyList(), eventLog1.events); assertEquals("Event log should be empty", Collections.emptyList(), eventLog2.events); cache1.put(fqn, oldData); List expected = new ArrayList(); if (optLocking) expected.add(new EventImpl(false, cache1, null, null, null, null, true, null, false, null, TRANSACTION_REGISTERED)); expected.add(new EventImpl(true, cache1, null, null, fqn, null, true, null, false, null, NODE_CREATED)); expected.add(new EventImpl(false, cache1, null, null, fqn, null, true, null, false, null, NODE_CREATED)); expected.add(new EventImpl(true, cache1, PUT_MAP, Collections.emptyMap(), fqn, null, true, null, false, null, NODE_MODIFIED)); expected.add(new EventImpl(false, cache1, PUT_MAP, oldData, fqn, null, true, null, false, null, NODE_MODIFIED)); if (optLocking) { expected.add(new EventImpl(false, cache1, null, null, null, null, true, null, true, null, TRANSACTION_COMPLETED)); eventLog1.scrubImplicitTransactions(); eventLog2.scrubImplicitTransactions(); } assertEquals("Local events not as expected", expected, eventLog1.events); //expectedRemote setCache(cache2, expected); markOriginRemote(expected); assertEquals("Remote events not as expected", expected, eventLog2.events); } public void testMove() { assertEquals("Event log should be empty", Collections.emptyList(), eventLog2.events); Fqn newParent = Fqn.fromString("/a"); cache1.put(fqn, "key", "value"); cache1.put(newParent, "key", "value"); Node n1 = cache1.getRoot().getChild(fqn); Node n2 = cache1.getRoot().getChild(newParent); eventLog1.events.clear(); eventLog2.events.clear();// clear events assertEquals("Event log should be empty", Collections.emptyList(), eventLog1.events); assertEquals("Event log should be empty", Collections.emptyList(), eventLog2.events); cache1.move(n1.getFqn(), n2.getFqn()); Fqn newFqn = Fqn.fromRelativeElements(newParent, fqn.getLastElement()); List expected = new ArrayList(); if (optLocking) expected.add(new EventImpl(false, cache1, null, null, null, null, true, null, false, null, TRANSACTION_REGISTERED)); expected.add(new EventImpl(true, cache1, null, null, fqn, null, true, newFqn, false, null, NODE_MOVED)); expected.add(new EventImpl(false, cache1, null, null, fqn, null, true, newFqn, false, null, NODE_MOVED)); if (optLocking) { expected.add(new EventImpl(false, cache1, null, null, null, null, true, null, true, null, TRANSACTION_COMPLETED)); eventLog1.scrubImplicitTransactions(); eventLog2.scrubImplicitTransactions(); } assertEquals("Local events not as expected", expected, eventLog1.events); //expectedRemote setCache(cache2, expected); markOriginRemote(expected); assertEquals("Remote events not as expected", expected, eventLog2.events); } // -- now the transactional ones public void testTxCreationCommit() throws Exception { assertEquals("Event log should be empty", Collections.emptyList(), eventLog2.events); tm1.begin(); Transaction tx = tm1.getTransaction(); cache1.put(fqn, "key", "value"); Map data = new HashMap(); data.put("key", "value"); List expected = new ArrayList(); expected.add(new EventImpl(false, cache1, null, null, null, tx, true, null, false, null, TRANSACTION_REGISTERED)); expected.add(new EventImpl(true, cache1, null, null, fqn, tx, true, null, false, null, NODE_CREATED)); expected.add(new EventImpl(false, cache1, null, null, fqn, tx, true, null, false, null, NODE_CREATED)); expected.add(new EventImpl(true, cache1, PUT_DATA, Collections.emptyMap(), fqn, tx, true, null, false, null, NODE_MODIFIED)); expected.add(new EventImpl(false, cache1, PUT_DATA, data, fqn, tx, true, null, false, null, NODE_MODIFIED)); assertEquals(expected, eventLog1.events); assertTrue(eventLog2.events.isEmpty()); tm1.commit(); expected.add(new EventImpl(false, cache1, null, null, null, tx, true, null, true, null, TRANSACTION_COMPLETED)); assertEquals(expected, eventLog1.events); setCache(cache2, expected); markOriginRemote(expected); scrubTransactions(expected); eventLog2.scrubImplicitTransactions(); assertEquals(expected, eventLog2.events); assertEquals("value", cache1.get(fqn, "key")); assertEquals("value", cache2.get(fqn, "key")); } public void testTxNonexistentRemove() throws Exception { tm1.begin(); Transaction tx = tm1.getTransaction(); cache1.removeNode("/does/not/exist"); tm1.commit(); List expected = new ArrayList(); expected.add(new EventImpl(false, cache1, null, null, null, tx, true, null, false, null, TRANSACTION_REGISTERED)); expected.add(new EventImpl(false, cache1, null, null, null, tx, true, null, true, null, TRANSACTION_COMPLETED)); assertEquals(expected, eventLog1.events); setCache(cache2, expected); markOriginRemote(expected); scrubTransactions(expected); eventLog2.scrubImplicitTransactions(); assertEquals(expected, eventLog2.events); } public void testTxCreationRollback() throws Exception { assertEquals("Event log should be empty", Collections.emptyList(), eventLog2.events); tm1.begin(); Transaction tx = tm1.getTransaction(); cache1.put(fqn, "key", "value"); Map data = new HashMap(); data.put("key", "value"); List expected = new ArrayList(); expected.add(new EventImpl(false, cache1, null, null, null, tx, true, null, false, null, TRANSACTION_REGISTERED)); expected.add(new EventImpl(true, cache1, null, null, fqn, tx, true, null, false, null, NODE_CREATED)); expected.add(new EventImpl(false, cache1, null, null, fqn, tx, true, null, false, null, NODE_CREATED)); expected.add(new EventImpl(true, cache1, PUT_DATA, Collections.emptyMap(), fqn, tx, true, null, false, null, NODE_MODIFIED)); expected.add(new EventImpl(false, cache1, PUT_DATA, data, fqn, tx, true, null, false, null, NODE_MODIFIED)); assertEquals(expected, eventLog1.events); assertTrue(eventLog2.events.isEmpty()); tm1.rollback(); expected.add(new EventImpl(false, cache1, null, null, null, tx, true, null, false, null, TRANSACTION_COMPLETED)); assertEquals(expected, eventLog1.events); assertTrue(eventLog2.events.isEmpty()); assertNull(cache1.get(fqn, "key")); assertNull(cache2.get(fqn, "key")); } public void testTxOnlyModification() throws Exception { assertNull(cache1.get(fqn, "key")); assertNull(cache2.get(fqn, "key")); cache1.put(fqn, "key", "value"); Map oldData = new HashMap(); oldData.put("key", "value"); assertEquals("value", cache1.get(fqn, "key")); assertEquals("value", cache2.get(fqn, "key")); // clear event log eventLog1.events.clear(); eventLog2.events.clear(); assertEquals("Event log should be empty", Collections.emptyList(), eventLog1.events); assertEquals("Event log should be empty", Collections.emptyList(), eventLog2.events); // modify existing node tm1.begin(); Transaction tx = tm1.getTransaction(); Map newData = new HashMap(); newData.put("key", "value2"); cache1.put(fqn, "key", "value2"); List expected = new ArrayList(); expected.add(new EventImpl(false, cache1, null, null, null, tx, true, null, false, null, TRANSACTION_REGISTERED)); expected.add(new EventImpl(true, cache1, PUT_DATA, oldData, fqn, tx, true, null, false, null, NODE_MODIFIED)); expected.add(new EventImpl(false, cache1, PUT_DATA, newData, fqn, tx, true, null, false, null, NODE_MODIFIED)); assertEquals(expected, eventLog1.events); assertEquals("Events log should be empty until commit time", 0, eventLog2.events.size()); tm1.commit(); expected.add(new EventImpl(false, cache1, null, null, null, tx, true, null, true, null, TRANSACTION_COMPLETED)); assertEquals(expected, eventLog1.events); setCache(cache2, expected); markOriginRemote(expected); scrubTransactions(expected); eventLog2.scrubImplicitTransactions(); assertEquals(expected, eventLog2.events); } public void testTxOnlyRemoval() throws Exception { assertEquals("Event log should be empty", Collections.emptyList(), eventLog2.events); cache1.put(fqn, "key", "value"); Map oldData = new HashMap(); oldData.put("key", "value"); assertEquals("value", cache1.get(fqn, "key")); assertEquals("value", cache2.get(fqn, "key")); // clear event log eventLog1.events.clear(); eventLog2.events.clear(); assertEquals("Event log should be empty", Collections.emptyList(), eventLog1.events); assertEquals("Event log should be empty", Collections.emptyList(), eventLog2.events); // modify existing node tm1.begin(); Transaction tx = tm1.getTransaction(); cache1.removeNode(fqn); List expected = new ArrayList(); expected.add(new EventImpl(false, cache1, null, null, null, tx, true, null, false, null, TRANSACTION_REGISTERED)); expected.add(new EventImpl(true, cache1, null, oldData, fqn, tx, true, null, false, null, NODE_REMOVED)); expected.add(new EventImpl(false, cache1, null, null, fqn, tx, true, null, false, null, NODE_REMOVED)); assertEquals(expected, eventLog1.events); assertEquals("Events log should be empty until commit time", 0, eventLog2.events.size()); tm1.commit(); expected.add(new EventImpl(false, cache1, null, null, null, tx, true, null, true, null, TRANSACTION_COMPLETED)); assertEquals(expected, eventLog1.events); setCache(cache2, expected); markOriginRemote(expected); scrubTransactions(expected); eventLog2.scrubImplicitTransactions(); assertEquals(expected, eventLog2.events); // test that the node has in fact been removed. assertNull("Should be null", cache1.getRoot().getChild(fqn)); assertNull("Should be null", cache2.getRoot().getChild(fqn)); } public void testTxRemoveData() throws Exception { assertEquals("Event log should be empty", Collections.emptyList(), eventLog2.events); cache1.put(fqn, "key", "value"); cache1.put(fqn, "key2", "value2"); Map oldData = new HashMap(); oldData.put("key", "value"); oldData.put("key2", "value2"); // clear event log eventLog1.events.clear(); eventLog2.events.clear(); assertEquals("Event log should be empty", Collections.emptyList(), eventLog1.events); assertEquals("Event log should be empty", Collections.emptyList(), eventLog2.events); // modify existing node tm1.begin(); Transaction tx = tm1.getTransaction(); Map removed = new HashMap(); removed.put("key2", "value2"); cache1.remove(fqn, "key2"); List expected = new LinkedList(); expected.add(new EventImpl(false, cache1, null, null, null, tx, true, null, false, null, TRANSACTION_REGISTERED)); expected.add(new EventImpl(true, cache1, REMOVE_DATA, oldData, fqn, tx, true, null, false, null, NODE_MODIFIED)); expected.add(new EventImpl(false, cache1, REMOVE_DATA, removed, fqn, tx, true, null, false, null, NODE_MODIFIED)); assertEquals(expected, eventLog1.events); assertEquals("Events log should be empty until commit time", 0, eventLog2.events.size()); tm1.commit(); expected.add(new EventImpl(false, cache1, null, null, null, tx, true, null, true, null, TRANSACTION_COMPLETED)); assertEquals(expected, eventLog1.events); setCache(cache2, expected); markOriginRemote(expected); scrubTransactions(expected); eventLog2.scrubImplicitTransactions(); assertEquals(expected, eventLog2.events); } public void testTxMove() throws Exception { assertEquals("Event log should be empty", Collections.emptyList(), eventLog2.events); Fqn newParent = Fqn.fromString("/a"); cache1.put(fqn, "key", "value"); cache1.put(newParent, "key", "value"); Node n1 = cache1.getRoot().getChild(fqn); Node n2 = cache1.getRoot().getChild(newParent); eventLog1.events.clear(); eventLog2.events.clear();// clear events assertEquals("Event log should be empty", Collections.emptyList(), eventLog1.events); assertEquals("Event log should be empty", Collections.emptyList(), eventLog2.events); tm1.begin(); Transaction tx = tm1.getTransaction(); Fqn newFqn = Fqn.fromRelativeElements(newParent, fqn.getLastElement()); cache1.move(n1.getFqn(), n2.getFqn()); List expected = new ArrayList(); expected.add(new EventImpl(false, cache1, null, null, null, tx, true, null, false, null, TRANSACTION_REGISTERED)); expected.add(new EventImpl(true, cache1, null, null, fqn, tx, true, newFqn, false, null, NODE_MOVED)); expected.add(new EventImpl(false, cache1, null, null, fqn, tx, true, newFqn, false, null, NODE_MOVED)); assertEquals(expected, eventLog1.events); assertEquals("Events log should be empty until commit time", 0, eventLog2.events.size()); tm1.commit(); expected.add(new EventImpl(false, cache1, null, null, null, tx, true, null, true, null, TRANSACTION_COMPLETED)); assertEquals(expected, eventLog1.events); setCache(cache2, expected); markOriginRemote(expected); scrubTransactions(expected); eventLog2.scrubImplicitTransactions(); assertEquals(expected, eventLog2.events); } @Test(enabled = true) public void testStateTransfer() throws Exception { // first stop cache2 TestingUtil.killCaches(cache2); // wait till cache2 has disappeared. TestingUtil.blockUntilViewsReceived(5000, false, cache1); // get some state in cache1 Fqn fqnA = Fqn.fromString("/a"); Fqn fqnB = Fqn.fromString("/a/b"); Map data = Collections.singletonMap("k", "v"); cache1.put(fqnA, data); cache1.put(fqnB, data); // create cache2 CacheFactory instance = new DefaultCacheFactory(); cache2 = instance.createCache(cache1.getConfiguration().clone(), false); cache2.create(); eventLog2.events.clear(); cache2.addCacheListener(eventLog2); cache2.start(); // should initiate a state transfer // wait until cache2 has joined the cluster TestingUtil.blockUntilViewsReceived(5000, cache1, cache2); List expected = new ArrayList(); // public EventImpl(boolean pre, Cache cache, ModificationType modificationType, Map data, Fqn fqn, // Transaction transaction, boolean originLocal, Fqn targetFqn, boolean successful, View newView, Type type) expected.add(new EventImpl(true, cache2, null, null, Fqn.ROOT, null, false, null, false, null, NODE_CREATED)); expected.add(new EventImpl(false, cache2, null, null, Fqn.ROOT, null, false, null, false, null, NODE_CREATED)); expected.add(new EventImpl(true, cache2, null, null, fqnA, null, false, null, false, null, NODE_CREATED)); expected.add(new EventImpl(false, cache2, null, null, fqnA, null, false, null, false, null, NODE_CREATED)); expected.add(new EventImpl(true, cache2, NodeModifiedEvent.ModificationType.PUT_MAP, Collections.emptyMap(), fqnA, null, false, null, false, null, NODE_MODIFIED)); expected.add(new EventImpl(false, cache2, NodeModifiedEvent.ModificationType.PUT_MAP, data, fqnA, null, false, null, false, null, NODE_MODIFIED)); expected.add(new EventImpl(true, cache2, null, null, fqnB, null, false, null, false, null, NODE_CREATED)); expected.add(new EventImpl(false, cache2, null, null, fqnB, null, false, null, false, null, NODE_CREATED)); expected.add(new EventImpl(true, cache2, NodeModifiedEvent.ModificationType.PUT_MAP, Collections.emptyMap(), fqnB, null, false, null, false, null, NODE_MODIFIED)); expected.add(new EventImpl(false, cache2, NodeModifiedEvent.ModificationType.PUT_MAP, data, fqnB, null, false, null, false, null, NODE_MODIFIED)); scrubTransactions(expected); if (optLocking) eventLog2.scrubImplicitTransactions(); assertEquals(expected, eventLog2.events); } private void setCache(Cache c, List l) { for (Event e : l) ((EventImpl) e).setCache(c); } private void markOriginRemote(List l) { for (Event e : l) ((EventImpl) e).setOriginLocal(false); } private void scrubTransactions(List l) { for (Event e : l) ((EventImpl) e).setTransaction(null); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/notifications/EventLog.java0000644000175000017500000000172411027753345027706 0ustar twernertwernerpackage org.jboss.cache.notifications; import org.jboss.cache.notifications.annotation.*; import org.jboss.cache.notifications.event.Event; import org.jboss.cache.notifications.event.EventImpl; import java.util.ArrayList; import java.util.List; @CacheListener public class EventLog { public final List events = new ArrayList(); @NodeCreated @NodeRemoved @NodeModified @NodeVisited @NodeMoved @TransactionCompleted @TransactionRegistered @NodeEvicted @NodePassivated @NodeActivated public void callback(Event e) { events.add(e); } public String toString() { return "EventLog{" + "events=" + events + '}'; } /** * Done when we don't have a Transaction reference to compare with, e.g., when using implicit transactions in * opt locking. */ public void scrubImplicitTransactions() { for (Event e : events) ((EventImpl) e).setTransaction(null); } }././@LongLink0000000000000000000000000000014500000000000011565 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/notifications/ConcurrentNotificationTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/notifications/ConcurrentNotificationTest.java0000644000175000017500000001011210732232543033475 0ustar twernertwernerpackage org.jboss.cache.notifications; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.Cache; import org.jboss.cache.CacheFactory; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.notifications.annotation.CacheListener; import org.jboss.cache.notifications.annotation.NodeCreated; import org.jboss.cache.notifications.annotation.NodeModified; import org.jboss.cache.notifications.annotation.NodeRemoved; import org.jboss.cache.notifications.annotation.NodeVisited; import org.jboss.cache.notifications.event.Event; import static org.testng.AssertJUnit.assertEquals; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.LinkedList; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicInteger; /** * @author Manik Surtani * @since 2.0.0 */ @Test(groups = {"functional"}) public class ConcurrentNotificationTest { private Cache cache; private Listener listener; private Fqn fqn = Fqn.fromString("/a/b/c"); private static final Log log = LogFactory.getLog(ConcurrentNotificationTest.class); @BeforeMethod(alwaysRun = true) public void setUp() { CacheFactory instance = new DefaultCacheFactory(); cache = instance.createCache(); listener = new Listener(); cache.addCacheListener(listener); } @AfterMethod(alwaysRun = true) public void tearDown() { cache.stop(); } public void testNulls() { cache.put(fqn, "key", null); cache.put(fqn, null, "value"); } public void testThreads() throws Exception { Thread workers[] = new Thread[20]; final List exceptions = new LinkedList(); final int loops = 100; final CountDownLatch latch = new CountDownLatch(1); for (int i = 0; i < workers.length; i++) { workers[i] = new Thread() { public void run() { try { latch.await(); } catch (InterruptedException e) { } for (int j = 0; j < loops; j++) { try { cache.put(fqn, "key", "value"); } catch (Exception e) { log.error("Exception doing put in loop " + j, e); exceptions.add(new Exception("Caused on thread " + getName() + " in loop " + j + " when doing a put()", e)); } try { cache.remove(fqn, "key"); } catch (Exception e) { log.error("Exception doing remove in loop " + j, e); exceptions.add(new Exception("Caused on thread " + getName() + " in loop " + j + " when doing a remove()", e)); } try { cache.get(fqn, "key"); } catch (Exception e) { log.error("Exception doing get in loop " + j, e); exceptions.add(new Exception("Caused on thread " + getName() + " in loop " + j + " when doing a get()", e)); } } } }; workers[i].start(); } latch.countDown(); for (Thread t : workers) t.join(); for (Exception e : exceptions) throw e; assertEquals(3 * loops * workers.length + 3, listener.counter.get()); } @CacheListener public class Listener { private AtomicInteger counter = new AtomicInteger(0); @NodeModified @NodeRemoved @NodeVisited @NodeCreated public void catchEvent(Event e) { if (e.isPre()) counter.getAndIncrement(); } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/notifications/NotifierTest.java0000644000175000017500000002746511015552370030604 0ustar twernertwernerpackage org.jboss.cache.notifications; import static org.easymock.EasyMock.*; import org.testng.annotations.Test; import org.testng.annotations.BeforeMethod; import org.jboss.cache.Cache; import org.jboss.cache.InvocationContext; import org.jboss.cache.Fqn; import org.jboss.cache.buddyreplication.BuddyGroup; import org.jboss.cache.notifications.annotation.*; import org.jboss.cache.notifications.event.*; import org.jgroups.View; import javax.transaction.Transaction; import java.util.Map; import java.util.HashMap; /** * Tester class for {@link org.jboss.cache.notifications.NotifierImpl}. * * @author Mircea.Markus@jboss.com * @since 2.2 */ @Test(groups = "unit") public class NotifierTest { private NotifierImpl notifier; private Cache cache; private InvocationContext ctx; private AllEventsListener allEventsListener; private Fqn fqn = Fqn.fromString("/a/b/c"); @BeforeMethod public void setUp() { cache = createNiceMock(Cache.class); notifier = new NotifierImpl(cache); ctx = new InvocationContext(); allEventsListener = new AllEventsListener(); notifier.addCacheListener(allEventsListener); } public void testNotifyNodeCreated() { assert allEventsListener.nodeCreatedEvent == null; notifier.notifyNodeCreated(fqn, true, ctx); assert allEventsListener.nodeCreatedEvent != null; assert allEventsListener.nodeCreatedEvent.getType() == Event.Type.NODE_CREATED; } public void testShouldNotifyOnNodeModified() { assert notifier.shouldNotifyOnNodeModified(); notifier.destroy(); assert !notifier.shouldNotifyOnNodeModified(); } public void testNotifyNodeModified() { assert allEventsListener.nodeModifiedEvent == null; Map expected = new HashMap(); expected.put("k", "v"); notifier.notifyNodeModified(fqn, true, NodeModifiedEvent.ModificationType.PUT_DATA, expected, ctx); assert notifier.getNodeModifiedListeners().size() == 1; assert allEventsListener.nodeModifiedEvent != null; assert allEventsListener.nodeModifiedEvent.getData().equals(expected); assert allEventsListener.nodeModifiedEvent.getModificationType() == NodeModifiedEvent.ModificationType.PUT_DATA; } public void testNotifyNodeRemoved() { assert allEventsListener.nodeRemoveEvent == null; Map data = new HashMap(); data.put("k", "v"); notifier.notifyNodeRemoved(fqn, true, data, ctx); assert allEventsListener.nodeRemoveEvent != null; assert allEventsListener.nodeRemoveEvent.getData().equals(data); assert allEventsListener.nodeRemoveEvent.getType() == Event.Type.NODE_REMOVED; } public void testNotifyNodeVisited() { assert allEventsListener.nodeVisistedEvent == null; notifier.notifyNodeVisited(fqn, true, ctx); assert allEventsListener.nodeVisistedEvent != null; assert allEventsListener.nodeVisistedEvent.getType() == Event.Type.NODE_VISITED; } public void testNotifyNodeMoved() { assert allEventsListener.nodeMovedEvent == null; Fqn second = Fqn.fromString("/a/s/f"); notifier.notifyNodeMoved(fqn, second, true, ctx); assert allEventsListener.nodeMovedEvent != null; assert allEventsListener.nodeMovedEvent.getFqn().equals(fqn); assert allEventsListener.nodeMovedEvent.getTargetFqn().equals(second); assert allEventsListener.nodeMovedEvent.getType() == Event.Type.NODE_MOVED; } public void testNotifyNodeEvicted() { assert allEventsListener.nodeEvictedEvent == null; notifier.notifyNodeEvicted(fqn, true, ctx); assert allEventsListener.nodeEvictedEvent != null; assert allEventsListener.nodeEvictedEvent.getFqn().equals(fqn); assert allEventsListener.nodeEvictedEvent.getType() == Event.Type.NODE_EVICTED; } public void testNotifyNodeLoaded() { assert allEventsListener.nodeLoadedEvent == null; Map expected = new HashMap(); expected.put("key", "value"); notifier.notifyNodeLoaded(fqn, true, expected, ctx); assert allEventsListener.nodeLoadedEvent != null; assert allEventsListener.nodeLoadedEvent.getFqn().equals(fqn); assert allEventsListener.nodeLoadedEvent.getData().equals(expected); assert allEventsListener.nodeLoadedEvent.getType() == Event.Type.NODE_LOADED; } public void testNotifyNodeActivated() { assert allEventsListener.nodeActivatedEvent == null; Map expected = new HashMap(); expected.put("key", "value"); notifier.notifyNodeActivated(fqn, true, expected, ctx); assert allEventsListener.nodeActivatedEvent != null; assert allEventsListener.nodeActivatedEvent.getFqn().equals(fqn); assert allEventsListener.nodeActivatedEvent.getData().equals(expected); assert allEventsListener.nodeActivatedEvent.getType() == Event.Type.NODE_ACTIVATED; } public void testNotifyNodePassivated() { assert allEventsListener.nodePassivatedEvent == null; Map expected = new HashMap(); expected.put("key", "value"); notifier.notifyNodePassivated(fqn, true, expected, ctx); assert allEventsListener.nodePassivatedEvent != null; assert allEventsListener.nodePassivatedEvent.getFqn().equals(fqn); assert allEventsListener.nodePassivatedEvent.getData().equals(expected); assert allEventsListener.nodePassivatedEvent.getType() == Event.Type.NODE_PASSIVATED; } public void testNotifyCacheStarted() { assert allEventsListener.cacheStartedEvent == null; notifier.notifyCacheStarted(); assert allEventsListener.cacheStartedEvent != null; assert allEventsListener.cacheStartedEvent.getType() == Event.Type.CACHE_STARTED; } public void testNotifyCacheStopped() { assert allEventsListener.cacheStoppedEvent == null; notifier.notifyCacheStopped(); assert allEventsListener.cacheStoppedEvent != null; assert allEventsListener.cacheStoppedEvent.getType() == Event.Type.CACHE_STOPPED; } public void testNotifyViewChange() { assert allEventsListener.viewChanged == null; View view = new View(); notifier.notifyViewChange(view, ctx); assert allEventsListener.viewChanged != null; assert allEventsListener.viewChanged.getNewView().equals(view); assert allEventsListener.viewChanged.getType() == Event.Type.VIEW_CHANGED; } public void testNotifyBuddyGroupChange() { assert allEventsListener.buddyGroupChangedEvent == null; BuddyGroup buddyGroup = new BuddyGroup(); notifier.notifyBuddyGroupChange(buddyGroup, true); assert allEventsListener.buddyGroupChangedEvent != null; assert allEventsListener.buddyGroupChangedEvent.getBuddyGroup().equals(buddyGroup); assert allEventsListener.buddyGroupChangedEvent.getType() == Event.Type.BUDDY_GROUP_CHANGED; } public void testNotifyTransactionCompleted() { assert allEventsListener.transactionCompleted == null; Transaction tx = createNiceMock(Transaction.class); notifier.notifyTransactionCompleted(tx, false, ctx); assert allEventsListener.transactionCompleted != null; assert allEventsListener.transactionCompleted.getTransaction() == tx; assert !allEventsListener.transactionCompleted.isSuccessful(); assert allEventsListener.transactionCompleted.getType() == Event.Type.TRANSACTION_COMPLETED; } public void testNotifyTransactionRegistered() { assert allEventsListener.transactionRegistered == null; Transaction tx = createNiceMock(Transaction.class); notifier.notifyTransactionRegistered(tx, ctx); assert allEventsListener.transactionRegistered != null; assert allEventsListener.transactionRegistered.getTransaction() == tx; assert allEventsListener.transactionRegistered.getType() == Event.Type.TRANSACTION_REGISTERED; } public void testNotifyCacheBlocked() { assert allEventsListener.cacheBlockedEvent == null; notifier.notifyCacheBlocked(false); assert allEventsListener.cacheBlockedEvent != null; assert !allEventsListener.cacheBlockedEvent.isPre(); assert allEventsListener.cacheBlockedEvent.getType() == Event.Type.CACHE_BLOCKED; } public void testNotifyCacheUnblocked() { assert allEventsListener.cacheUnblockedEvent== null; notifier.notifyCacheUnblocked(false); assert allEventsListener.cacheUnblockedEvent != null; assert !allEventsListener.cacheUnblockedEvent.isPre(); assert allEventsListener.cacheUnblockedEvent.getType() == Event.Type.CACHE_UNBLOCKED; } @CacheListener public static class AllEventsListener { CacheStartedEvent cacheStartedEvent; CacheStoppedEvent cacheStoppedEvent; CacheBlockedEvent cacheBlockedEvent; CacheUnblockedEvent cacheUnblockedEvent; NodeCreatedEvent nodeCreatedEvent; NodeRemovedEvent nodeRemoveEvent; NodeVisitedEvent nodeVisistedEvent; NodeModifiedEvent nodeModifiedEvent; NodeMovedEvent nodeMovedEvent; NodeActivatedEvent nodeActivatedEvent; NodePassivatedEvent nodePassivatedEvent; NodeLoadedEvent nodeLoadedEvent; NodeEvictedEvent nodeEvictedEvent; TransactionRegisteredEvent transactionRegistered; TransactionCompletedEvent transactionCompleted; ViewChangedEvent viewChanged; BuddyGroupChangedEvent buddyGroupChangedEvent; @CacheStarted public void onCacheStarted(CacheStartedEvent event) { cacheStartedEvent = event; } @CacheStopped public void onCacheStopped(CacheStoppedEvent event) { cacheStoppedEvent = event; } @CacheBlocked public void onCacheBlocked(CacheBlockedEvent event) { cacheBlockedEvent = event; } @CacheUnblocked public void onCacheUnblocked(CacheUnblockedEvent event) { cacheUnblockedEvent = event; } @NodeCreated public void onNodeCreated(NodeCreatedEvent event) { nodeCreatedEvent = event; } @NodeRemoved public void onNodeRemoved(NodeRemovedEvent event) { nodeRemoveEvent = event; } @NodeVisited public void onNodeVisited(NodeVisitedEvent event) { nodeVisistedEvent = event; } @NodeModified public void onNodeModified(NodeModifiedEvent event) { nodeModifiedEvent = event; } @NodeMoved public void onNodeMoved(NodeMovedEvent event) { nodeMovedEvent = event; } @NodeActivated public void onNodeActivated(NodeActivatedEvent event) { nodeActivatedEvent = event; } @NodePassivated public void onNodePassivated(NodePassivatedEvent event) { nodePassivatedEvent = event; } @NodeLoaded public void onNodeLoaded(NodeLoadedEvent event) { nodeLoadedEvent = event; } @NodeEvicted public void onNodeEvicted(NodeEvictedEvent event) { nodeEvictedEvent = event; } @TransactionRegistered public void onTransactionRegistered(TransactionRegisteredEvent event) { transactionRegistered = event; } @TransactionCompleted public void onTransactionCompleted(TransactionCompletedEvent event) { transactionCompleted = event; } @ViewChanged public void onViewChanged(ViewChangedEvent event) { viewChanged = event; } @BuddyGroupChanged public void onBuddyGroupChanged(BuddyGroupChangedEvent event) { buddyGroupChangedEvent = event; } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/interceptors/0000755000175000017500000000000011376173744025172 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/interceptors/LegacyInterceptorTest.java0000644000175000017500000000611411017455042032304 0ustar twernertwernerpackage org.jboss.cache.interceptors; import org.jboss.cache.Cache; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.commands.write.PutKeyValueCommand; import org.testng.annotations.AfterTest; import org.testng.annotations.BeforeMethod; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent.Executors; /** * This is to test that "old-style" interceptors from 2.0.x and 2.1.x will work with the new interceptor structure. * * @author Manik Surtani (manik@jboss.org) * @since 2.2.0 */ @Test(groups = "functional") public class LegacyInterceptorTest { Cache cache; static CountDownLatch interceptorResumeLatch, interceptorInvokedLatch; TestInterceptor testInterceptor; Executor testRunner; @BeforeTest public void setUp() { cache = new DefaultCacheFactory().createCache(); testInterceptor = new TestInterceptor(); ((CacheSPI) cache).addInterceptor(testInterceptor, TxInterceptor.class); testRunner = Executors.newSingleThreadExecutor(); } @BeforeMethod public void createLatches() { interceptorResumeLatch = new CountDownLatch(1); interceptorInvokedLatch = new CountDownLatch(1); } @AfterTest public void tearDown() { TestingUtil.killCaches(cache); } public void testPut() throws Exception { testRunner.execute(new Runnable() { public void run() { cache.put("/a", "k", "v"); } }); interceptorInvokedLatch.await(); // check that the context on the test interceptor is correct. InvocationContext ctx = testInterceptor.ctx; interceptorResumeLatch.countDown(); assert ctx.getMethodCall().getMethodId() == PutKeyValueCommand.METHOD_ID; assert ctx.getMethodCall().getArgs()[0] == null; // gtx assert ctx.getMethodCall().getArgs()[1].equals(Fqn.fromString("/a")); // fqn assert ctx.getMethodCall().getArgs()[2].equals("k"); // key assert ctx.getMethodCall().getArgs()[3].equals("v"); // value assert ctx.getMethodCall().getArgs()[4] == Boolean.FALSE; //last boolean value } public static class TestInterceptor extends Interceptor { InvocationContext ctx; @Override public Object invoke(InvocationContext ctx) throws Throwable { if (ctx.isOriginLocal()) { // copy the context so tests can inspect it this.ctx = ctx; // signal to the test that this has been invoked. interceptorInvokedLatch.countDown(); // wait for tests to finish interceptorResumeLatch.await(); // wipe class-level context variable this.ctx = null; } // the "old-style" of passing up the interceptor chain return super.invoke(ctx); } } } ././@LongLink0000000000000000000000000000015000000000000011561 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/interceptors/MarshalledValueInterceptorTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/interceptors/MarshalledValueInterceptorTest.j0000644000175000017500000001416511017455042033466 0ustar twernertwernerpackage org.jboss.cache.interceptors; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.config.Configuration; import org.jboss.cache.marshall.MarshalledValueHelper; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.util.CachePrinter; import org.testng.annotations.AfterMethod; import org.testng.annotations.Test; import java.util.Collection; import java.util.Date; import java.util.List; import java.util.Map; /** * @author Manik Surtani (manik@jboss.org) * @since 2.1.0 */ @Test(groups = "functional") public class MarshalledValueInterceptorTest { CacheSPI c; @AfterMethod public void tearDown() { TestingUtil.killCaches(c); } public void testDefaultInterceptorStack() { c = (CacheSPI) new DefaultCacheFactory().createCache(); assert TestingUtil.findInterceptor(c, MarshalledValueInterceptor.class) == null; TestingUtil.killCaches(c); c = (CacheSPI) new DefaultCacheFactory().createCache(false); c.getConfiguration().setUseLazyDeserialization(true); c.start(); assert TestingUtil.findInterceptor(c, MarshalledValueInterceptor.class) != null; } public void testEnabledInterceptorStack() { Configuration cfg = new Configuration(); cfg.setUseLazyDeserialization(true); c = (CacheSPI) new DefaultCacheFactory().createCache(cfg); System.out.println(CachePrinter.printCacheInterceptors(c)); assert TestingUtil.findInterceptor(c, MarshalledValueInterceptor.class) != null; } public void testDisabledInterceptorStack() { Configuration cfg = new Configuration(); cfg.setUseLazyDeserialization(false); c = (CacheSPI) new DefaultCacheFactory().createCache(cfg); System.out.println(CachePrinter.printCacheInterceptors(c)); assert TestingUtil.findInterceptor(c, MarshalledValueInterceptor.class) == null; } public void testExcludedTypes() { // Strings assert MarshalledValueHelper.isTypeExcluded(String.class); assert MarshalledValueHelper.isTypeExcluded(String[].class); assert MarshalledValueHelper.isTypeExcluded(String[][].class); assert MarshalledValueHelper.isTypeExcluded(String[][][].class); // primitives assert MarshalledValueHelper.isTypeExcluded(void.class); assert MarshalledValueHelper.isTypeExcluded(boolean.class); assert MarshalledValueHelper.isTypeExcluded(char.class); assert MarshalledValueHelper.isTypeExcluded(byte.class); assert MarshalledValueHelper.isTypeExcluded(short.class); assert MarshalledValueHelper.isTypeExcluded(int.class); assert MarshalledValueHelper.isTypeExcluded(long.class); assert MarshalledValueHelper.isTypeExcluded(float.class); assert MarshalledValueHelper.isTypeExcluded(double.class); assert MarshalledValueHelper.isTypeExcluded(boolean[].class); assert MarshalledValueHelper.isTypeExcluded(char[].class); assert MarshalledValueHelper.isTypeExcluded(byte[].class); assert MarshalledValueHelper.isTypeExcluded(short[].class); assert MarshalledValueHelper.isTypeExcluded(int[].class); assert MarshalledValueHelper.isTypeExcluded(long[].class); assert MarshalledValueHelper.isTypeExcluded(float[].class); assert MarshalledValueHelper.isTypeExcluded(double[].class); assert MarshalledValueHelper.isTypeExcluded(boolean[][].class); assert MarshalledValueHelper.isTypeExcluded(char[][].class); assert MarshalledValueHelper.isTypeExcluded(byte[][].class); assert MarshalledValueHelper.isTypeExcluded(short[][].class); assert MarshalledValueHelper.isTypeExcluded(int[][].class); assert MarshalledValueHelper.isTypeExcluded(long[][].class); assert MarshalledValueHelper.isTypeExcluded(float[][].class); assert MarshalledValueHelper.isTypeExcluded(double[][].class); assert MarshalledValueHelper.isTypeExcluded(Void.class); assert MarshalledValueHelper.isTypeExcluded(Boolean.class); assert MarshalledValueHelper.isTypeExcluded(Character.class); assert MarshalledValueHelper.isTypeExcluded(Byte.class); assert MarshalledValueHelper.isTypeExcluded(Short.class); assert MarshalledValueHelper.isTypeExcluded(Integer.class); assert MarshalledValueHelper.isTypeExcluded(Long.class); assert MarshalledValueHelper.isTypeExcluded(Float.class); assert MarshalledValueHelper.isTypeExcluded(Double.class); assert MarshalledValueHelper.isTypeExcluded(Boolean[].class); assert MarshalledValueHelper.isTypeExcluded(Character[].class); assert MarshalledValueHelper.isTypeExcluded(Byte[].class); assert MarshalledValueHelper.isTypeExcluded(Short[].class); assert MarshalledValueHelper.isTypeExcluded(Integer[].class); assert MarshalledValueHelper.isTypeExcluded(Long[].class); assert MarshalledValueHelper.isTypeExcluded(Float[].class); assert MarshalledValueHelper.isTypeExcluded(Double[].class); assert MarshalledValueHelper.isTypeExcluded(Boolean[][].class); assert MarshalledValueHelper.isTypeExcluded(Character[][].class); assert MarshalledValueHelper.isTypeExcluded(Byte[][].class); assert MarshalledValueHelper.isTypeExcluded(Short[][].class); assert MarshalledValueHelper.isTypeExcluded(Integer[][].class); assert MarshalledValueHelper.isTypeExcluded(Long[][].class); assert MarshalledValueHelper.isTypeExcluded(Float[][].class); assert MarshalledValueHelper.isTypeExcluded(Double[][].class); } public void testNonExcludedTypes() { assert !MarshalledValueHelper.isTypeExcluded(Object.class); assert !MarshalledValueHelper.isTypeExcluded(List.class); assert !MarshalledValueHelper.isTypeExcluded(Collection.class); assert !MarshalledValueHelper.isTypeExcluded(Map.class); assert !MarshalledValueHelper.isTypeExcluded(Date.class); assert !MarshalledValueHelper.isTypeExcluded(Thread.class); assert !MarshalledValueHelper.isTypeExcluded(Collection.class); assert !MarshalledValueHelper.isTypeExcluded(new Object() { String blah; }.getClass()); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/interceptors/EvictionInterceptorTest.java0000644000175000017500000004757711020007335032672 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.interceptors; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.Node; import org.jboss.cache.NodeSPI; import org.jboss.cache.Region; import org.jboss.cache.RegionManager; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.commands.VisitableCommand; import org.jboss.cache.commands.read.GetKeyValueCommand; import org.jboss.cache.commands.read.GetNodeCommand; import org.jboss.cache.commands.write.PutDataMapCommand; import org.jboss.cache.commands.write.PutKeyValueCommand; import org.jboss.cache.commands.write.ClearDataCommand; import org.jboss.cache.commands.write.RemoveKeyCommand; import org.jboss.cache.commands.write.RemoveNodeCommand; import org.jboss.cache.config.EvictionConfig; import org.jboss.cache.config.EvictionRegionConfig; import org.jboss.cache.eviction.DummyEvictionConfiguration; import org.jboss.cache.eviction.EvictedEventNode; import org.jboss.cache.eviction.NodeEventType; import org.jboss.cache.factories.CommandsFactory; import org.jboss.cache.lock.IsolationLevel; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; /** * @author Daniel Huang (dhuang@jboss.org) * @version $Revision: $ */ @Test(groups = "functional") public class EvictionInterceptorTest { private static final String fqn1 = "/a/b/c"; private static final String fqn2 = "/a/b"; private static final String fqn3 = "/a/b/d"; private static final String fqn4 = "/d/e/f"; private CacheSPI cache; private InterceptorChain invoker; private RegionManager regionManager; private CommandsFactory commandsFactory; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { cache = (CacheSPI) new DefaultCacheFactory().createCache(false); cache.getConfiguration().setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); cache.getConfiguration().setIsolationLevel(IsolationLevel.SERIALIZABLE); cache.getConfiguration().setCacheMode("LOCAL"); EvictionConfig ec = new EvictionConfig(); List ercs = new LinkedList(); ercs.add(new EvictionRegionConfig(Fqn.ROOT, new DummyEvictionConfiguration())); ercs.add(new EvictionRegionConfig(Fqn.fromString("/a/b/c"), new DummyEvictionConfiguration())); ercs.add(new EvictionRegionConfig(Fqn.fromString("/a/b/c/d"), new DummyEvictionConfiguration())); ercs.add(new EvictionRegionConfig(Fqn.fromString("/a/b"), new DummyEvictionConfiguration())); ercs.add(new EvictionRegionConfig(Fqn.fromString("/d/e/f"), new DummyEvictionConfiguration())); ercs.add(new EvictionRegionConfig(Fqn.fromString("/d/e/g"), new DummyEvictionConfiguration())); ercs.add(new EvictionRegionConfig(Fqn.fromString("/d/e"), new DummyEvictionConfiguration())); ec.setEvictionRegionConfigs(ercs); cache.getConfiguration().setEvictionConfig(ec); cache.start(); invoker = TestingUtil.extractComponentRegistry(cache).getComponent(InterceptorChain.class); commandsFactory = TestingUtil.extractCommandsFactory(cache); regionManager = cache.getRegionManager(); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { TestingUtil.killCaches(cache); } @SuppressWarnings("unchecked") private NodeSPI cast(Node node) { return (NodeSPI) node; } public void testVisitNode() throws Throwable { // make sure node that doesn't exist does not result in a node visit event. VisitableCommand command = commandsFactory.buildGetNodeCommand(Fqn.fromString(fqn1)); invoker.invoke(command); Region regionABC = regionManager.getRegion(fqn1, false); assertNull(regionABC.takeLastEventNode()); putQuietly(fqn1, "key", "value"); NodeSPI node = cast(cache.peek(Fqn.fromString(fqn1), false, false)); assertNotNull(node); assertEquals("value", node.getDirect("key")); putQuietly(fqn3, "key", "value"); node = cast(cache.peek(Fqn.fromString(fqn3), false, false)); assertNotNull(node); assertEquals("value", node.getDirect("key")); command = commandsFactory.buildGetNodeCommand(Fqn.fromString(fqn1)); invoker.invoke(command); regionABC = regionManager.getRegion(fqn1, false); EvictedEventNode event = regionABC.takeLastEventNode(); assertEquals(NodeEventType.VISIT_NODE_EVENT, event.getEventType()); assertEquals(fqn1, event.getFqn().toString()); assertNull(regionABC.takeLastEventNode()); command = commandsFactory.buildGetNodeCommand(Fqn.fromString(fqn2)); invoker.invoke(command); Region regionAB = regionManager.getRegion(fqn2, false); event = regionAB.takeLastEventNode(); assertEquals(NodeEventType.VISIT_NODE_EVENT, event.getEventType()); assertEquals(fqn2, event.getFqn().toString()); assertNull(regionAB.takeLastEventNode()); command = commandsFactory.buildGetNodeCommand(Fqn.fromString(fqn3)); invoker.invoke(command); Region regionABD = regionManager.getRegion(fqn3, false); event = regionABD.takeLastEventNode(); assertEquals(NodeEventType.VISIT_NODE_EVENT, event.getEventType()); assertEquals(fqn3, event.getFqn().toString()); assertNull(regionABD.takeLastEventNode()); for (int i = 0; i < 10; i++) { command = commandsFactory.buildGetNodeCommand(Fqn.fromString(fqn3)); invoker.invoke(command); } for (int i = 0; i < 10; i++) { Region region = regionManager.getRegion(fqn3, false); event = region.takeLastEventNode(); assertEquals(NodeEventType.VISIT_NODE_EVENT, event.getEventType()); assertEquals(fqn3, event.getFqn().toString()); } assertNull(regionManager.getRegion(fqn3, false).takeLastEventNode()); // check null handling. command = commandsFactory.buildGetDataMapCommand(null); invoker.invoke(command); } /** * Helper to quietly add something into the cache without generating eviction events * * @param fqn fqn to add * @param key key * @param value value */ private void putQuietly(String fqn, Object key, Object value) { putQuietly(Fqn.fromString(fqn), key, value); } /** * Helper to quietly add something into the cache without generating eviction events * * @param fqn fqn to add * @param key key * @param value value */ private void putQuietly(Fqn fqn, Object key, Object value) { NodeSPI root = cache.getRoot(); NodeSPI child = root; for (int i = 0; i < fqn.size(); i++) { child = child.addChildDirect(Fqn.fromElements(fqn.get(i))); } assert child.getFqn().equals(fqn); child.putDirect(key, value); } public void testVisitElement() throws Throwable { // make sure a get/visit on an empty node and empty element results in no cache events being added to event queue // aka MarshRegion. Fqn fqn = Fqn.fromString(fqn4); Object key = "key"; GetKeyValueCommand command = commandsFactory.buildGetKeyValueCommand(fqn, key, false); invoker.invoke(command); Region region = regionManager.getRegion(fqn.toString(), false); assertNull(region.takeLastEventNode()); // add the node but try to get on a null element should result in no cache events being added to Region. putQuietly(fqn, "wrongkey", ""); command = commandsFactory.buildGetKeyValueCommand(fqn, key, false); invoker.invoke(command); assertNull(region.takeLastEventNode()); // now make sure if we try to get on the node/key we just created in cache, that this DOES add a EvictedEventNode to // the MarshRegion. command = commandsFactory.buildGetKeyValueCommand(fqn, "wrongkey", false); invoker.invoke(command); EvictedEventNode event = region.takeLastEventNode(); assertEquals(fqn, event.getFqn()); assertEquals(NodeEventType.VISIT_NODE_EVENT, event.getEventType()); assertNull(region.takeLastEventNode()); putQuietly(fqn4, key, "value"); // test on element granularity configured node. fqn = Fqn.fromString(fqn4); command = commandsFactory.buildGetKeyValueCommand(fqn, key, false); invoker.invoke(command); region = regionManager.getRegion(fqn.toString(), false); event = region.takeLastEventNode(); assertEquals(NodeEventType.VISIT_NODE_EVENT, event.getEventType()); assertEquals(fqn, event.getFqn()); assertNull(region.takeLastEventNode()); fqn = Fqn.fromString("/d/e/g"); for (int i = 0; i < 100; i++) { key = i; putQuietly("/d/e/g", key, ""); command = commandsFactory.buildGetKeyValueCommand(fqn, key, false); invoker.invoke(command); } region = regionManager.getRegion(fqn.toString(), false); for (int i = 0; i < 100; i++) { event = region.takeLastEventNode(); assertEquals(fqn, event.getFqn()); assertEquals(NodeEventType.VISIT_NODE_EVENT, event.getEventType()); } putQuietly("/a/b/c", key, ""); fqn = Fqn.fromString("/a/b/c"); command = commandsFactory.buildGetKeyValueCommand(fqn, key, false); invoker.invoke(command); region = regionManager.getRegion(fqn.toString(), false); event = region.takeLastEventNode(); assertNull(region.takeLastEventNode()); assertEquals(NodeEventType.VISIT_NODE_EVENT, event.getEventType()); assertEquals(fqn, event.getFqn()); for (int i = 0; i < 100; i++) { key = i; putQuietly(fqn, key, ""); command = commandsFactory.buildGetKeyValueCommand(fqn, key, false); invoker.invoke(command); } for (int i = 0; i < 100; i++) { event = region.takeLastEventNode(); assertEquals(NodeEventType.VISIT_NODE_EVENT, event.getEventType()); assertEquals(fqn, event.getFqn()); } assertNull(region.takeLastEventNode()); } public void testCreateNode() throws Throwable { Map data = new HashMap(); for (int i = 0; i < 100; i++) { data.put(i, i); } // this region is node granularity Fqn fqn = Fqn.fromString("/a/b/c"); PutDataMapCommand putDataMapCommand = commandsFactory.buildPutDataMapCommand(null, fqn, data); invoker.invoke(putDataMapCommand); Region region = regionManager.getRegion(fqn.toString(), false); EvictedEventNode event = region.takeLastEventNode(); assertEquals(NodeEventType.ADD_NODE_EVENT, event.getEventType()); assertEquals(fqn, event.getFqn()); assertEquals(100, event.getElementDifference()); NodeSPI node = cast(cache.peek(fqn, false, false)); assertNotNull(node); for (int i = 0; i < 100; i++) { assertTrue(node.getDataDirect().containsKey(i)); assertEquals(i, node.getDirect(i)); } for (int i = 0; i < 100; i++) { PutKeyValueCommand pkvCommand = commandsFactory.buildPutKeyValueCommand(null, (Fqn) fqn, i, "value"); invoker.invoke(pkvCommand); assertEquals("value", cache.peek(fqn, false, false).getDirect(i)); } for (int i = 0; i < 100; i++) { event = region.takeLastEventNode(); assertNotNull(event); assertEquals(fqn, event.getFqn()); assertEquals(NodeEventType.ADD_ELEMENT_EVENT, event.getEventType()); } assertNull(region.takeLastEventNode()); fqn = Fqn.fromString("/a/b"); PutDataMapCommand putCommand = commandsFactory.buildPutDataMapCommand(null, fqn, data); invoker.invoke(putCommand); event = regionManager.getRegion(fqn.toString(), false).takeLastEventNode(); assertEquals(NodeEventType.ADD_NODE_EVENT, event.getEventType()); assertEquals(fqn, event.getFqn()); assertEquals(100, event.getElementDifference()); assertNull(regionManager.getRegion(fqn.toString(), false).takeLastEventNode()); node = cast(cache.peek(fqn, false, false)); assertEquals(100, node.getDataDirect().size()); assertNotNull(node); for (int i = 0; i < 100; i++) { assertTrue(node.getDataDirect().containsKey(i)); assertEquals(i, node.getDirect(i)); } PutDataMapCommand putDataMap = commandsFactory.buildPutDataMapCommand(null, fqn, data); invoker.invoke(putDataMap); event = regionManager.getRegion(fqn.toString(), false).takeLastEventNode(); assertEquals(NodeEventType.ADD_NODE_EVENT, event.getEventType()); assertEquals(fqn, event.getFqn()); assertEquals(100, event.getElementDifference()); assertNull(regionManager.getRegion(fqn.toString(), false).takeLastEventNode()); node = cast(cache.getNode(fqn)); assertEquals(100, node.getData().size()); assertNotNull(node); for (int i = 0; i < 100; i++) { assertTrue(node.getDataDirect().containsKey(i)); assertEquals(i, node.getDirect(i)); } } public void testCreateElement() throws Throwable { Fqn fqn = Fqn.fromString("/d/e/f"); Region region = regionManager.getRegion(fqn.toString(), false); Object key = "key"; Object value = "value"; PutKeyValueCommand command = commandsFactory.buildPutKeyValueCommand(null, (Fqn) fqn, key, value); invoker.invoke(command); assertEquals("value", cache.peek(fqn, false, false).getDirect(key)); EvictedEventNode event = region.takeLastEventNode(); assertEquals(NodeEventType.ADD_ELEMENT_EVENT, event.getEventType()); assertEquals(1, event.getElementDifference()); assertEquals(fqn, event.getFqn()); assertEquals("value", cache.peek(fqn, false, false).getDirect(key)); assertNull(region.takeLastEventNode()); command = commandsFactory.buildPutKeyValueCommand(null, (Fqn) fqn, key, value); invoker.invoke(command); assertEquals("value", cache.peek(fqn, false, false).getDirect(key)); event = region.takeLastEventNode(); assertEquals(NodeEventType.ADD_ELEMENT_EVENT, event.getEventType()); assertEquals(1, event.getElementDifference()); assertEquals(fqn, event.getFqn()); assertEquals("value", cache.peek(fqn, false, false).getDirect(key)); assertNull(region.takeLastEventNode()); } public void testRemoveNode() throws Throwable { Fqn fqn = Fqn.fromString("/a/b/c"); putQuietly(fqn, "a", "b"); putQuietly(fqn, "b", "c"); ClearDataCommand clearDataCommand = commandsFactory.buildClearDataCommand(null, fqn); invoker.invoke(clearDataCommand); assertEquals(0, cache.peek(fqn, false, false).getDataDirect().size()); Region region = regionManager.getRegion(fqn.toString(), false); EvictedEventNode event = region.takeLastEventNode(); assertEquals(NodeEventType.REMOVE_NODE_EVENT, event.getEventType()); assertEquals(fqn, event.getFqn()); assertNull(region.takeLastEventNode()); RemoveNodeCommand removeNodeCommand = commandsFactory.buildRemoveNodeCommand(null, fqn); invoker.invoke(removeNodeCommand); assertNull(cache.peek(fqn, false, false)); event = region.takeLastEventNode(); assertEquals(NodeEventType.REMOVE_NODE_EVENT, event.getEventType()); assertEquals(fqn, event.getFqn()); assertNull(region.takeLastEventNode()); } public void testRemoveElement() throws Throwable { Fqn fqn = Fqn.fromString("/a/b/c"); putQuietly(fqn, "a", "b"); putQuietly(fqn, "b", "c"); RemoveKeyCommand removeKeyCommand = commandsFactory.buildRemoveKeyCommand(null, fqn, "a"); invoker.invoke(removeKeyCommand); assertNull(cache.get(fqn, "a")); Region region = regionManager.getRegion(fqn.toString(), false); EvictedEventNode event = region.takeLastEventNode(); assertEquals(NodeEventType.REMOVE_ELEMENT_EVENT, event.getEventType()); assertEquals(fqn, event.getFqn()); assertEquals(1, event.getElementDifference()); assertNull(region.takeLastEventNode()); RemoveKeyCommand removeKeyCommand2 = commandsFactory.buildRemoveKeyCommand(null, fqn, "b"); invoker.invoke(removeKeyCommand2); assertNull(cache.get(fqn, "b")); event = region.takeLastEventNode(); assertEquals(NodeEventType.REMOVE_ELEMENT_EVENT, event.getEventType()); assertEquals(fqn, event.getFqn()); assertEquals(1, event.getElementDifference()); assertNull(region.takeLastEventNode()); RemoveKeyCommand removeKeyCommand3 = commandsFactory.buildRemoveKeyCommand(null, fqn, "a"); invoker.invoke(removeKeyCommand3); event = region.takeLastEventNode(); assertNull(event); } public void testMixedEvent() throws Throwable { Map data = new HashMap(); for (int i = 0; i < 100; i++) { data.put(i, i); } // this region is node granularity Fqn fqn = Fqn.fromString("/a/b/c"); PutDataMapCommand putDataCommand = commandsFactory.buildPutDataMapCommand(null, fqn, data); invoker.invoke(putDataCommand); Region region = regionManager.getRegion(fqn.toString(), false); EvictedEventNode event = region.takeLastEventNode(); assertEquals(NodeEventType.ADD_NODE_EVENT, event.getEventType()); assertEquals(fqn, event.getFqn()); assertEquals(100, event.getElementDifference()); assertNull(region.takeLastEventNode()); GetNodeCommand getNodeCommand = commandsFactory.buildGetNodeCommand(fqn); invoker.invoke(getNodeCommand); event = region.takeLastEventNode(); assertEquals(NodeEventType.VISIT_NODE_EVENT, event.getEventType()); assertEquals(fqn, event.getFqn()); assertNull(region.takeLastEventNode()); RemoveNodeCommand removeNodeCommand = commandsFactory.buildRemoveNodeCommand(null, fqn); invoker.invoke(removeNodeCommand); assertNull(cache.getNode(fqn)); event = region.takeLastEventNode(); assertEquals(NodeEventType.REMOVE_NODE_EVENT, event.getEventType()); assertEquals(fqn, event.getFqn()); assertNull(region.takeLastEventNode()); Object key = "key"; Object value = "value"; PutKeyValueCommand putKeyValueCommand = commandsFactory.buildPutKeyValueCommand(null, (Fqn) fqn, key, value); invoker.invoke(putKeyValueCommand); assertEquals("value", cache.peek(fqn, false, false).getDirect(key)); event = region.takeLastEventNode(); assertEquals(NodeEventType.ADD_ELEMENT_EVENT, event.getEventType()); assertEquals(1, event.getElementDifference()); assertEquals(fqn, event.getFqn()); assertEquals("value", cache.peek(fqn, false, false).getDirect(key)); assertNull(region.takeLastEventNode()); GetKeyValueCommand getKeyValueCommand = commandsFactory.buildGetKeyValueCommand(fqn, key, false); invoker.invoke(getKeyValueCommand); region = regionManager.getRegion(fqn.toString(), false); event = region.takeLastEventNode(); assertEquals(NodeEventType.VISIT_NODE_EVENT, event.getEventType()); assertEquals(fqn, event.getFqn()); assertNull(region.takeLastEventNode()); RemoveKeyCommand removeKeyCommand = commandsFactory.buildRemoveKeyCommand(null, fqn, key); invoker.invoke(removeKeyCommand); assertNull(cache.get(fqn, key)); event = region.takeLastEventNode(); assertEquals(NodeEventType.REMOVE_ELEMENT_EVENT, event.getEventType()); assertEquals(fqn, event.getFqn()); assertEquals(1, event.getElementDifference()); assertNull(region.takeLastEventNode()); } }jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/invalidation/0000755000175000017500000000000011376173735025132 5ustar twernertwerner././@LongLink0000000000000000000000000000014500000000000011565 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/invalidation/InvalidationInterceptorTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/invalidation/InvalidationInterceptorTest.java0000644000175000017500000010027711020007335033456 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.invalidation; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.Node; import org.jboss.cache.NodeSPI; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.util.internals.ReplicationListener; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.XmlConfigurationParser; import org.jboss.cache.optimistic.DefaultDataVersion; import org.jboss.cache.xml.XmlHelper; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.Test; import org.w3c.dom.Element; import javax.transaction.RollbackException; import javax.transaction.Transaction; import javax.transaction.TransactionManager; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; /** * Tests the async interceptor * * @author Manik Surtani (manik@jboss.org) */ @Test(groups = {"functional", "jgroups"}) public class InvalidationInterceptorTest { private static Log log = LogFactory.getLog(InvalidationInterceptorTest.class); private CacheSPI cache1, cache2; private Set toClean = new HashSet(); @AfterMethod public void tearDown() { TestingUtil.killCaches(cache1, cache2); for (CacheSPI c : toClean) TestingUtil.killCaches(c); toClean.clear(); } public void testPessimisticNonTransactional() throws Exception { cache1 = createCache(false); cache2 = createCache(false); Fqn fqn = Fqn.fromString("/a/b"); cache1.put(fqn, "key", "value"); // test that this has NOT replicated, but rather has been invalidated: assertEquals("value", cache1.get(fqn, "key")); assertNull("Should NOT have replicated!", cache2.getNode(fqn)); log.info("***** Node not replicated, as expected."); // now make sure cache2 is in sync with cache1: cache2.put(fqn, "key", "value"); // since the node already exists even PL will not remove it - but will invalidate it's data Node n = cache1.getNode(fqn); assertHasBeenInvalidated(n, "Should have been invalidated"); assertEquals("value", cache2.get(fqn, "key")); // now test the invalidation: cache1.put(fqn, "key2", "value2"); assertEquals("value2", cache1.get(fqn, "key2")); n = cache2.getNode(fqn); assertHasBeenInvalidated(n, "Should have been invalidated"); } public void testUnnecessaryEvictions() throws Exception { cache1 = createCache(false); cache2 = createCache(false); Fqn fqn1 = Fqn.fromString("/a/b/c"); Fqn fqn2 = Fqn.fromString("/a/b/d"); cache1.put(fqn1, "hello", "world"); assertEquals("world", cache1.get(fqn1, "hello")); assertNull(cache2.get(fqn1, "hello")); cache2.put(fqn2, "hello", "world"); assertEquals("world", cache1.get(fqn1, "hello")); assertNull(cache2.get(fqn1, "hello")); assertEquals("world", cache2.get(fqn2, "hello")); assertNull(cache1.get(fqn2, "hello")); cache2.put(fqn1, "hello", "world"); assertEquals("world", cache2.get(fqn1, "hello")); assertEquals("world", cache2.get(fqn2, "hello")); assertNull(cache1.get(fqn1, "hello")); assertNull(cache1.get(fqn2, "hello")); } public void testPessimisticNonTransactionalAsync() throws Exception { cache1 = createUnstartedCache(false); cache2 = createUnstartedCache(false); cache1.getConfiguration().setCacheMode(Configuration.CacheMode.INVALIDATION_ASYNC); cache2.getConfiguration().setCacheMode(Configuration.CacheMode.INVALIDATION_ASYNC); cache1.start(); cache2.start(); Fqn fqn = Fqn.fromString("/a/b"); ReplicationListener replListener1 = new ReplicationListener(cache1); ReplicationListener replListener2 = new ReplicationListener(cache2); replListener2.expectAny(); cache1.put(fqn, "key", "value"); replListener2.waitForReplicationToOccur(500); // TestingUtil.sleepThread(500);// give it time to broadcast the evict call // test that this has NOT replicated, but rather has been invalidated: assertEquals("value", cache1.get(fqn, "key")); assertNull("Should NOT have replicated!", cache2.getNode(fqn)); replListener1.expectAny(); // now make sure cache2 is in sync with cache1: cache2.put(fqn, "key", "value"); // TestingUtil.sleepThread(500);// give it time to broadcast the evict call replListener1.waitForReplicationToOccur(500); // since the node already exists even PL will not remove it - but will invalidate it's data Node n = cache1.getNode(fqn); assertHasBeenInvalidated(n, "Should have been invalidated"); assertEquals("value", cache2.get(fqn, "key")); replListener2.expectAny(); // now test the invalidation: cache1.put(fqn, "key2", "value2"); assertEquals("value2", cache1.get(fqn, "key2")); // TestingUtil.sleepThread(500);// give it time to broadcast the evict call replListener2.waitForReplicationToOccur(500); // since the node already exists even PL will not remove it - but will invalidate it's data n = cache2.getNode(fqn); assertHasBeenInvalidated(n, "Should have been invalidated"); } public void testPessimisticTransactional() throws Exception { cache1 = createCache(false); cache2 = createCache(false); Fqn fqn = Fqn.fromString("/a/b"); cache1.put(fqn, "key", "value"); // test that this has NOT replicated, but rather has been invalidated: assertEquals("value", cache1.get(fqn, "key")); assertNull("Should NOT have replicated!", cache2.getNode(fqn)); log.info("***** Node not replicated, as expected."); // now make sure cache2 is in sync with cache1: // make sure this is in a tx TransactionManager txm = cache2.getTransactionManager(); assertEquals("value", cache1.get(fqn, "key")); txm.begin(); cache2.put(fqn, "key", "value"); assertEquals("value", cache2.get(fqn, "key")); txm.commit(); // since the node already exists even PL will not remove it - but will invalidate it's data Node n = cache1.getNode(fqn); assertHasBeenInvalidated(n, "Should have been invalidated"); assertEquals("value", cache2.get(fqn, "key")); // now test the invalidation again txm = cache1.getTransactionManager(); assertEquals("value", cache2.get(fqn, "key")); txm.begin(); cache1.put(fqn, "key2", "value2"); assertEquals("value2", cache1.get(fqn, "key2")); txm.commit(); assertEquals("value2", cache1.get(fqn, "key2")); // since the node already exists even PL will not remove it - but will invalidate it's data n = cache2.getNode(fqn); assertHasBeenInvalidated(n, "Should have been invalidated"); // test a rollback txm = cache2.getTransactionManager(); assertEquals("value2", cache1.get(fqn, "key2")); txm.begin(); cache2.put(fqn, "key", "value"); assertEquals("value", cache2.get(fqn, "key")); txm.rollback(); assertEquals("value2", cache1.get(fqn, "key2")); n = cache2.getNode(fqn); assertHasBeenInvalidated(n, "Should have been invalidated"); } public void testOptSyncUnableToEvict() throws Exception { cache1 = createCache(true); cache2 = createCache(true); Fqn fqn = Fqn.fromString("/a/b"); cache2.put(fqn, "key", "value"); assertEquals("value", cache2.get(fqn, "key")); Node n = cache1.getNode(fqn); assertHasBeenInvalidated(n, "Should have been invalidated"); assertHasBeenInvalidated(cache1.peek(fqn, true, true), "Should have been invalidated"); // start a tx that cache1 will have to send out an evict ... TransactionManager mgr1 = cache1.getTransactionManager(); TransactionManager mgr2 = cache2.getTransactionManager(); mgr1.begin(); cache1.put(fqn, "key2", "value2"); Transaction tx1 = mgr1.suspend(); mgr2.begin(); cache2.put(fqn, "key3", "value3"); Transaction tx2 = mgr2.suspend(); mgr1.resume(tx1); // this oughtta fail try { mgr1.commit(); assertTrue("Ought to have succeeded!", true); } catch (RollbackException roll) { assertTrue("Ought to have succeeded!", false); } mgr2.resume(tx2); try { mgr2.commit(); assertTrue("Ought to have failed!", false); } catch (RollbackException roll) { assertTrue("Ought to have failed!", true); } } public void testPessTxSyncUnableToEvict() throws Exception { cache1 = createCache(false); cache2 = createCache(false); Fqn fqn = Fqn.fromString("/a/b"); cache1.put("/a/b", "key", "value"); assertEquals("value", cache1.get(fqn, "key")); assertNull(cache2.getNode(fqn)); // start a tx that cacahe1 will have to send out an evict ... TransactionManager mgr1 = cache1.getTransactionManager(); TransactionManager mgr2 = cache2.getTransactionManager(); mgr1.begin(); cache1.put(fqn, "key2", "value2"); Transaction tx1 = mgr1.suspend(); mgr2.begin(); cache2.put(fqn, "key3", "value3"); Transaction tx2 = mgr2.suspend(); mgr1.resume(tx1); // this oughtta fail try { mgr1.commit(); assertTrue("Ought to have failed!", false); } catch (RollbackException roll) { assertTrue("Ought to have failed!", true); } mgr2.resume(tx2); try { mgr2.commit(); assertTrue("Ought to have succeeded!", true); } catch (RollbackException roll) { assertTrue("Ought to have succeeded!", false); } } public void testPessTxAsyncUnableToEvict() throws Exception { cache1 = createUnstartedCache(false); cache2 = createUnstartedCache(false); cache1.getConfiguration().setCacheMode(Configuration.CacheMode.INVALIDATION_ASYNC); cache2.getConfiguration().setCacheMode(Configuration.CacheMode.INVALIDATION_ASYNC); cache1.start(); cache2.start(); Fqn fqn = Fqn.fromString("/a/b"); cache1.put("/a/b", "key", "value"); assertEquals("value", cache1.get(fqn, "key")); assertNull(cache2.getNode(fqn)); // start a tx that cacahe1 will have to send out an evict ... TransactionManager mgr1 = cache1.getTransactionManager(); TransactionManager mgr2 = cache2.getTransactionManager(); mgr1.begin(); cache1.put(fqn, "key2", "value2"); Transaction tx1 = mgr1.suspend(); mgr2.begin(); cache2.put(fqn, "key3", "value3"); Transaction tx2 = mgr2.suspend(); mgr1.resume(tx1); // this oughtta fail try { mgr1.commit(); assertTrue("Ought to have succeeded!", true); } catch (RollbackException roll) { assertTrue("Ought to have succeeded!", false); } mgr2.resume(tx2); try { mgr2.commit(); assertTrue("Ought to have succeeded!", true); } catch (RollbackException roll) { assertTrue("Ought to have succeeded!", false); } } public void testPessimisticNodeRemoval() throws Exception { nodeRemovalTest(false); } public void testOptimisticNodeRemoval() throws Exception { nodeRemovalTest(true); } @SuppressWarnings("unchecked") private void nodeRemovalTest(boolean optimistic) throws Exception { cache1 = createCache(optimistic); cache2 = createCache(optimistic); Node root1 = cache1.getRoot(); Node root2 = cache2.getRoot(); // this fqn is relative, but since it is from the root it may as well be absolute Fqn fqn = Fqn.fromString("/test/fqn"); cache1.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); cache1.put(fqn, "key", "value"); assertEquals("value", cache1.get(fqn, "key")); cache2.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); cache2.put(fqn, "key", "value"); assertEquals("value", cache2.get(fqn, "key")); assertEquals(true, cache1.removeNode(fqn)); assertFalse(root1.hasChild(fqn)); Node remoteNode = root2.getChild(fqn); checkRemoteNodeIsRemoved(remoteNode); assertEquals(false, cache1.removeNode(fqn)); Fqn child = Fqn.fromString("/test/fqn/child"); cache1.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); cache1.put(child, "key", "value"); assertEquals("value", cache1.get(child, "key")); cache2.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); cache2.put(child, "key", "value"); assertEquals("value", cache2.get(child, "key")); assertEquals(true, cache1.removeNode(fqn)); assertFalse(root1.hasChild(fqn)); remoteNode = root2.getChild(fqn); checkRemoteNodeIsRemoved(remoteNode); assertEquals(false, cache1.removeNode(fqn)); } private void checkRemoteNodeIsRemoved(Node remoteNode) { assertHasBeenInvalidated(remoteNode, "Should have been removed"); // Recursively check any children if (remoteNode != null) { for (Node child : remoteNode.getChildren()) { checkRemoteNodeIsRemoved(child); } } } public void testPessimisticNodeResurrection() throws Exception { nodeResurrectionTest(false); } public void testOptimisticNodeResurrection() throws Exception { nodeResurrectionTest(true); } private void nodeResurrectionTest(boolean optimistic) throws Exception { cache1 = createCache(optimistic); cache2 = createCache(optimistic); // this fqn is relative, but since it is from the root it may as well be absolute Fqn fqn = Fqn.fromString("/test/fqn1"); cache1.put(fqn, "key", "value"); assertEquals("value", cache1.get(fqn, "key")); assertEquals(null, cache2.get(fqn, "key")); // Change the value in order to increment the version if Optimistic is used cache1.put(fqn, "key", "newValue"); assertEquals("newValue", cache1.get(fqn, "key")); assertEquals(null, cache2.get(fqn, "key")); assertEquals(true, cache1.removeNode(fqn)); assertEquals(null, cache1.get(fqn, "key")); assertEquals(null, cache2.get(fqn, "key")); // Restore locally cache1.put(fqn, "key", "value"); assertEquals("value", cache1.get(fqn, "key")); assertEquals(null, cache2.get(fqn, "key")); // Repeat, but now restore the node on the remote cache fqn = Fqn.fromString("/test/fqn2"); cache1.put(fqn, "key", "value"); assertEquals("value", cache1.get(fqn, "key")); assertEquals(null, cache2.get(fqn, "key")); // Change the value in order to increment the version if Optimistic is used cache1.put(fqn, "key", "newValue"); assertEquals("newValue", cache1.get(fqn, "key")); assertEquals(null, cache2.get(fqn, "key")); assertEquals(true, cache1.removeNode(fqn)); assertEquals(null, cache1.get(fqn, "key")); assertEquals(null, cache2.get(fqn, "key")); // Restore on remote cache cache2.put(fqn, "key", "value"); assertEquals("value", cache2.get(fqn, "key")); assertEquals(null, cache1.get(fqn, "key")); } /** * Test for JBCACHE-1251. * * @throws Exception */ public void testPessimisticNodeResurrection2() throws Exception { nodeResurrectionTest2(false); } /** * OPTIMISTIC locking verion of test for JBCACHE-1251. JBCACHE-1251 * did not effect optimistic, but we add the test to guard against * regressions. * * @throws Exception */ public void testOptimisticNodeResurrection2() throws Exception { nodeResurrectionTest2(true); } /** * Here we model a scenario where a parent node represents * a structural node, and then child nodes represent different * data elements. *

* Such data structures are set up on both caches, and then the parent node * is removed (globally) and re-added (locally) on one cache. This * represents an attempt to clear the region -- removing a node and * re-adding is one of the only ways to do this. *

* On the second cache, the fact that the structural node is missing is * detected, and an attempt is made to re-add it locally. * * @param optimistic should the cache be configured for optimistic locking * @throws Exception */ private void nodeResurrectionTest2(boolean optimistic) throws Exception { cache1 = createCache(optimistic); cache2 = createCache(optimistic); Node root1 = cache1.getRoot(); Node root2 = cache2.getRoot(); // this fqn is relative, but since it is from the root it may as well be absolute Fqn fqn = Fqn.fromString("/test/fqn"); cache1.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); root1.addChild(fqn); assertEquals(true, root1.hasChild(fqn)); cache2.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); root1.addChild(fqn); assertEquals(true, root1.hasChild(fqn)); Fqn child = Fqn.fromRelativeElements(fqn, "child"); cache1.putForExternalRead(child, "key", "value"); cache2.putForExternalRead(child, "key", "value"); assertEquals("value", cache1.get(child, "key")); assertEquals("value", cache2.get(child, "key")); assertEquals(true, cache1.removeNode(fqn)); assertFalse(root1.hasChild(fqn)); cache1.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); root1.addChild(fqn); assertEquals(true, root1.hasChild(fqn)); Node remoteNode = root2.getChild(fqn); checkRemoteNodeIsRemoved(remoteNode); cache2.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); root2.addChild(fqn); assertEquals(true, root2.hasChild(fqn)); } private void dumpVersionInfo(CacheSPI c1, CacheSPI c2, Fqn fqn) { System.out.println("**** Versin Info for Fqn [" + fqn + "] ****"); NodeSPI n1 = c1.getRoot().getChildDirect(fqn); System.out.println(" Cache 1: " + n1.getVersion() + " dataLoaded? " + n1.isDataLoaded()); NodeSPI n2 = c2.getRoot().getChildDirect(fqn); System.out.println(" Cache 2: " + n2.getVersion() + " dataLoaded? " + n2.isDataLoaded()); } public void testOptimistic() throws Exception { cache1 = createCache(true); cache2 = createCache(true); Fqn fqn = Fqn.fromString("/a/b"); cache1.put(fqn, "key", "value"); dumpVersionInfo(cache1, cache2, fqn); // test that this has NOT replicated, but rather has been invalidated: assertEquals("value", cache1.get(fqn, "key")); Node n2 = cache2.getNode(fqn); assertHasBeenInvalidated(n2, "Should have been invalidated"); assertHasBeenInvalidated(cache2.peek(fqn, true, true), "Should have been invalidated"); // now make sure cache2 is in sync with cache1: cache2.put(fqn, "key", "value"); dumpVersionInfo(cache1, cache2, fqn); Node n1 = cache1.getNode(fqn); assertHasBeenInvalidated(n1, "Should have been invalidated"); assertHasBeenInvalidated(cache1.peek(fqn, true, true), "Should have been invalidated"); assertEquals("value", cache2.get(fqn, "key")); // now test the invalidation: cache1.put(fqn, "key2", "value2"); dumpVersionInfo(cache1, cache2, fqn); assertEquals("value2", cache1.get(fqn, "key2")); n2 = cache2.getNode(fqn); assertHasBeenInvalidated(n2, "Should have been invalidated"); assertHasBeenInvalidated(cache2.peek(fqn, false, false), "Should have been invalidated"); // with tx's TransactionManager txm = cache2.getTransactionManager(); txm.begin(); cache2.put(fqn, "key", "value"); assertEquals("value", cache2.get(fqn, "key")); assertEquals("value2", cache1.get(fqn, "key2")); txm.commit(); n1 = cache1.getNode(fqn); assertHasBeenInvalidated(n1, "Should have been invalidated"); assertHasBeenInvalidated(cache1.peek(fqn, false, false), "Should have been invalidated"); assertEquals("value", cache2.get(fqn, "key")); // now test the invalidation again txm = cache1.getTransactionManager(); txm.begin(); cache1.put(fqn, "key2", "value2"); assertEquals("value", cache2.get(fqn, "key")); assertEquals("value2", cache1.get(fqn, "key2")); txm.commit(); assertEquals("value2", cache1.get(fqn, "key2")); n2 = cache2.getNode(fqn); assertHasBeenInvalidated(n2, "Should have been invalidated"); assertHasBeenInvalidated(cache2.peek(fqn, false, false), "Should have been invalidated"); // test a rollback txm = cache2.getTransactionManager(); txm.begin(); cache2.put(fqn, "key", "value"); assertEquals("value2", cache1.get(fqn, "key2")); assertEquals("value", cache2.get(fqn, "key")); txm.rollback(); assertEquals("value2", cache1.get(fqn, "key2")); n2 = cache2.getNode(fqn); assertHasBeenInvalidated(n2, "Should have been invalidated"); assertHasBeenInvalidated(cache2.peek(fqn, false, false), "Should have been invalidated"); } public void testPessimisticNonTransactionalWithCacheLoader() throws Exception { List> caches = createCachesWithSharedCL(false); cache1 = caches.get(0); cache2 = caches.get(1); Fqn fqn = Fqn.fromString("/a/b"); caches.get(0).put(fqn, "key", "value"); assertEquals("value", caches.get(0).get(fqn, "key")); assertEquals("value", caches.get(1).get(fqn, "key")); // now make sure cache2 is in sync with cache1: caches.get(1).put(fqn, "key", "value"); assertEquals("value", caches.get(1).get(fqn, "key")); assertEquals("value", caches.get(0).get(fqn, "key")); // now test the invalidation: caches.get(0).put(fqn, "key2", "value2"); assertEquals("value2", caches.get(0).get(fqn, "key2")); assertEquals("value2", caches.get(1).get(fqn, "key2")); assertEquals("value", caches.get(0).get(fqn, "key")); assertEquals("value", caches.get(1).get(fqn, "key")); } public void testPessimisticTransactionalWithCacheLoader() throws Exception { List> caches = createCachesWithSharedCL(false); cache1 = caches.get(0); cache2 = caches.get(1); Fqn fqn = Fqn.fromString("/a/b"); TransactionManager mgr = caches.get(0).getTransactionManager(); assertNull("Should be null", caches.get(0).get(fqn, "key")); assertNull("Should be null", caches.get(1).get(fqn, "key")); mgr.begin(); caches.get(0).put(fqn, "key", "value"); assertEquals("value", caches.get(0).get(fqn, "key")); mgr.commit(); assertEquals("value", caches.get(1).get(fqn, "key")); assertEquals("value", caches.get(0).get(fqn, "key")); mgr.begin(); caches.get(0).put(fqn, "key2", "value2"); assertEquals("value2", caches.get(0).get(fqn, "key2")); mgr.rollback(); assertEquals("value", caches.get(1).get(fqn, "key")); assertEquals("value", caches.get(0).get(fqn, "key")); assertNull("Should be null", caches.get(0).get(fqn, "key2")); assertNull("Should be null", caches.get(1).get(fqn, "key2")); } public void testOptimisticWithCacheLoader() throws Exception { List> caches = createCachesWithSharedCL(true); cache1 = caches.get(0); cache2 = caches.get(1); Fqn fqn = Fqn.fromString("/a/b"); TransactionManager mgr = caches.get(0).getTransactionManager(); assertNull("Should be null", caches.get(0).get(fqn, "key")); assertNull("Should be null", caches.get(1).get(fqn, "key")); mgr.begin(); caches.get(0).put(fqn, "key", "value"); assertEquals("value", caches.get(0).get(fqn, "key")); assertNull("Should be null", caches.get(1).get(fqn, "key")); mgr.commit(); assertEquals("value", caches.get(1).get(fqn, "key")); assertEquals("value", caches.get(0).get(fqn, "key")); mgr.begin(); caches.get(0).put(fqn, "key2", "value2"); assertEquals("value2", caches.get(0).get(fqn, "key2")); assertNull("Should be null", caches.get(1).get(fqn, "key2")); mgr.rollback(); assertEquals("value", caches.get(1).get(fqn, "key")); assertEquals("value", caches.get(0).get(fqn, "key")); assertNull("Should be null", caches.get(0).get(fqn, "key2")); assertNull("Should be null", caches.get(1).get(fqn, "key2")); } public void testInvalidationWithRegionBasedMarshalling() throws Exception { doRegionBasedTest(false); } public void testInvalidationWithRegionBasedMarshallingOptimistic() throws Exception { doRegionBasedTest(true); } protected void doRegionBasedTest(boolean optimistic) throws Exception { List> caches = new ArrayList>(); caches.add(createUnstartedCache(false)); caches.add(createUnstartedCache(false)); cache1 = caches.get(0); cache2 = caches.get(1); caches.get(0).getConfiguration().setUseRegionBasedMarshalling(true); caches.get(1).getConfiguration().setUseRegionBasedMarshalling(true); if (optimistic) { caches.get(0).getConfiguration().setNodeLockingScheme("OPTIMISTIC"); caches.get(1).getConfiguration().setNodeLockingScheme("OPTIMISTIC"); } caches.get(0).start(); caches.get(1).start(); TestingUtil.blockUntilViewsReceived(caches.toArray(new CacheSPI[0]), 5000); Fqn fqn = Fqn.fromString("/a/b"); assertNull("Should be null", caches.get(0).getNode(fqn)); assertNull("Should be null", caches.get(1).getNode(fqn)); caches.get(0).put(fqn, "key", "value"); assertEquals("expecting value", "value", caches.get(0).get(fqn, "key")); Node n = caches.get(1).getNode(fqn); assertHasBeenInvalidated(n, "Should have been invalidated"); // now put in caches.get(1), should fire an eviction caches.get(1).put(fqn, "key", "value2"); assertEquals("expecting value2", "value2", caches.get(1).get(fqn, "key")); n = caches.get(0).getNode(fqn); assertHasBeenInvalidated(n, "Should have been invalidated"); } public void testDeleteNonExistentPessimistic() throws Exception { deleteNonExistentTest(false); } /** * Test for JBCACHE-1297 * * @throws Exception */ public void testDeleteNonExistentOptimistic() throws Exception { deleteNonExistentTest(true); } private void deleteNonExistentTest(boolean optimistic) throws Exception { List> caches = new ArrayList>(); caches.add(createUnstartedCache(optimistic)); caches.add(createUnstartedCache(optimistic)); cache1 = caches.get(0); cache2 = caches.get(1); cache1.start(); cache2.start(); TestingUtil.blockUntilViewsReceived(caches.toArray(new CacheSPI[0]), 5000); Fqn fqn = Fqn.fromString("/a/b"); assertNull("Should be null", cache1.getNode(fqn)); assertNull("Should be null", cache2.getNode(fqn)); cache1.putForExternalRead(fqn, "key", "value"); assertEquals("value", cache1.getNode(fqn).get("key")); assertNull("Should be null", cache2.getNode(fqn)); // OK, here's the real test TransactionManager tm = cache2.getTransactionManager(); tm.begin(); try { // Remove a node that doesn't exist in cache2 cache2.removeNode(fqn); tm.commit(); } catch (Exception e) { String msg = "Unable to remove non-existent node " + fqn; log.error(msg, e); fail(msg + " -- " + e); } assertHasBeenInvalidated(cache1.getNode(fqn), "Should have been invalidated"); assertNull("Should be null", cache2.getNode(fqn)); } /** * Test for JBCACHE-1298. * * @throws Exception */ public void testAddOfDeletedNonExistent() throws Exception { cache1 = createCache(true); cache2 = createCache(true); TestingUtil.blockUntilViewsReceived(5000, cache1, cache2); Fqn fqn = Fqn.fromString("/a/b"); assertNull("Should be null", cache1.getNode(fqn)); assertNull("Should be null", cache2.getNode(fqn)); // OK, here's the real test TransactionManager tm = cache2.getTransactionManager(); tm.begin(); try { // Remove a node that doesn't exist in cache2 cache2.removeNode(fqn); tm.commit(); } catch (Exception e) { String msg = "Unable to remove non-existent node " + fqn; log.error(msg, e); fail(msg + " -- " + e); } // Actually, it shouldn't have been invalidated, should be null // But, this assertion will pass if it is null, and we want assertHasBeenInvalidated(cache1.getNode(fqn), "Should have been invalidated"); assertNull("Should be null", cache2.getNode(fqn)); cache1.getInvocationContext().getOptionOverrides().setDataVersion(new DefaultDataVersion()); cache1.put(fqn, "key", "value"); assertEquals("value", cache1.getNode(fqn).get("key")); assertHasBeenInvalidated(cache2.getNode(fqn), "Should have been invalidated"); } protected CacheSPI createUnstartedCache(boolean optimistic) throws Exception { CacheSPI cache = (CacheSPI) new DefaultCacheFactory().createCache(false); cache.getConfiguration().setClusterName("MyCluster"); cache.getConfiguration().setStateRetrievalTimeout(3000); cache.getConfiguration().setCacheMode(Configuration.CacheMode.INVALIDATION_SYNC); if (optimistic) cache.getConfiguration().setNodeLockingScheme("OPTIMISTIC"); cache.getConfiguration().setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); toClean.add(cache); return cache; } protected CacheSPI createCache(boolean optimistic) throws Exception { CacheSPI cache = createUnstartedCache(optimistic); cache.start(); toClean.add(cache); return cache; } protected List> createCachesWithSharedCL(boolean optimistic) throws Exception { List> caches = new ArrayList>(); caches.add(createUnstartedCache(optimistic)); caches.add(createUnstartedCache(optimistic)); caches.get(0).getConfiguration().setCacheLoaderConfig(getCacheLoaderConfig()); caches.get(1).getConfiguration().setCacheLoaderConfig(getCacheLoaderConfig()); caches.get(0).start(); caches.get(1).start(); toClean.addAll(caches); return caches; } protected CacheLoaderConfig getCacheLoaderConfig() throws Exception { String xml = " \n" + " shared\n" + " false\n" + " \n" + " \n" + " org.jboss.cache.loader.DummySharedInMemoryCacheLoader\n" + " false\n" + " false\n" + " false\n" + " \n" + " \n" + " "; Element element = XmlHelper.stringToElement(xml); return XmlConfigurationParser.parseCacheLoaderConfig(element); } protected void assertHasBeenInvalidated(Node n, String message) { // depending on how n was retrieved! if (n == null) { assert true : message; } else { assert !n.isValid() : message; } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/invalidation/VersionInconsistencyTest.java0000644000175000017500000000560111017455042033017 0ustar twernertwernerpackage org.jboss.cache.invalidation; import org.jboss.cache.*; import org.jboss.cache.config.Configuration; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.optimistic.DefaultDataVersion; import org.jboss.cache.transaction.DummyTransactionManagerLookup; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.Transaction; import javax.transaction.TransactionManager; /** * This test simulates the problem described in JBCACHE-1155 * * @author Manik Surtani * @since 2.1.0 */ @Test(groups = {"functional", "jgroups"}) public class VersionInconsistencyTest { private Cache cache1, cache2; private TransactionManager tm1, tm2; private Fqn node = Fqn.fromString("/a"); @BeforeMethod public void setUp() { cache1 = new DefaultCacheFactory().createCache(false); cache2 = new DefaultCacheFactory().createCache(false); cache1.getConfiguration().setCacheMode(Configuration.CacheMode.INVALIDATION_SYNC); cache2.getConfiguration().setCacheMode(Configuration.CacheMode.INVALIDATION_SYNC); cache1.getConfiguration().setNodeLockingScheme(Configuration.NodeLockingScheme.OPTIMISTIC); cache2.getConfiguration().setNodeLockingScheme(Configuration.NodeLockingScheme.OPTIMISTIC); cache1.getConfiguration().setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName()); cache2.getConfiguration().setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName()); cache1.start(); cache2.start(); tm1 = cache1.getConfiguration().getRuntimeConfig().getTransactionManager(); tm2 = cache2.getConfiguration().getRuntimeConfig().getTransactionManager(); TestingUtil.blockUntilViewsReceived(1000, cache1, cache2); } @AfterMethod public void tearDown() { cache1.stop(); cache2.stop(); } public void dataInconsistency() throws Exception { tm1.begin(); cache1.put(node, "k", "v-older"); Transaction t1 = tm1.suspend(); tm2.begin(); cache2.put(node, "k", "v-newer"); tm2.commit(); tm1.resume(t1); try { tm1.commit(); assert false : "Should not be allowed to commit with older data!!"; } catch (Exception good) { } // the NEWER version of the data should be available, not the OLDER one. Object val = cache1.get(node, "k"); System.out.println("val = " + val); assert val == null : "Older data should not have committed"; val = cache2.get(node, "k"); System.out.println("val = " + val); assert val.equals("v-newer"); // test node versions NodeSPI n = ((CacheSPI) cache1).peek(node, true, true); assert ((DefaultDataVersion) n.getVersion()).getRawVersion() == 1 : "Version should be 1"; } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/invalidation/TombstoneEvictionTest.java0000644000175000017500000001157111017455042032277 0ustar twernertwernerpackage org.jboss.cache.invalidation; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.EvictionConfig; import org.jboss.cache.config.EvictionRegionConfig; import org.jboss.cache.eviction.FIFOConfiguration; import org.jboss.cache.transaction.DummyTransactionManagerLookup; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import java.util.ArrayList; import java.util.List; /** * Make sure tombstones are evicted * * @author Manik Surtani * @since 2.1.0 */ @Test(groups = {"functional"}) public class TombstoneEvictionTest { private CacheSPI c1, c2; private Fqn fqn = Fqn.fromString("/data/test"); private Fqn dummy = Fqn.fromString("/data/dummy"); private long evictionWaitTime = 4100; @BeforeMethod public void setUp() throws Exception { log.trace("**** setup called"); c1 = (CacheSPI) new DefaultCacheFactory().createCache(false); // the FIFO policy cfg FIFOConfiguration cfg = new FIFOConfiguration(); cfg.setMaxNodes(1); cfg.setMinTimeToLiveSeconds(0); // the region configuration EvictionRegionConfig regionCfg = new EvictionRegionConfig(); regionCfg.setRegionFqn(dummy.getParent()); regionCfg.setRegionName(dummy.getParent().toString()); regionCfg.setEvictionPolicyConfig(cfg); // set regions in a list List evictionRegionConfigs = new ArrayList(); evictionRegionConfigs.add(regionCfg); EvictionConfig ec = new EvictionConfig(); ec.setWakeupIntervalSeconds(2); ec.setEvictionRegionConfigs(evictionRegionConfigs); c1.getConfiguration().setCacheMode(Configuration.CacheMode.INVALIDATION_SYNC); c1.getConfiguration().setNodeLockingScheme(Configuration.NodeLockingScheme.OPTIMISTIC); c1.getConfiguration().setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName()); c1.getConfiguration().setEvictionConfig(ec); c2 = (CacheSPI) new DefaultCacheFactory().createCache(c1.getConfiguration().clone(), false); c1.start(); c2.start(); TestingUtil.blockUntilViewsReceived(60000, c1, c2); } @AfterMethod public void tearDown() { TestingUtil.killCaches(c1, c2); } private static final Log log = LogFactory.getLog(TombstoneEvictionTest.class); public void testControl() { log.trace("***** entered testControl()"); c1.put(fqn, "k", "v"); c1.put(dummy, "k", "v"); log.trace("***** nodes were added testControl()"); assert c1.peek(fqn, false, true) != null : "Node should exist"; assert c1.peek(dummy, false, true) != null : "Node should exist"; log.trace("***** testControl() right before sleeping"); TestingUtil.sleepThread(evictionWaitTime); log.trace("***** testControl() right after sleeping"); assert c1.peek(fqn, false, true) == null : "Should have evicted"; assert c1.peek(dummy, false, true) != null : "Node should exist"; } public void testWithInvalidationMarkers() { log.trace(" **** testWithInvalidationMarkers() before put"); c1.put(fqn, "k", "v"); c1.put(dummy, "k", "v"); log.trace(" **** testWithInvalidationMarkers() after put"); assert c1.peek(fqn, false, true) != null : "Node should exist"; assert c1.peek(dummy, false, true) != null : "Node should exist"; assert c2.peek(fqn, false, true) != null : "Node should exist"; assert c2.peek(dummy, false, true) != null : "Node should exist"; TestingUtil.sleepThread(evictionWaitTime); assert c1.peek(fqn, false, true) == null : "Should have evicted"; assert c1.peek(dummy, false, true) != null : "Node should exist"; assert c2.peek(fqn, false, true) == null : "Should have evicted"; assert c2.peek(dummy, false, true) != null : "Node should exist"; } public void testWithTombstones() { c1.put(fqn, "k", "v"); c1.removeNode(fqn); c1.put(dummy, "k", "v"); assert c1.peek(fqn, false, true) != null : "Node should exist"; assert c1.peek(dummy, false, true) != null : "Node should exist"; assert c2.peek(fqn, false, true) != null : "Node should exist"; assert c2.peek(dummy, false, true) != null : "Node should exist"; TestingUtil.sleepThread(evictionWaitTime); assert c1.peek(fqn, false, true) == null : "Should have evicted"; assert c1.peek(dummy, false, true) != null : "Node should exist"; assert c2.peek(fqn, false, true) == null : "Should have evicted"; assert c2.peek(dummy, false, true) != null : "Node should exist"; } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/util/0000755000175000017500000000000011376173770023425 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/util/TestingUtil.java0000644000175000017500000004644011031017377026537 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.util; import org.jboss.cache.Cache; import org.jboss.cache.CacheSPI; import org.jboss.cache.CacheStatus; import org.jboss.cache.Fqn; import org.jboss.cache.commands.VisitableCommand; import org.jboss.cache.factories.CommandsFactory; import org.jboss.cache.factories.ComponentRegistry; import org.jboss.cache.interceptors.InterceptorChain; import org.jboss.cache.interceptors.base.CommandInterceptor; import org.jboss.cache.invocation.CacheInvocationDelegate; import org.jboss.cache.loader.CacheLoader; import org.jboss.cache.loader.CacheLoaderManager; import org.jboss.cache.lock.LockManager; import org.jgroups.JChannel; import java.io.File; import java.lang.reflect.Field; import java.util.Arrays; import java.util.List; import java.util.Random; /** * Utilities for unit testing JBossCache. * * @author Brian Stansberry * @version $Revision$ */ public class TestingUtil { private static Random random = new Random(); /** * Extracts the value of a field in a given target instance using reflection, able to extract private fields as well. * * @param target object to extract field from * @param fieldName name of field to extract * @return field value */ public static Object extractField(Object target, String fieldName) { return extractField(target.getClass(), target, fieldName); } public static void replaceField(Object newValue, String fieldName, Object owner, Class baseType) { Field field; try { field = baseType.getDeclaredField(fieldName); field.setAccessible(true); field.set(owner, newValue); } catch (Exception e) { throw new RuntimeException(e);//just to simplify exception handeling } } public static Object extractField(Class type, Object target, String fieldName) { Field field; try { field = type.getDeclaredField(fieldName); field.setAccessible(true); return field.get(target); } catch (Exception e) { if (type.equals(Object.class)) { e.printStackTrace(); return null; } else { // try with superclass!! return extractField(type.getSuperclass(), target, fieldName); } } } public static T findInterceptor(CacheSPI cache, Class interceptorToFind) { for (CommandInterceptor i : cache.getInterceptorChain()) { if (interceptorToFind.isInstance(i)) return interceptorToFind.cast(i); } return null; } /** * Injects an interceptor after a specified interceptor in a running cache. Your new interceptor need not be * initialised with pointers to the next interceptor, etc. as this method does all that for you, including calling * setCache(). * * @param cache running cache instance * @param interceptorToInject interceptor instance to inject. * @param interceptorAfterWhichToInject class of interceptor to search for in the chain and after which to add your interceptor */ public static void injectInterceptor(CacheSPI cache, CommandInterceptor interceptorToInject, Class interceptorAfterWhichToInject) { cache.addInterceptor(interceptorToInject, interceptorAfterWhichToInject); } /** * Loops, continually calling {@link #areCacheViewsComplete(org.jboss.cache.Cache[])} * until it either returns true or timeout ms have elapsed. * * @param caches caches which must all have consistent views * @param timeout max number of ms to loop * @throws RuntimeException if timeout ms have elapse without * all caches having the same number of members. */ public static void blockUntilViewsReceived(Cache[] caches, long timeout) { long failTime = System.currentTimeMillis() + timeout; while (System.currentTimeMillis() < failTime) { sleepThread(100); if (areCacheViewsComplete(caches)) { return; } } throw new RuntimeException("timed out before caches had complete views"); } /** * Version of blockUntilViewsReceived that uses varargs */ public static void blockUntilViewsReceived(long timeout, Cache... caches) { blockUntilViewsReceived(caches, timeout); } /** * Loops, continually calling {@link #areCacheViewsComplete(org.jboss.cache.CacheSPI[])} * until it either returns true or timeout ms have elapsed. * * @param caches caches which must all have consistent views * @param timeout max number of ms to loop * @throws RuntimeException if timeout ms have elapse without * all caches having the same number of members. */ public static void blockUntilViewsReceived(CacheSPI[] caches, long timeout) { long failTime = System.currentTimeMillis() + timeout; while (System.currentTimeMillis() < failTime) { sleepThread(100); if (areCacheViewsComplete(caches)) { return; } } throw new RuntimeException("timed out before caches had complete views"); } /** * An overloaded version of {@link #blockUntilViewsReceived(long,org.jboss.cache.Cache[])} that allows for 'shrinking' clusters. * I.e., the usual method barfs if there are more members than expected. This one takes a param (barfIfTooManyMembers) which, * if false, will NOT barf but will wait until the cluster 'shrinks' to the desired size. Useful if in tests, you kill * a member and want to wait until this fact is known across the cluster. * * @param timeout * @param barfIfTooManyMembers * @param caches */ public static void blockUntilViewsReceived(long timeout, boolean barfIfTooManyMembers, Cache... caches) { long failTime = System.currentTimeMillis() + timeout; while (System.currentTimeMillis() < failTime) { sleepThread(100); if (areCacheViewsComplete(caches, barfIfTooManyMembers)) { return; } } throw new RuntimeException("timed out before caches had complete views"); } /** * Loops, continually calling {@link #areCacheViewsComplete(org.jboss.cache.CacheSPI[])} * until it either returns true or timeout ms have elapsed. * * @param groupSize number of caches expected in the group * @param timeout max number of ms to loop * @throws RuntimeException if timeout ms have elapse without * all caches having the same number of members. */ public static void blockUntilViewReceived(CacheSPI cache, int groupSize, long timeout) { blockUntilViewReceived(cache, groupSize, timeout, true); } public static void blockUntilViewReceived(CacheSPI cache, int groupSize, long timeout, boolean barfIfTooManyMembersInView) { long failTime = System.currentTimeMillis() + timeout; while (System.currentTimeMillis() < failTime) { sleepThread(100); if (isCacheViewComplete(cache, groupSize, barfIfTooManyMembersInView)) { return; } } throw new RuntimeException("timed out before caches had complete views"); } /** * Checks each cache to see if the number of elements in the array * returned by {@link CacheSPI#getMembers()} matches the size of * the caches parameter. * * @param caches caches that should form a View * @return true if all caches have * caches.length members; false otherwise * @throws IllegalStateException if any of the caches have MORE view * members than caches.length */ public static boolean areCacheViewsComplete(Cache[] caches) { return areCacheViewsComplete(caches, true); } public static boolean areCacheViewsComplete(Cache[] caches, boolean barfIfTooManyMembers) { int memberCount = caches.length; for (int i = 0; i < memberCount; i++) { if (!isCacheViewComplete(caches[i], memberCount, barfIfTooManyMembers)) { return false; } } return true; } /** * Checks each cache to see if the number of elements in the array * returned by {@link org.jboss.cache.RPCManager#getMembers()} matches the size of * the caches parameter. * * @param caches caches that should form a View * @return true if all caches have * caches.length members; false otherwise * @throws IllegalStateException if any of the caches have MORE view * members than caches.length */ public static boolean areCacheViewsComplete(CacheSPI[] caches) { if (caches == null) throw new NullPointerException("Cache impl array is null"); Cache[] c = new Cache[caches.length]; for (int i = 0; i < caches.length; i++) c[i] = caches[i]; return areCacheViewsComplete(c); } /** * @param cache * @param memberCount */ public static boolean isCacheViewComplete(CacheSPI cache, int memberCount) { List members = cache.getRPCManager().getMembers(); if (members == null || memberCount > members.size()) { return false; } else if (memberCount < members.size()) { // This is an exceptional condition StringBuilder sb = new StringBuilder("Cache at address "); sb.append(cache.getRPCManager().getLocalAddress()); sb.append(" had "); sb.append(members.size()); sb.append(" members; expecting "); sb.append(memberCount); sb.append(". Members were ("); for (int j = 0; j < members.size(); j++) { if (j > 0) { sb.append(", "); } sb.append(members.get(j)); } sb.append(')'); throw new IllegalStateException(sb.toString()); } return true; } /** * @param c * @param memberCount */ public static boolean isCacheViewComplete(Cache c, int memberCount) { return isCacheViewComplete(c, memberCount, true); } public static boolean isCacheViewComplete(Cache c, int memberCount, boolean barfIfTooManyMembers) { CacheSPI cache = (CacheSPI) c; List members = cache.getMembers(); if (members == null || memberCount > members.size()) { return false; } else if (memberCount < members.size()) { if (barfIfTooManyMembers) { // This is an exceptional condition StringBuilder sb = new StringBuilder("Cache at address "); sb.append(cache.getLocalAddress()); sb.append(" had "); sb.append(members.size()); sb.append(" members; expecting "); sb.append(memberCount); sb.append(". Members were ("); for (int j = 0; j < members.size(); j++) { if (j > 0) { sb.append(", "); } sb.append(members.get(j)); } sb.append(')'); throw new IllegalStateException(sb.toString()); } else return false; } return true; } /** * Puts the current thread to sleep for the desired number of ms, suppressing * any exceptions. * * @param sleeptime number of ms to sleep */ public static void sleepThread(long sleeptime) { try { Thread.sleep(sleeptime); } catch (InterruptedException ie) { } } public static void sleepRandom(int maxTime) { sleepThread(random.nextInt(maxTime)); } public static void recursiveFileRemove(String directoryName) { File file = new File(directoryName); recursiveFileRemove(file); } public static void recursiveFileRemove(File file) { if (file.exists()) { System.out.println("Deleting file " + file); recursivedelete(file); } } private static void recursivedelete(File f) { if (f.isDirectory()) { File[] files = f.listFiles(); for (File file : files) { recursivedelete(file); } } //System.out.println("File " + f.toURI() + " deleted = " + f.delete()); f.delete(); } /** * Kills a cache - stops it, clears any data in any cache loaders, and rolls back any associated txs */ public static void killCaches(Cache... caches) { for (Cache c : caches) { if (c != null && c.getCacheStatus() == CacheStatus.STARTED) { CacheSPI spi = (CacheSPI) c; if (spi.getTransactionManager() != null) { try { spi.getTransactionManager().rollback(); } catch (Exception e) { // don't care } } CacheLoaderManager clm = spi.getCacheLoaderManager(); CacheLoader cl = clm == null ? null : clm.getCacheLoader(); if (cl != null) { try { cl.remove(Fqn.ROOT); } catch (Exception e) { // don't care } } spi.stop(); spi.destroy(); } } } /** * Clears any associated transactions with the current thread in the caches' transaction managers. */ public static void killTransactions(Cache... caches) { for (Cache c : caches) { if (c != null && c.getCacheStatus() == CacheStatus.STARTED) { CacheSPI ci = (CacheSPI) c; if (ci.getTransactionManager() != null) { try { ci.getTransactionManager().rollback(); } catch (Exception e) { // don't care } } } } } /** * For testing only - introspects a cache and extracts the ComponentRegistry * * @param cache cache to introspect * @return component registry */ public static ComponentRegistry extractComponentRegistry(Cache cache) { return (ComponentRegistry) extractField(cache, "componentRegistry"); } public static LockManager extractLockManager(Cache cache) { return extractComponentRegistry(cache).getComponent(LockManager.class); } /** * For testing only - introspects a cache and extracts the ComponentRegistry * * @param ci interceptor chain to introspect * @return component registry */ public static ComponentRegistry extractComponentRegistry(InterceptorChain ci) { return (ComponentRegistry) extractField(ci, "componentRegistry"); } /** * Replaces the existing interceptor chain in the cache wih one represented by the interceptor passed in. This * utility updates dependencies on all components that rely on the interceptor chain as well. * * @param cache cache that needs to be altered * @param interceptor the first interceptor in the new chain. */ public static void replaceInterceptorChain(CacheSPI cache, CommandInterceptor interceptor) { ComponentRegistry cr = extractComponentRegistry(cache); // make sure all interceptors here are wired. CommandInterceptor i = interceptor; do { cr.wireDependencies(i); } while ((i = i.getNext()) != null); InterceptorChain inch = cr.getComponent(InterceptorChain.class); inch.setFirstInChain(interceptor); } /** * Retrieves the remote delegate for a given cache. It is on this remote delegate that the JGroups RPCDispatcher * invokes remote methods. * * @param cache cache instance for which a remote delegate is to be retrieved * @return remote delegate, or null if the cacge is not configured for replication. */ public static CacheInvocationDelegate getInvocationDelegate(CacheSPI cache) { ComponentRegistry cr = extractComponentRegistry(cache); return cr.getComponent(CacheInvocationDelegate.class); } /** * Blocks until the cache has reached a specified state. * * @param cache cache to watch * @param cacheStatus status to wait for * @param timeout timeout to wait for */ public static void blockUntilCacheStatusAchieved(Cache cache, CacheStatus cacheStatus, long timeout) { CacheSPI spi = (CacheSPI) cache; long killTime = System.currentTimeMillis() + timeout; while (System.currentTimeMillis() < killTime) { if (spi.getCacheStatus() == cacheStatus) return; sleepThread(50); } throw new RuntimeException("Timed out waiting for condition"); } public static void replicateCommand(CacheSPI cache, VisitableCommand command) throws Throwable { ComponentRegistry cr = extractComponentRegistry(cache); InterceptorChain ic = cr.getComponent(InterceptorChain.class); ic.invoke(command); } public static void blockUntilViewsReceived(int timeout, List caches) { blockUntilViewsReceived((Cache[]) caches.toArray(new Cache[]{}), timeout); } public static CommandsFactory extractCommandsFactory(CacheSPI cache) { return (CommandsFactory) extractField(cache, "commandsFactory"); } public static String getJGroupsAttribute(Cache cache, String protocol, String attribute) { String s = ((JChannel) ((CacheSPI) cache).getRPCManager().getChannel()).getProperties(); String[] protocols = s.split(":"); String attribs = null; for (String p : protocols) { boolean hasAttribs = p.contains("("); String name = hasAttribs ? p.substring(0, p.indexOf('(')) : p; attribs = hasAttribs ? p.substring(p.indexOf('(') + 1, p.length() - 1) : null; if (name.equalsIgnoreCase(protocol)) break; } if (attribs != null) { String[] attrArray = attribs.split(";"); for (String a : attrArray) { String[] kvPairs = a.split("="); if (kvPairs[0].equalsIgnoreCase(attribute)) return kvPairs[1]; } } return null; } public static void dumpCacheContents(List caches) { System.out.println("**** START: Cache Contents ****"); int count = 1; for (Object o : caches) { CacheSPI c = (CacheSPI) o; if (c == null) { System.out.println(" ** Cache " + count + " is null!"); } else { System.out.println(" ** Cache " + count + " is " + c.getLocalAddress()); System.out.println(" " + CachePrinter.printCacheLockingInfo(c)); } count++; } System.out.println("**** END: Cache Contents ****"); } public static void dumpCacheContents(Cache... caches) { dumpCacheContents(Arrays.asList(caches)); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/util/internals/0000755000175000017500000000000011376173767025432 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/util/internals/ReplicationListener.java0000644000175000017500000002341611075062420032235 0ustar twernertwernerpackage org.jboss.cache.util.internals; import org.jboss.cache.Cache; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.RPCManager; import org.jboss.cache.commands.ReplicableCommand; import org.jboss.cache.commands.remote.ReplicateCommand; import org.jboss.cache.commands.tx.PrepareCommand; import org.jboss.cache.factories.ComponentRegistry; import org.jboss.cache.marshall.CommandAwareRpcDispatcher; import org.jboss.cache.marshall.InactiveRegionAwareRpcDispatcher; import org.jboss.cache.marshall.Marshaller; import org.jboss.cache.marshall.RegionalizedMethodCall; import org.jboss.cache.util.TestingUtil; import org.jgroups.blocks.RpcDispatcher; import org.jgroups.blocks.RpcDispatcher.Marshaller2; import org.jgroups.util.Buffer; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.Arrays; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; /** * Utility class that notifies when certain commands were asynchronously replicated on secondary cache. * Especially useful for avaoiding Thread.sleep() statements. *

* Usage: *

 *   Cache c1, c2; //these being two async caches
 *   AsyncReplicationListener listener2 = new AsyncReplicationListener(c2);
 *   listener2.expect(PutKeyValueCommand.class);
 *   c1.put(fqn, key, value);
 *   listener2.waitForReplicationToOccur(1000); // -this will block here untill c2 recieves the PutKeyValueCommand command
 * 
* Lifecycle - after being used (i.e. waitForReplicationToOccur returns sucessfully) the object returns to the * non-initialized state and *can* be reused through expect-wait cycle. * Note: this class might be used aswell for sync caches, e.g. a test could have subclasses which use sync and * async replication * * @author Mircea.Markus@jboss.com * @since 2.2 */ public class ReplicationListener { private CountDownLatch latch = new CountDownLatch(1); private Set> expectedCommands; /** * Builds a listener that will observe the given cache for recieving replication commands. */ public ReplicationListener(Cache cache) { ComponentRegistry componentRegistry = TestingUtil.extractComponentRegistry(cache); RPCManager rpcManager = componentRegistry.getComponent(RPCManager.class); CommandAwareRpcDispatcher realDispatcher = (CommandAwareRpcDispatcher) TestingUtil.extractField(rpcManager, "rpcDispatcher"); RpcDispatcher.Marshaller2 realMarshaller = (Marshaller2) realDispatcher.getMarshaller(); RpcDispatcher.Marshaller delegate = null; if (realDispatcher instanceof InactiveRegionAwareRpcDispatcher) delegate = new RegionMarshallerDelegate((Marshaller) realMarshaller); else delegate = new MarshallerDelegate(realMarshaller); realDispatcher.setMarshaller(delegate); realDispatcher.setRequestMarshaller(delegate); realDispatcher.setResponseMarshaller(delegate); } private class MarshallerDelegate implements RpcDispatcher.Marshaller { RpcDispatcher.Marshaller marshaller; private MarshallerDelegate(RpcDispatcher.Marshaller marshaller) { this.marshaller = marshaller; } public byte[] objectToByteBuffer(Object obj) throws Exception { return marshaller.objectToByteBuffer(obj); } public Object objectFromByteBuffer(byte bytes[]) throws Exception { Object result = marshaller.objectFromByteBuffer(bytes); if (result instanceof ReplicateCommand && expectedCommands != null) { ReplicateCommand replicateCommand = (ReplicateCommand) result; return new ReplicateCommandDelegate(replicateCommand); } return result; } } /** * We want the notification to be performed only *after* the remote command is executed. */ private class ReplicateCommandDelegate extends ReplicateCommand { ReplicateCommand realOne; private ReplicateCommandDelegate(ReplicateCommand realOne) { this.realOne = realOne; } @Override public Object perform(InvocationContext ctx) throws Throwable { try { return realOne.perform(ctx); } finally { System.out.println("Processed command: " + realOne); Iterator> it = expectedCommands.iterator(); while (it.hasNext()) { Class replicableCommandClass = it.next(); if (realOne.containsCommandType(replicableCommandClass)) { it.remove(); } else if (realOne.getSingleModification() instanceof PrepareCommand) //explicit transaction { PrepareCommand prepareCommand = (PrepareCommand) realOne.getSingleModification(); if (prepareCommand.containsModificationType(replicableCommandClass)) { it.remove(); } } } if (expectedCommands.isEmpty()) { latch.countDown(); } } } } /** * Needed for region based marshalling. */ private class RegionMarshallerDelegate implements Marshaller { private Marshaller realOne; private RegionMarshallerDelegate(Marshaller realOne) { this.realOne = realOne; } public void objectToObjectStream(Object obj, ObjectOutputStream out) throws Exception { realOne.objectToObjectStream(obj, out); } public Object objectFromObjectStream(ObjectInputStream in) throws Exception { return realOne.objectFromObjectStream(in); } public Object objectFromStream(InputStream is) throws Exception { return realOne.objectFromStream(is); } public void objectToObjectStream(Object obj, ObjectOutputStream out, Fqn region) throws Exception { realOne.objectToObjectStream(obj, out, region); } public RegionalizedMethodCall regionalizedMethodCallFromByteBuffer(byte[] buffer) throws Exception { RegionalizedMethodCall result = realOne.regionalizedMethodCallFromByteBuffer(buffer); if (result.command instanceof ReplicateCommand && expectedCommands != null) { ReplicateCommand replicateCommand = (ReplicateCommand) result.command; result.command = new ReplicateCommandDelegate(replicateCommand); } return result; } public RegionalizedMethodCall regionalizedMethodCallFromObjectStream(ObjectInputStream in) throws Exception { return realOne.regionalizedMethodCallFromObjectStream(in); } public byte[] objectToByteBuffer(Object o) throws Exception { return realOne.objectToByteBuffer(o); } public Object objectFromByteBuffer(byte[] bytes) throws Exception { return realOne.objectFromByteBuffer(bytes); } public Buffer objectToBuffer(Object obj) throws Exception { return realOne.objectToBuffer(obj); } public Object objectFromByteBuffer(byte[] buf, int offset, int length) throws Exception { return realOne.objectFromByteBuffer(buf, offset, length); } } /** * Blocks for the elements specified through {@link #expect(Class[])} invocations to be replicated in this cache. * if replication does not occur in the give timeout then an exception is being thrown. */ public void waitForReplicationToOccur(long timeoutMillis) { System.out.println("enter... ReplicationListener.waitForReplicationToOccur"); waitForReplicationToOccur(timeoutMillis, TimeUnit.MILLISECONDS); System.out.println("exit... ReplicationListener.waitForReplicationToOccur"); } /** * Similar to {@link #waitForReplicationToOccur(long)} except that this method provides more flexibility in time units. * * @param timeout the maximum time to wait * @param timeUnit the time unit of the timeout argument. */ public void waitForReplicationToOccur(long timeout, TimeUnit timeUnit) { assert expectedCommands != null : "there are no replication expectations; please use AsyncReplicationListener.expect(...) before calling this method"; try { if (!latch.await(timeout, timeUnit)) { assert false : "waiting for more than " + timeout + " " + timeUnit + " and following commands did not replicate: " + expectedCommands; } } catch (InterruptedException e) { throw new IllegalStateException("unexpected", e); } finally { expectedCommands = null; latch = new CountDownLatch(1); } } /** * {@link #waitForReplicationToOccur(long)} will block untill all the commands specified here are being replicated * to this cache. The method can be called several times with various arguments. */ public void expect(Class... expectedCommands) { if (this.expectedCommands == null) { this.expectedCommands = new HashSet>(); } this.expectedCommands.addAll(Arrays.asList(expectedCommands)); } /** * Waits untill first command is replicated. */ public void expectAny() { expect(); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/util/internals/ViewChangeListener.java0000644000175000017500000000307211017455042032002 0ustar twernertwernerpackage org.jboss.cache.util.internals; import org.jboss.cache.Cache; import org.jboss.cache.notifications.annotation.CacheListener; import org.jboss.cache.notifications.annotation.ViewChanged; import org.jboss.cache.notifications.event.ViewChangedEvent; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; /** * A class that registers a cache listener with a given cache, and waits for a view change on the cache. *

* Sample usage: *

 *    Cache c = getCache();
 *    ViewChangeListener vcl = new ViewChangeListener(c);
 *    assert vcl.waitForViewChange(60, TimeUnit.SECONDS); // will block for up to 60 seconds for a view change on cache c
 * 
*/ @CacheListener public class ViewChangeListener { CountDownLatch latch; /** * Constructs a view change listener * * @param cache cache to listen on for view change events */ public ViewChangeListener(Cache cache) { this.latch = new CountDownLatch(1); cache.addCacheListener(this); } @ViewChanged public void handleViewChange(ViewChangedEvent e) { if (!e.isPre()) latch.countDown(); } /** * Waits for up to millis milliseconds for a view change to be received. * * @param timeout length of time to wait for a view change * @param unit time unit to use * @return true if a view change is received, false otherwise. * @throws InterruptedException */ public boolean waitForViewChange(long timeout, TimeUnit unit) throws InterruptedException { return latch.await(timeout, unit); } }jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/util/internals/EvictionController.java0000644000175000017500000000646711020007335032103 0ustar twernertwernerpackage org.jboss.cache.util.internals; import org.jboss.cache.Cache; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.Region; import org.jboss.cache.RegionManager; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.config.EvictionConfig; import org.jboss.cache.config.EvictionRegionConfig; import org.jboss.cache.eviction.EvictionTimerTask; import org.jboss.cache.eviction.LRUConfiguration; import java.lang.reflect.Method; import java.util.Timer; /** * when used on a cache will disable defaul eviction behavior and it will supply means of kicking off evction * programmatically. It is intended for replcaing Thread.sleep(xyz) - like statements in which the executing tests wait * untill eviction finishes. * * @author Mircea.Markus@jboss.com * @since 2.2 */ public class EvictionController { CacheSPI cache; EvictionTimerTask timerTask; public EvictionController(Cache cache) { this.cache = (CacheSPI) cache; RegionManager regionManager = this.cache.getRegionManager(); if (regionManager == null) { throw new IllegalStateException("Null region manager; is the cache started?"); } timerTask = (EvictionTimerTask) TestingUtil.extractField(regionManager, "evictionTimerTask"); if (timerTask == null) { throw new IllegalStateException("No timer task!!!"); } Timer evictionThread = (Timer) TestingUtil.extractField(timerTask, "evictionThread"); evictionThread.cancel(); } public void startEviction() { try { Method method = EvictionTimerTask.class.getDeclaredMethod("processRegions", new Class[]{}); method.setAccessible(true); method.invoke(timerTask); } catch (Exception e) { e.printStackTrace(); throw new IllegalStateException(e); } } /** * Evicts the given region but only after ensuring that region's TTL passed. */ public void evictRegionWithTimeToLive(String region) throws Exception { EvictionConfig evConfig = cache.getConfiguration().getEvictionConfig(); EvictionRegionConfig erConfig = evConfig.getEvictionRegionConfig(region); if (erConfig == null) { throw new IllegalStateException("No such region!"); } int ttl = 0; if (erConfig.getEvictionPolicyConfig() instanceof LRUConfiguration) { LRUConfiguration configuration = (LRUConfiguration) erConfig.getEvictionPolicyConfig(); ttl = configuration.getTimeToLiveSeconds(); } else { throw new IllegalArgumentException("Only LRU being handled for now; please add other implementations here"); } TestingUtil.sleepThread(ttl * 1000 + 500); evictRegion(region); } /** * Only evicts the given region. */ public void evictRegion(String regionStr) throws Exception { for (Region region : timerTask.getProcessedRegions()) { if (region.getFqn().equals(Fqn.fromString(regionStr))) { Method method = EvictionTimerTask.class.getDeclaredMethod("handleRegion", Region.class); method.setAccessible(true); method.invoke(timerTask, region); } } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/util/internals/ReplicationQueueNotifier.java0000644000175000017500000000570111020007335033223 0ustar twernertwernerpackage org.jboss.cache.util.internals; import org.jboss.cache.Cache; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.config.Configuration; import org.jboss.cache.cluster.ReplicationQueue; import org.jboss.cache.factories.ComponentRegistry; import org.jboss.cache.interceptors.BaseRpcInterceptor; import org.jboss.cache.interceptors.InterceptorChain; import org.jboss.cache.interceptors.base.CommandInterceptor; import org.jboss.cache.invocation.CacheInvocationDelegate; import java.util.List; /** * Knows how to notify one whether on certain state changes in the replication queue. * * @author Mircea.Markus@jboss.com * @since 2.2 */ public class ReplicationQueueNotifier { private CacheInvocationDelegate cache; private Object replicated = new Object(); public ReplicationQueueNotifier(Cache cache) { this.cache = (CacheInvocationDelegate) cache; if (!isAsync(cache)) { throw new RuntimeException("No queue events expected on a sync cache!"); } replaceInternal(); } private boolean isAsync(Cache cache) { return cache.getConfiguration().getCacheMode() == Configuration.CacheMode.INVALIDATION_ASYNC || cache.getConfiguration().getCacheMode() == Configuration.CacheMode.REPL_ASYNC; } private void replaceInternal() { ComponentRegistry componentRegistry = TestingUtil.extractComponentRegistry(cache); InterceptorChain ic = componentRegistry.getComponent(InterceptorChain.class); List commands = ic.getInterceptorsWhichExtend(BaseRpcInterceptor.class); for (CommandInterceptor interceptor: commands) { ReplicationQueue original = (ReplicationQueue) TestingUtil.extractField(BaseRpcInterceptor.class, interceptor, "replicationQueue"); TestingUtil.replaceField(new ReplicationQueueDelegate(original),"replicationQueue", interceptor, BaseRpcInterceptor.class); log("replaced replicationQueue in " + interceptor.getClass()); } } public void waitUntillAllReplicated(long timeout) { synchronized (replicated) { try { replicated.wait(timeout); } catch (InterruptedException e) { throw new RuntimeException(e); } } log("returning from waitUntillAllReplicated call"); } private class ReplicationQueueDelegate extends ReplicationQueue { ReplicationQueue original; private ReplicationQueueDelegate(ReplicationQueue original) { this.original = original; } @Override public void flush() { log("Flush invoked!"); original.flush(); synchronized (replicated) { replicated.notifyAll(); } } } private void log(String str) { System.out.println("ReplicationQueueNotifier >>> " + str); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/util/MapCopyTest.java0000755000175000017500000000775610665031725026513 0ustar twernertwernerpackage org.jboss.cache.util; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.fail; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.HashMap; import java.util.Map; import org.jboss.util.stream.MarshalledValueInputStream; import org.jboss.util.stream.MarshalledValueOutputStream; import org.testng.annotations.Test; @Test(groups={"functional", "transaction"}) public class MapCopyTest { public void testSerializable() throws Exception { HashMap hm = new HashMap(); hm.put(null, null); hm.put("y", "z"); MapCopy mc = new MapCopy(hm); assertEquals(hm, mc); ByteArrayOutputStream os = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(os); oos.writeObject(mc); ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray()); ObjectInputStream ois = new ObjectInputStream(is); Object o = ois.readObject(); assertEquals(hm, o); } public void testSerializableWithMarshalledValueStream() throws Exception { HashMap hm = new HashMap(); hm.put(null, null); hm.put("y", "z"); MapCopy mc = new MapCopy(hm); assertEquals(hm, mc); ByteArrayOutputStream os = new ByteArrayOutputStream(); ObjectOutputStream oos = new MarshalledValueOutputStream(os); oos.writeObject(mc); ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray()); ObjectInputStream ois = new MarshalledValueInputStream(is); Object o = ois.readObject(); assertEquals(hm, o); } public void testNull() { HashMap hm = new HashMap(); hm.put(null, null); MapCopy mc = new MapCopy(hm); assertEquals(hm, mc); assertEquals(hm.toString(), mc.toString()); hm.put(null, "x"); hm.put("y", null); mc = new MapCopy(hm); mc.toString(); assertEquals(true, mc.containsKey("y")); } public void testAll() { HashMap hm = new HashMap(); hm.put("a", "b"); hm.put("b", "c"); MapCopy mc = new MapCopy(hm); assertEquals(hm, mc); assertEquals(hm.size(), mc.size()); try { mc.clear(); fail("read only"); } catch (UnsupportedOperationException e) { } HashMap bhm = new HashMap(hm); hm.put("b", "d"); assertEquals(bhm, mc); Map.Entry me = mc.entrySet().iterator().next(); try { me.setValue("arg"); fail("read only"); } catch (UnsupportedOperationException e) { } } public void testModifications() { Map hm = new HashMap(); hm.put("a", "b"); Map mc = new MapCopy(hm); try { mc.put("x", "y"); fail("should fail"); } catch (UnsupportedOperationException uoe) { // ok } try { mc.remove("a"); fail("should fail"); } catch (UnsupportedOperationException uoe) { // ok } try { mc.keySet().iterator().remove(); fail("should fail"); } catch (UnsupportedOperationException uoe) { // ok } try { mc.entrySet().iterator().remove(); fail("should fail"); } catch (UnsupportedOperationException uoe) { // ok } try { mc.values().iterator().remove(); fail("should fail"); } catch (UnsupportedOperationException uoe) { // ok } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/util/MinMapUtilTest.java0000644000175000017500000000352710707457507027156 0ustar twernertwernerpackage org.jboss.cache.util; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertSame; import java.util.Collections; import java.util.Map; import java.util.HashMap; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; @Test(groups = {"functional", "transaction"}) public class MinMapUtilTest { private static final Map empty = Collections.emptyMap(); private Map map; private String key = "a"; private String key2 = "b"; private String value = "y"; @BeforeMethod public void before() { map = empty; } public void testRemove() { assertSame(map, MinMapUtil.remove(map, key)); map = MinMapUtil.put(map, key, value); map = MinMapUtil.put(map, key2, value); assertEquals(value, map.get(key)); assertEquals(value, map.get(key2)); MinMapUtil.remove(map, key2); assertSame(map, MinMapUtil.remove(map, "not here")); assertSame(empty, MinMapUtil.remove(map, key)); } public void testPut() { assertSame(map, MinMapUtil.remove(map, key)); map = MinMapUtil.put(map, key, value); assertEquals(1, map.size()); assertEquals(true, map.containsKey(key)); map = MinMapUtil.put(map, key, value); assertEquals(1, map.size()); assertEquals(true, map.containsKey(key)); map = MinMapUtil.put(map, key2, value); assertEquals(2, map.size()); } public void testPutAll() { HashMap hm = new HashMap(); assertSame(empty, MinMapUtil.putAll(map, hm)); hm.put(key, value); assertEquals(1, MinMapUtil.putAll(map, hm).size()); hm.put(key2, value); assertEquals(2, MinMapUtil.putAll(map, hm).size()); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/util/ImmutableListCopyTest.java0000644000175000017500000000475511041646535030542 0ustar twernertwernerpackage org.jboss.cache.util; import org.testng.annotations.Test; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.ListIterator; @Test(groups = "unit") public class ImmutableListCopyTest { public void testImmutability() { List l = new ImmutableListCopy(Collections.singletonList("one")); try { l.add("two"); assert false; } catch (UnsupportedOperationException good) { } try { l.remove(0); assert false; } catch (UnsupportedOperationException good) { } try { l.clear(); assert false; } catch (UnsupportedOperationException good) { } try { l.add(0, "x"); assert false; } catch (UnsupportedOperationException good) { } try { l.set(0, "i"); assert false; } catch (UnsupportedOperationException good) { } try { l.addAll(Collections.singletonList("l")); assert false; } catch (UnsupportedOperationException good) { } try { l.addAll(0, Collections.singletonList("l")); assert false; } catch (UnsupportedOperationException good) { } try { l.removeAll(Collections.singletonList("l")); assert false; } catch (UnsupportedOperationException good) { } try { l.retainAll(Collections.singletonList("l")); assert false; } catch (UnsupportedOperationException good) { } try { l.iterator().remove(); assert false; } catch (UnsupportedOperationException good) { } try { l.listIterator().set("w"); assert false; } catch (UnsupportedOperationException good) { } } public void testListIterator() { List ints = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); List list = new ImmutableListCopy(ints); ListIterator li = list.listIterator(); int number = 1; while (li.hasNext()) assert li.next() == number++; assert number == 11; number = 10; li = list.listIterator(list.size()); while (li.hasPrevious()) assert li.previous() == number--; assert number == 0; } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/util/BitEncodedIntegerSetTest.java0000644000175000017500000000741511021001464031102 0ustar twernertwernerpackage org.jboss.cache.util; import org.testng.annotations.Test; import java.util.HashSet; /** * @author Manik Surtani (manik@jboss.org) * @since 2.1.0 */ @Test(groups = "functional") public class BitEncodedIntegerSetTest { public void testLimits() { BitEncodedIntegerSet set = new BitEncodedIntegerSet(); set.add(0); set.add(1); set.add(62); set.add(63); for (int i = 0; i < 64; i++) { if (i == 0 || i == 1 || i == 62 || i == 63) { assert set.contains(i) : "Should contain " + i; } else { assert !set.contains(i) : "Should not contain " + i; } } } public void testRemoval() { BitEncodedIntegerSet set = new BitEncodedIntegerSet(); set.add(0); set.add(1); set.add(62); set.add(63); set.remove(0); set.remove(63); for (int i = 0; i < 64; i++) { if (i == 1 || i == 62) { assert set.contains(i); } else { assert !set.contains(i); } } } public void testAddAll() { BitEncodedIntegerSet set = new BitEncodedIntegerSet(); set.add(0); set.add(1); set.add(62); set.add(63); for (int i = 0; i < 64; i++) { if (i == 0 || i == 1 || i == 62 || i == 63) { assert set.contains(i); } else { assert !set.contains(i); } } BitEncodedIntegerSet set2 = new BitEncodedIntegerSet(); set2.add(0); set2.add(1); set2.add(44); set2.add(55); for (int i = 0; i < 64; i++) { if (i == 0 || i == 1 || i == 44 || i == 55) { assert set2.contains(i); } else { assert !set2.contains(i); } } set.addAll(set2); for (int i = 0; i < 64; i++) { if (i == 0 || i == 1 || i == 62 || i == 63 || i == 44 || i == 55) { assert set.contains(i) : "Should contain " + i; } else { assert !set.contains(i); } } } public void testClear() { BitEncodedIntegerSet set = new BitEncodedIntegerSet(); set.add(0); set.add(1); set.add(62); set.add(63); for (int i = 0; i < 64; i++) { if (i == 0 || i == 1 || i == 62 || i == 63) { assert set.contains(i); } else { assert !set.contains(i); } } set.clear(); assert set.isEmpty(); } public void testIsEmpty() { BitEncodedIntegerSet set = new BitEncodedIntegerSet(); assert set.isEmpty(); set.add(1); assert !set.isEmpty(); } public void testEquals() { BitEncodedIntegerSet set1 = new BitEncodedIntegerSet(); BitEncodedIntegerSet set2 = new BitEncodedIntegerSet(); assert set1.equals(set2); assert set2.equals(set1); set1.add(1); assert !set1.equals(set2); assert !set2.equals(set1); set2.add(1); assert set1.equals(set2); assert set2.equals(set1); set2.add(2); assert !set1.equals(set2); assert !set2.equals(set1); assert set1.equals(set1); assert !set1.equals(null); assert !set1.equals(new HashSet()); } public void testHashCode() { BitEncodedIntegerSet set = new BitEncodedIntegerSet(); int hash = set.hashCode(); assert hash >= Integer.MIN_VALUE; assert hash <= Integer.MAX_VALUE; } public void testToString() { BitEncodedIntegerSet set = new BitEncodedIntegerSet(); assert set.toString() != null; } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/util/DeltaMapTest.java0000644000175000017500000001020210707457507026612 0ustar twernertwernerpackage org.jboss.cache.util; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.fail; import java.util.HashMap; import java.util.Iterator; import java.util.NoSuchElementException; import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; @Test(groups = {"functional", "transaction"}) public class DeltaMapTest { static String Y = "y"; static String Z = "z"; static String K = "k"; HashMap hm; HashMap backup; DeltaMap dm; @BeforeMethod public void setUp() { hm = new HashMap(); hm.put(null, null); hm.put(Y, Z); hm.put(K, Y); backup = new HashMap(hm); dm = DeltaMap.create(hm); assertEquals(false, dm.isModified()); assertEquals(hm, dm); } public void testSize() { assertEquals(3, dm.size()); dm.put(Y, "HI"); assertEquals(3, dm.size()); dm.remove(Y); assertEquals(2, dm.size()); hm.clear(); assertEquals(0, dm.size()); dm.put(Z, Z); dm.getRemoved().add("NOT HERE"); assertEquals(1, dm.size()); } public void testConcurrent() throws Exception { ConcurrentHashMap m = new ConcurrentHashMap(); m.put(new Object(), Z); m.put(new Object(), Y); DeltaMap dm = DeltaMap.create(m); assertEquals(m, dm); assertEquals(m.toString(), dm.toString()); } public void testChanges() throws Exception { assertEquals("" + dm.toDebugString(), backup, dm); assertEquals(Z, dm.remove(Y)); assertEquals(true, dm.isModified()); assertEquals(null, dm.remove(Y)); assertEquals("changes not made to underlying map", backup, hm); assertEquals(false, dm.containsKey(Y)); assertEquals(false, dm.containsValue(Z)); assertEquals(null, dm.put(Y, Z)); assertEquals(Z, dm.put(Y, Z)); assertEquals("changes not made to underlying map", backup, hm); assertEquals(backup.size(), dm.size()); assertEquals(backup, dm); dm.commit(); assertEquals(hm, dm); dm.commit(); assertEquals(hm, dm); } public void testAddRemove() throws Exception { dm.remove(K); dm.put(K, Z); assertEquals(Z, dm.get(K)); assertEquals(Z, dm.remove(K)); assertEquals(null, dm.remove(K)); } public void testExclude() throws Exception { dm = DeltaMap.excludeKeys(hm, Y); assertEquals(false, dm.containsKey(Y)); } public void testExclude2() throws Exception { dm = DeltaMap.excludeKeys(hm, hm.keySet()); assertEquals(true, dm.isModified()); assertEquals(0, dm.size()); } public void testClearedMap() throws Exception { dm.clear(); assertEquals(0, dm.size()); assertEquals(backup, hm); assertEquals(null, dm.remove(Y)); } public void testIterator() throws Exception { dm.remove(null); dm.put(K, Y); System.out.println(dm.toDebugString()); System.out.println(dm.toString()); Iterator> i = dm.entrySet().iterator(); assertEquals(true, i.hasNext()); assertEquals(true, i.hasNext()); i.next(); assertEquals(true, i.hasNext()); i.next(); assertEquals("" + dm, false, i.hasNext()); try { i.next(); fail("no next"); } catch (NoSuchElementException e) { } try { i.next(); fail("no next"); } catch (NoSuchElementException e) { } } public void testEx() { HashMap hm = new HashMap(); hm.put("a", "apple"); DeltaMap dm = DeltaMap.create(hm); dm.remove("a"); assert hm.containsKey("a"); assert !dm.containsKey("a"); dm.commit(); assert !hm.containsKey("a"); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/invocationcontext/0000755000175000017500000000000011376173770026226 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/invocationcontext/TransactionTest.java0000644000175000017500000001341011012534035032173 0ustar twernertwernerpackage org.jboss.cache.invocationcontext; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.transaction.TransactionEntry; import org.jboss.cache.transaction.TransactionTable; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.Transaction; import javax.transaction.TransactionManager; import java.util.Map; /** * A test to ensure the transactional context is properly set up in the IC * * @author Manik Surtani */ @Test(groups = {"functional", "transaction"}) public class TransactionTest { private CacheSPI cache; private TransactionManager tm; @BeforeMethod(alwaysRun = true) public void setUp() { cache = (CacheSPI) new DefaultCacheFactory().createCache("META-INF/conf-test/local-tx-service.xml"); tm = cache.getTransactionManager(); } @AfterMethod(alwaysRun = true) public void tearDown() { if (cache != null) { cache.stop(); cache = null; } } public void testTxExistenceAfterWrite() throws Exception { // make sure we have a running transaction. tm.begin(); assertNull("Tx should not have been set up yet", cache.getInvocationContext().getTransaction()); assertNull("Gtx should not have been set up yet", cache.getInvocationContext().getGlobalTransaction()); // now make a WRITE call into the cache (should start a tx) cache.getRoot().put("k", "v"); Map data = cache.getRoot().getData(); assertEquals("Data map should not empty", 1, data.size()); // but now we should have a local tx registered in the invocation context assertNotNull("Tx should have been set up by now", cache.getInvocationContext().getTransaction()); assertEquals("The same current transaction should be associated with this invocation ctx.", tm.getTransaction(), cache.getInvocationContext().getTransaction()); assertNotNull("Gtx should have been set up by now", cache.getInvocationContext().getGlobalTransaction()); tm.commit(); } public void testTxExistenceAfterRead() throws Exception { // make sure we have a running transaction. tm.begin(); assertNull("Tx should not have been set up yet", cache.getInvocationContext().getTransaction()); assertNull("Gtx should not have been set up yet", cache.getInvocationContext().getGlobalTransaction()); // now make a WRITE call into the cache (should start a tx) Object value = cache.get(Fqn.ROOT, "k"); assertNull("Value should be null", value); // but now we should have a local tx registered in the invocation context assertNotNull("Tx should have been set up by now", cache.getInvocationContext().getTransaction()); assertEquals("The same current transaction should be associated with this invocation ctx.", tm.getTransaction(), cache.getInvocationContext().getTransaction()); assertNotNull("Gtx should have been set up by now", cache.getInvocationContext().getGlobalTransaction()); tm.commit(); } public void testScrubbingAfterCommit() throws Exception { doScrubbingTest(true); } public void testScrubbingAfterOnePhaseCommit() throws Exception { setUpOnePhaseCache(); doScrubbingTest(true); } public void testScrubbingAfterRollback() throws Exception { doScrubbingTest(false); } public void testScrubbingAfterOnePhaseRollback() throws Exception { setUpOnePhaseCache(); doScrubbingTest(true); } @SuppressWarnings("deprecation") private void doScrubbingTest(boolean commit) throws Exception { // Start clean cache.getInvocationContext().reset(); tm.begin(); TransactionTable tt = cache.getTransactionTable(); cache.getRoot().put("key", "value"); assertNotNull("Tx should have been set up by now", cache.getInvocationContext().getTransaction()); assertEquals("The same current transaction should be associated with this invocation ctx.", tm.getTransaction(), cache.getInvocationContext().getTransaction()); assertNotNull("Gtx should have been set up by now", cache.getInvocationContext().getGlobalTransaction()); Transaction tx = tm.getTransaction(); TransactionEntry entry = tt.get(tt.get(tx)); if (commit) { tm.commit(); } else { tm.rollback(); } assertNull("Tx should have been scrubbed", cache.getInvocationContext().getTransaction()); assertNull("Gtx should have been scrubbed", cache.getInvocationContext().getGlobalTransaction()); assertEquals("Method call should have been scrubbed", null, cache.getInvocationContext().getMethodCall()); assertEquals("Cache command should have been scrubbed", null, cache.getInvocationContext().getCommand()); // check that the transaction entry hasn't leaked stuff. assert entry.getModifications().isEmpty() : "Should have scrubbed modifications in transaction entry"; assert entry.getLocks().isEmpty() : "Should have scrubbed modifications in transaction entry"; assert entry.getOrderedSynchronizationHandler() == null : "Should have removed the ordered sync handler"; } private void setUpOnePhaseCache() { if (cache != null) { cache.stop(); cache = null; } cache = (CacheSPI) new DefaultCacheFactory().createCache("META-INF/conf-test/replSync-service.xml", false); cache.getConfiguration().setCacheMode(CacheMode.REPL_ASYNC); cache.start(); tm = cache.getTransactionManager(); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/cluster/0000755000175000017500000000000011376173735024132 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/cluster/ReplicationQueueTest.java0000644000175000017500000001422611020007335031072 0ustar twernertwernerpackage org.jboss.cache.cluster; import org.easymock.EasyMock; import static org.easymock.EasyMock.*; import org.jboss.cache.Cache; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.RPCManager; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.util.internals.ReplicationQueueNotifier; import org.jboss.cache.commands.ReplicableCommand; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.ComponentRegistry; import org.jgroups.Address; import static org.testng.AssertJUnit.assertNotNull; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.Collections; import java.util.Vector; import java.util.concurrent.CountDownLatch; @Test(groups = "functional") public class ReplicationQueueTest { private static final int COUNT = 10; Cache cache, cache2; ReplicationQueue replQ; ComponentRegistry registry; RPCManager originalRpcManager; @BeforeMethod public void setUp() throws CloneNotSupportedException { cache = new DefaultCacheFactory().createCache(false); cache.getConfiguration().setCacheMode(Configuration.CacheMode.REPL_ASYNC); cache.getConfiguration().setUseReplQueue(true); cache.getConfiguration().setReplQueueMaxElements(COUNT); cache.getConfiguration().setReplQueueInterval(-1); cache.start(); registry = TestingUtil.extractComponentRegistry(cache); replQ = registry.getComponent(ReplicationQueue.class); originalRpcManager = cache.getConfiguration().getRuntimeConfig().getRPCManager(); cache2 = new DefaultCacheFactory().createCache(cache.getConfiguration().clone()); TestingUtil.blockUntilViewsReceived(60000, cache, cache2); } @AfterMethod public void tearDown() { // reset the original RPCManager injectRpcManager(originalRpcManager); TestingUtil.killCaches(cache, cache2); } private void injectRpcManager(RPCManager manager) { registry.registerComponent(manager, RPCManager.class); } public void testQueueHoldAndFlush() throws Exception { assert replQ != null; // mock the RPCManager used in the cache RPCManager mockRpcManager = EasyMock.createStrictMock(RPCManager.class); injectRpcManager(mockRpcManager); // expect basic cluster related calls expect(mockRpcManager.getMembers()).andReturn(originalRpcManager.getMembers()).anyTimes(); replay(mockRpcManager); // check that nothing on the RPCManager will be called until we hit the replication queue threshold. for (int i = 0; i < COUNT - 1; i++) cache.put("/a/b/c/" + i, "k", "v"); assert replQ.elements.size() == COUNT - 1; // verify that no calls have been made on the mockRpcManager verify(mockRpcManager); // reset the mock reset(mockRpcManager); // now try the last PUT which should result in the queue being flushed. expect(mockRpcManager.getMembers()).andReturn(originalRpcManager.getMembers()).anyTimes(); expect(mockRpcManager.callRemoteMethods((Vector
) anyObject(), (ReplicableCommand) anyObject(), anyBoolean(), anyLong(), anyBoolean())).andReturn(Collections.emptyList()).anyTimes(); replay(mockRpcManager); cache.put("/a/b/c/LAST", "k", "v"); assert replQ.elements.size() == 0; // verify that the rpc call was only made once. verify(mockRpcManager); } public void testFlushConcurrency() throws Exception { // will create multiple threads to constantly perform a cache update, and measure the number of expected invocations on the RPC manager. final int numThreads = 25; final int numLoopsPerThread = 1000; int totalInvocations = numThreads * numLoopsPerThread; assert totalInvocations % COUNT == 0 : "NumThreads and NumLoopsPerThread must multiply to be a multiple of COUNT"; final CountDownLatch latch = new CountDownLatch(1); // mock the RPCManager used in the cache RPCManager mockRpcManager = EasyMock.createStrictMock(RPCManager.class); injectRpcManager(mockRpcManager); // expect basic cluster related calls expect(mockRpcManager.getMembers()).andReturn(originalRpcManager.getMembers()).anyTimes(); expect(mockRpcManager.callRemoteMethods((Vector
) anyObject(), (ReplicableCommand) anyObject(), anyBoolean(), anyLong(), anyBoolean())).andReturn(Collections.emptyList()).anyTimes(); replay(mockRpcManager); Thread[] threads = new Thread[numThreads]; for (int i = 0; i < numThreads; i++) { threads[i] = new Thread() { public void run() { try { latch.await(); } catch (InterruptedException e) { // do nothing } for (int j = 0; j < numLoopsPerThread; j++) { cache.put("/a/b/c/" + getName() + "/" + j, "k", "v"); } } }; threads[i].start(); } // start the threads latch.countDown(); // wait for threads to join for (Thread t : threads) t.join(); // now test results verify(mockRpcManager); ReplicationQueueNotifier notifier = new ReplicationQueueNotifier(cache); notifier.waitUntillAllReplicated(250); // TestingUtil.sleepThread(250); // make sure the queue flushes assert replQ.elements.size() == 0; } public void testFailure() throws InterruptedException { for (int i = 0; i < COUNT; i++) { System.out.println("on put i = " + i); cache.put("/a/b/c" + i, "key", "value"); assertNotNull(cache.get("/a/b/c" + i, "key")); } ReplicationQueueNotifier notifier = new ReplicationQueueNotifier(cache); notifier.waitUntillAllReplicated(500); // TestingUtil.sleepThread(500); for (int i = 0; i < COUNT; i++) assertNotNull("on get i = " + i, cache2.get("/a/b/c" + i, "key")); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/cluster/ReplicationQueueTxTest.java0000644000175000017500000000404111020007335031400 0ustar twernertwernerpackage org.jboss.cache.cluster; import org.jboss.cache.Cache; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.util.internals.ReplicationQueueNotifier; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.UnitTestCacheConfigurationFactory; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.TransactionManager; @Test(groups = {"functional", "transaction"}) public class ReplicationQueueTxTest { Cache cache, cache2; TransactionManager txManager; @BeforeMethod public void setUp() throws CloneNotSupportedException { cache = new DefaultCacheFactory().createCache(UnitTestCacheConfigurationFactory.createConfiguration(Configuration.CacheMode.REPL_ASYNC), false); cache.getConfiguration().setUseReplQueue(true); cache.getConfiguration().setReplQueueInterval(100); cache.getConfiguration().setReplQueueMaxElements(10); cache.start(); cache2 = new DefaultCacheFactory().createCache(cache.getConfiguration().clone()); TestingUtil.blockUntilViewsReceived(60000, cache, cache2); txManager = cache.getConfiguration().getRuntimeConfig().getTransactionManager(); } @AfterMethod public void tearDown() { TestingUtil.killCaches(cache, cache2); } public void testTransactionalReplication() throws Exception { // outside of tx scope cache.put("/a", "k", "v"); ReplicationQueueNotifier replicationQueueNotifier = new ReplicationQueueNotifier(cache); replicationQueueNotifier.waitUntillAllReplicated(200); assert cache2.get("/a", "k").equals("v"); // now, a transactional call txManager.begin(); cache2.put("/a", "k", "v2"); txManager.commit(); ReplicationQueueNotifier replicationQueueNotifier2 = new ReplicationQueueNotifier(cache2); replicationQueueNotifier2.waitUntillAllReplicated(200); assert cache.get("/a", "k").equals("v2"); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/VersionConversionTest.java0000644000175000017500000000653210665031725027645 0ustar twernertwernerpackage org.jboss.cache; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.fail; import org.testng.annotations.Test; @Test(groups = {"functional"}) public class VersionConversionTest { public void testStringToShort() { try { Version.getVersionShort("1.2.4SP1"); fail("Correctly did not accept versionString '1.2.4SP1'"); } catch (IllegalArgumentException ok) {} try { Version.getVersionShort("1.2.4 SP1"); fail("Correctly did not accept versionString '1.2.4 SP1'"); } catch (IllegalArgumentException ok) {} try { Version.getVersionShort("1.3.alpha"); fail("Correctly did not accept versionString '1.3.alpha'"); } catch (IllegalArgumentException ok) {} assertEquals("MAX_SHORT correct", Short.MAX_VALUE, Version.getVersionShort("15.31.63")); assertEquals("0.0.1 correct", 1, Version.getVersionShort("0.0.1")); assertEquals("0.1.0 correct", (short) Math.pow(2, 6), Version.getVersionShort("0.1.0")); assertEquals("1.0 correct", (short) Math.pow(2, 11), Version.getVersionShort("1.0")); assertEquals("1.0.1 correct", (short) Math.pow(2, 11) + 1, Version.getVersionShort("1.0.1")); assertEquals("1.1 correct", (short) (Math.pow(2,11) + Math.pow(2,6)), Version.getVersionShort("1.1")); assertEquals("1.1.1 correct", (short) (Math.pow(2,11) + Math.pow(2,6)) + 1, Version.getVersionShort("1.1.1")); assertEquals("2.0 correct", (short) Math.pow(2,12), Version.getVersionShort("2.0")); // Ignore final qualifiers assertEquals("1.3.0.alpha correct", (short) (Math.pow(2,11) + Math.pow(2,7) + Math.pow(2,6)), Version.getVersionShort("1.3.0.alpha")); assertEquals("1.3.0.RC1 correct", (short) (Math.pow(2,11) + Math.pow(2,7) + Math.pow(2,6)), Version.getVersionShort("1.3.0.RC1")); assertEquals("1.3.0.SP1 correct", (short) (Math.pow(2,11) + Math.pow(2,7) + Math.pow(2,6)), Version.getVersionShort("1.3.0.SP1")); // Special cases assertEquals("1.2.4.SP2 correct", (short) 124, Version.getVersionShort("1.2.4")); assertEquals("1.2.4.SP2 correct", (short) 1241, Version.getVersionShort("1.2.4.SP1")); assertEquals("1.2.4.SP2 correct", (short) (Math.pow(2,11) + Math.pow(2,7)) + 4, Version.getVersionShort("1.2.4.SP2")); } public void testShortToString() { assertEquals("0.0.1 correct", "0.0.1", Version.getVersionString(Version.getVersionShort("0.0.1"))); assertEquals("1.3.0 correct", "1.3.0", Version.getVersionString(Version.getVersionShort("1.3.0"))); // Special cases assertEquals("1.2.4 correct", "1.2.4", Version.getVersionString((short) 124)); assertEquals("1.2.4.SP1 correct", "1.2.4.SP1", Version.getVersionString((short) 1241)); assertEquals("1.2.4.SP2 correct", "1.2.4.SP2", Version.getVersionString(Version.getVersionShort("1.2.4.SP2"))); } public void testDefault() { short defaultShort = Version.getVersionShort(); String versionString = Version.getVersionString(defaultShort); // only compare the main version string. String versionToCompareAgainst = Version.version; versionToCompareAgainst = versionToCompareAgainst.replaceAll("[\\-\\.]\\w+$", ""); assertEquals("Round-trip conversion consistent", versionToCompareAgainst, versionString); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/GetKeysTest.java0000644000175000017500000000451410732232105025511 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache; import static org.testng.AssertJUnit.*; import org.testng.annotations.Test; import java.io.ByteArrayOutputStream; import java.io.ObjectOutputStream; import java.util.Set; /** * @author Bela Ban * @version $Id: GetKeysTest.java 4879 2007-12-19 15:09:57Z manik.surtani@jboss.com $ */ public class GetKeysTest { CacheSPI cache; @Test(groups = {"functional"}) public void testGetKeys() throws Exception { cache = (CacheSPI) new DefaultCacheFactory().createCache(); cache.put("/a/b/c", "name", "Bela Ban"); cache.put("/a/b/c", "age", 40); cache.put("/a/b/c", "city", "Kreuzlingen"); Set keys = cache.getNode("/a/b/c").getKeys(); log("keys are " + keys); assertNotNull(keys); assertEquals(3, keys.size()); ByteArrayOutputStream outstream = new ByteArrayOutputStream(20); ObjectOutputStream out = new ObjectOutputStream(outstream); out.writeObject(keys);// must be serializable } @Test(groups = {"functional"}) public void testGetChildren() throws Exception { cache = (CacheSPI) new DefaultCacheFactory().createCache(); cache.put("/a/b/c", null); cache.put("/a/b/c/1", null); cache.put("/a/b/c/2", null); cache.put("/a/b/c/3", null); Set children = cache.getNode("/a/b/c").getChildrenNames(); log("children are " + children); assertNotNull(children); assertEquals(3, children.size()); ByteArrayOutputStream outstream = new ByteArrayOutputStream(20); ObjectOutputStream out = new ObjectOutputStream(outstream); out.writeObject(children);// must be serializable } @Test(groups = {"functional"}) public void testGetKeysOnNode() { cache = (CacheSPI) new DefaultCacheFactory().createCache(); cache.put("/a/b/c", "key", "value"); Node node = cache.getRoot().getChild(Fqn.fromString("/a/b/c")); Set keySet = node.getKeys(); try { keySet.add("asd"); fail(); } catch (Exception e) { //expected } } void log(String msg) { System.out.println("-- " + msg); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/demo/0000755000175000017500000000000011376173740023371 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/demo/JBossCacheView.java0000644000175000017500000001301211031417510027010 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. * Created on March 25 2003 */ package org.jboss.cache.demo; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.Cache; import org.jboss.cache.CacheFactory; import org.jboss.cache.DefaultCacheFactory; /** * Graphical view of a JBoss Cache instance. Think of it as a view in an MVC model. An instance of this view * needs to be given a reference to the model ({@link CacheModelDelegate}) and needs to registers as a * {@link org.jboss.cache.CacheListener}. Changes to the cache structure are propagated from the model to the * view (via the CacheListener interface), changes from the GUI (e.g. by a user) are executed on the cache model * which will delegate on the actual Cache instance (which, if clustered, will broadcast the changes to all replicas). *

* The view itself maintains references to the nodes, but doesn't cache any of the data associated with the nodes. When * data needs to be displayed, the underlying cache will be accessed directly. * * @author Manik Surtani * @author Galder Zamarreno * @version $Revision: 6116 $ */ public class JBossCacheView { private static Log log = LogFactory.getLog(JBossCacheView.class.getName()); /** * A reference to the Swing GUI that displays contents to the user. */ private JBossCacheGUI gui = null; /** * Whether or not to use the embedded BeanShell console. */ private boolean useConsole = false; /** * Cache configuration file. */ private String configurationFile = null; /** * The cache model. */ private CacheModelDelegate cacheModelDelegate; /** * Gets the configuration file * * @return String containing the path to the configuration file */ public String getConfigurationFile() { return configurationFile; } /** * Sets a reference to the cache model * * @param cacheModelDelegate cache model instance to associate with this view */ public void setCacheModelDelegate(CacheModelDelegate cacheModelDelegate) { this.cacheModelDelegate = cacheModelDelegate; if (gui != null) gui.setCacheModelDelegate(cacheModelDelegate); } /** * Main code for the view * * @param args arguments passed via command line * @throws Exception if there's any issues starting the cache demo */ public void doMain(String[] args) throws Exception { parseParameters(args); if (configurationFile == null) { help(); throw new Exception("Configuration file cannot be null, please specify with the -config parameter when starting!"); } CacheModelDelegate cacheModelDelegate = createCacheDelegate(); cacheModelDelegate.getGenericCache().start(); setCacheModelDelegate(cacheModelDelegate); start(); } /** * Starts the view * * @throws Exception */ public void start() throws Exception { if (gui == null) { log.info("start(): creating the GUI"); System.out.println("start(): creating the GUI"); gui = createGUI(cacheModelDelegate, useConsole); } } /** * Stops the view */ public void stop() { if (gui != null) { log.info("stop(): disposing the GUI"); gui.stopGui(); gui = null; } } /** * Starts the view * * @param args valid arguments are: -console to enable using the embedded beanShell console, -config [path/to/config/file] to specify a config file. */ public static void main(String args[]) { try { JBossCacheView view = new JBossCacheView(); view.doMain(args); } catch (Exception ex) { log.error("Cannot start up!!", ex); } } /** * Parses the parameters * * @param args arguments passed via command line */ protected void parseParameters(String[] args) { for (int i = 0; i < args.length; i++) { if (args[i].equals("-console")) { useConsole = true; continue; } if (args[i].equals("-config")) { configurationFile = args[++i]; continue; } help(); return; } } /** * Factory method that creates the cache model delegate instance for this demo * * @return instance of CacheModelDelegate * @throws Exception */ protected CacheModelDelegate createCacheDelegate() throws Exception { CacheFactory factory = new DefaultCacheFactory(); Cache cache = factory.createCache(configurationFile, false); CacheModelDelegate delegate = new JBossCacheModelDelegate(); delegate.setCacheShellVariable(cache); return delegate; } protected JBossCacheGUI createGUI(CacheModelDelegate delegate, boolean useConsole) throws Exception { return new JBossCacheGUI(delegate, useConsole); } private static void help() { System.out.println("JBossCacheView [-help] " + "[-console] " + "[-config ]"); System.out.println(); System.out.println("-console enables the embedded BeanShell console"); System.out.println("-config allows you to provide a path to the configuration file to use."); System.out.println(); System.out.println(); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/demo/CacheModelDelegate.java0000644000175000017500000000176110703164716027654 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.demo; import org.jboss.cache.Cache; /** * Delegate that hides cache model details for the demo GUI * * @author Galder Zamarreno */ public interface CacheModelDelegate { /** * Sets the cache instance that will be used by users to interact with the real cache via the beanshell console. * * @param cache either PojoCache or Cache instance */ void setCacheShellVariable(Object cache); /** * Gets the cache instance that will be used by users to interact with the real cache via the beanshell console. * * @return either PojoCache or Cache instance */ Object getCacheShellVariable(); /** * Gets the Cache instance that the GUI will use to populate the fiels in the GUI. * * @return returns an instance of Cache */ Cache getGenericCache(); } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/demo/JBossCacheGUI.java0000644000175000017500000010723011017257241026537 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.demo; import bsh.EvalError; import bsh.Interpreter; import bsh.util.JConsole; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.Cache; import org.jboss.cache.Fqn; import org.jboss.cache.Node; import org.jboss.cache.lock.TimeoutException; import org.jboss.cache.notifications.annotation.CacheListener; import org.jboss.cache.notifications.annotation.NodeCreated; import org.jboss.cache.notifications.annotation.NodeEvicted; import org.jboss.cache.notifications.annotation.NodeLoaded; import org.jboss.cache.notifications.annotation.NodeModified; import org.jboss.cache.notifications.annotation.NodeRemoved; import org.jboss.cache.notifications.annotation.ViewChanged; import org.jboss.cache.notifications.event.NodeEvent; import org.jboss.cache.notifications.event.NodeModifiedEvent; import org.jboss.cache.notifications.event.ViewChangedEvent; import org.jboss.cache.util.CachePrinter; import org.jgroups.Address; import javax.swing.*; import javax.swing.event.TableModelEvent; import javax.swing.event.TableModelListener; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; import javax.swing.table.DefaultTableModel; import javax.swing.table.TableColumn; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreePath; import javax.swing.tree.TreeSelectionModel; import javax.transaction.Transaction; import javax.transaction.TransactionManager; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.InputEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; import java.util.Vector; import java.util.concurrent.Executor; import java.util.concurrent.Executors; /** * JBossCache GUI for the demo * * @author Manik Surtani (manik@jboss.org) * @author Galder Zamarreno */ @CacheListener public class JBossCacheGUI extends JFrame implements WindowListener, TreeSelectionListener, TableModelListener { private static final long serialVersionUID = -1242167331988194987L; private transient CacheModelDelegate cacheModelDelegate; private transient Cache cache; private DefaultTreeModel tree_model = null; private transient Log log = LogFactory.getLog(getClass()); private JTree jtree = null; private DefaultTableModel tableModel = new DefaultTableModel(); private JTable table = new JTable(tableModel); private JBossCacheGUI.DisplayNode myNodeRoot = new JBossCacheGUI.DisplayNode(Fqn.SEPARATOR); private transient Node root; private transient Node selected_node = null; private JPanel tablePanel = null; private JPopupMenu operationsPopup = null, dataModificationsPopup = null; private static final int KEY_COL_WIDTH = 20; private static final int VAL_COL_WIDTH = 300; private TransactionManager tx_mgr = null; private transient Transaction tx = null; private JPanel mainPanel; private List

membership = new LinkedList
(); private Address coordinator = null; private boolean useConsole = false; JConsole bshConsole; /** * Run any work that happens in this interface in a separate thread. This is good practise. Unless there's a * resource-managemenet requirement, such as in an application server, newCachedThreadPool is a good option as it * provides better queuing performance. */ private transient Executor executor; public JBossCacheGUI(CacheModelDelegate cacheDelegate, boolean useConsole) throws Exception { executor = Executors.newCachedThreadPool(); addNotify(); this.useConsole = useConsole; tree_model = new DefaultTreeModel(new JBossCacheGUI.DisplayNode(Fqn.ROOT.toString())); jtree = new JTree(tree_model); jtree.setDoubleBuffered(true); jtree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); JScrollPane scroll_pane = new JScrollPane(jtree); mainPanel = new JPanel(); mainPanel.setLayout(new BorderLayout()); mainPanel.add(scroll_pane, BorderLayout.CENTER); addWindowListener(this); tableModel.setColumnIdentifiers(new String[]{"Name", "Value"}); tableModel.addTableModelListener(this); // add a mouse listener to the table model for delete and insert rows. MouseListener dataMouseListener = new MouseAdapter() { public void mouseClicked(MouseEvent e) { if (rightClick(e.getModifiers())) { dataModificationsPopup.show(e.getComponent(), e.getX(), e.getY()); } } }; setTableColumnWidths(); tablePanel = new JPanel(); tablePanel.setLayout(new BorderLayout()); tablePanel.add(table.getTableHeader(), BorderLayout.NORTH); tablePanel.add(table, BorderLayout.CENTER); table.addMouseListener(dataMouseListener); mainPanel.add(tablePanel, BorderLayout.SOUTH); JSplitPane contentPanel = null; if (useConsole) { String welcomeMessage = getWelcomeMessage(); bshConsole = new JConsole(); Interpreter interpreter = new Interpreter(bshConsole); configureInterpreter(interpreter, cacheDelegate); interpreter.println(welcomeMessage); interpreter.setShowResults(!interpreter.getShowResults());// show() in beanShell System.setOut(bshConsole.getOut()); System.setErr(bshConsole.getErr()); Thread t = new Thread(interpreter); t.start(); contentPanel = new JSplitPane(JSplitPane.VERTICAL_SPLIT, mainPanel, bshConsole); getContentPane().add(contentPanel); } else { getContentPane().add(mainPanel); } jtree.addTreeSelectionListener(this);// REVISIT MouseListener ml = new MouseAdapter() { public void mouseClicked(final MouseEvent e) { if (log.isTraceEnabled()) { log.trace("clicked GUI"); } final int selRow = jtree.getRowForLocation(e.getX(), e.getY()); final TreePath selPath = jtree.getPathForLocation(e.getX(), e.getY()); if (selRow != -1) { if (log.isTraceEnabled()) { log.trace("clicked on node in GUI"); } Runnable r = new Runnable() { public void run() { try { selected_node = getNode(selPath.getPath()); jtree.setSelectionPath(selPath); if (rightClick(e.getModifiers())) { operationsPopup.show(e.getComponent(), e.getX(), e.getY()); } } catch (TimeoutException te) { String message = "Unable to update GUI due to a timeout trying to acquire lock on a node." + "This might be due to clicking on a node which is currently locked by an ongoing " + "transaction: "; printAndLogStacktrace(message, te); } catch (Exception e) { printAndLogStacktrace("Updating GUI failed: ", e); } } }; executor.execute(r); } } }; jtree.addMouseListener(ml); createMenus(); setLocation(50, 50); setSize(getInsets().left + getInsets().right + 800, getInsets().top + getInsets().bottom + 800); // make sure we set the cache BEFORE initialising this!! setCacheModelDelegate(cacheDelegate); init(); setVisible(true); tree_model.setRoot(myNodeRoot); tree_model.reload(); if (useConsole) { //has to be called after setVisible() otherwise no effect contentPanel.setDividerLocation(0.65); } } public void setCacheModelDelegate(final CacheModelDelegate cacheModelDelegate) { this.cacheModelDelegate = cacheModelDelegate; if (this.cacheModelDelegate != null) { this.cache = this.cacheModelDelegate.getGenericCache(); Runtime.getRuntime().addShutdownHook( new Thread() { public void run() { cache.stop(); } }); cache.addCacheListener(this); root = cache.getRoot(); setTitle("JBoss Cache GUI: Local Address=" + getLocalAddress()); tx_mgr = cache.getConfiguration().getRuntimeConfig().getTransactionManager(); populateTree(); } else { setTitle("Cache undefined"); if (tree_model != null) { myNodeRoot = new JBossCacheGUI.DisplayNode(Fqn.SEPARATOR); tree_model.setRoot(myNodeRoot); tree_model.reload(); } if (tableModel != null) { clearTable(); } } } /** * Checks whether a click event is considered a "right-click" * * @param modifiers mouse event mods * @return true if deemed a right click */ private boolean rightClick(int modifiers) { // the simple right click case if (modifiers == InputEvent.BUTTON3_MASK) return true; // more complex on Mac OS X where a ctrl-click is deemed the same as a right click return modifiers == InputEvent.BUTTON1_MASK + InputEvent.CTRL_MASK; } public void windowClosed(WindowEvent event) { } public void windowDeiconified(WindowEvent event) { } public void windowIconified(WindowEvent event) { } public void windowActivated(WindowEvent event) { } public void windowDeactivated(WindowEvent event) { } public void windowOpened(WindowEvent event) { } public void windowClosing(WindowEvent event) { stopGui(); } public void tableChanged(TableModelEvent evt) { if (log.isTraceEnabled()) { log.trace("table contents changed, event type is: " + evt.getType()); } int row, col; String key, val; if (evt.getType() == TableModelEvent.UPDATE) { row = evt.getFirstRow(); col = evt.getColumn(); key = (String) tableModel.getValueAt(row, col == 0 ? 0 : col - 1); val = (String) tableModel.getValueAt(row, col == 0 ? 1 : col); if (key != null && val != null && !isPlaceHolder(key) && !isPlaceHolder(val)) { try { if (log.isTraceEnabled()) { log.trace("updating node: " + selected_node + " with new values [k=" + key + ",v=" + val + "]"); } selected_node.put(key, val); Map data = selected_node.getData(); populateTable(data); } catch (Exception e) { printAndLogStacktrace("Changing table failed: ", e); } } } } public void valueChanged(final TreeSelectionEvent evt) { if (log.isTraceEnabled()) { log.trace("node was selected in GUI: " + evt.getPath()); } Runnable r = new Runnable() { public void run() { try { Map data; selected_node = getNode(evt.getPath().getPath()); if (selected_node != null) { data = selected_node.getData(); if (data != null) { mainPanel.add(tablePanel, BorderLayout.SOUTH); populateTable(data); validate(); } else { clearTable(); mainPanel.remove(tablePanel); validate(); } } } catch (TimeoutException te) { String message = "Unable to update GUI due to a timeout trying to acquire lock on a node." + "This might be due to clicking on a node which is currently locked by an ongoing " + "transaction: "; printAndLogStacktrace(message, te); } catch (Exception e) { printAndLogStacktrace("Updating GUI failed: ", e); } } }; executor.execute(r); } protected DefaultTableModel getTableModel() { return tableModel; } protected String getWelcomeMessage() { return "Welcome to the BeanShell console.\n\n" + "This console gives you a direct shell interface to the GUI above and allows you to manipulate the cache directly. " + "Some of the variables initialised in this shell session are:\n\n" + "// an instance of org.jboss.cache\n" + " Cache cache;\n" + "// a reference to the root node\n" + " Node root;\n" + "// the transaction manager registered with the cache\n" + " TransactionManager transactionManager;\n"; } protected void configureInterpreter(Interpreter interpreter, CacheModelDelegate cacheDelegate) throws EvalError { interpreter.getNameSpace().importPackage("org.jboss.cache"); interpreter.getNameSpace().importPackage("org.jboss.cache.transaction"); interpreter.set("cache", cacheDelegate.getGenericCache()); interpreter.set("root", cacheDelegate.getGenericCache().getRoot()); interpreter.set("transactionManager", cacheDelegate.getGenericCache().getConfiguration().getRuntimeConfig().getTransactionManager()); } /* ------------------ CacheListener interface ------------ */ @NodeCreated @NodeLoaded public void nodeCreated(NodeEvent e) { /* Updating the GUI upon node creation should be done sequentially to avoid concurrency issues when adding multiple nodes at the same time, for example for PojoCache, where attaching a pojo creates several nodes. Creating several visual nodes in paralell and trying to scroll to them is not a thread safe operation */ if (e.isPre()) { JBossCacheGUI.DisplayNode n; n = myNodeRoot.add(e.getFqn()); if (n != null) { tree_model.setRoot(myNodeRoot); tree_model.reload(); jtree.scrollPathToVisible(new TreePath(n.getPath())); } } } @NodeModified public void nodeModified(final NodeModifiedEvent e) { Runnable r = new Runnable() { public void run() { if (e.isPre() && selected_node != null && selected_node.getFqn().equals(e.getFqn()))//&& !isLocal) { clearTable(); Node n = root.getChild(e.getFqn()); if (n != null) { populateTable(n.getData()); } } } }; executor.execute(r); } @NodeRemoved @NodeEvicted public void nodeRemoved(final NodeEvent e) { if (log.isTraceEnabled()) { log.trace("node removed, updating GUI"); } Runnable r = new Runnable() { public void run() { if (e.isPre()) { JBossCacheGUI.DisplayNode n; n = myNodeRoot.findNode(e.getFqn()); if (n != null) { n.removeAllChildren(); n.removeFromParent(); tree_model.setRoot(myNodeRoot); tree_model.reload(); } } } }; executor.execute(r); } @ViewChanged public void viewChange(final ViewChangedEvent e) { Runnable r = new Runnable() { public void run() { List
mbrship; if (e.getNewView() != null && (mbrship = e.getNewView().getMembers()) != null) { membership.clear(); membership.addAll(mbrship); coordinator = mbrship.get(0); } } }; executor.execute(r); } /* ---------------- End of CacheListener interface -------- */ /*----------------- Runnable implementation to make View change calles in AWT Thread ---*/ public void run() { } /* ----------------------------- Private Methods ---------------------------------- */ /** * Fetches all data from underlying cache model and display it graphically */ private void init() { List
mbrship; mbrship = getMembers(); if (mbrship != null && mbrship.size() > 0) { membership.clear(); membership.addAll(mbrship); coordinator = mbrship.get(0); } } /** * Fetches all data from underlying cache model and display it graphically */ private void populateTree() { addGuiNode(Fqn.ROOT); } /** * Recursively adds GUI nodes starting from fqn */ private void addGuiNode(Fqn fqn) { Set children; if (fqn == null) return; // 1 . Add myself myNodeRoot.add(fqn); // 2. Then add my children children = cache.getRoot().getChild(fqn).getChildrenNames(); if (children != null) { for (Object child_name : children) { addGuiNode(Fqn.fromRelativeElements(fqn, (String) child_name)); } } } private Node getNode(Object[] path) { Fqn fqnToPath; if (path.length == 0) fqnToPath = Fqn.ROOT; List elements = convertMyNodeArrayToStringArray(path); fqnToPath = Fqn.fromList(elements); if (root.hasChild(fqnToPath)) { return root.getChild(fqnToPath); } else { /* No implicit creation, otherwise removing GUI selected nodes will be recreated */ return null; } } private List convertMyNodeArrayToStringArray(Object[] path) { List list = new LinkedList(); for (Object o : path) { JBossCacheGUI.DisplayNode n = (JBossCacheGUI.DisplayNode) o; if (!n.name.equals(Fqn.SEPARATOR)) list.add(n.name); } return list; } protected void clearTable() { int num_rows = table.getRowCount(); if (num_rows > 0) { for (int i = 0; i < num_rows; i++) { tableModel.removeRow(0); } tableModel.fireTableRowsDeleted(0, num_rows - 1); repaint(); } } protected void populateTable(Map data) { String key; String val; int num_rows; clearTable(); if (data == null) return; num_rows = data.size(); if (num_rows > 0) { // sort this according to the natural sort order of keys TreeMap sortedMap = new TreeMap(data); for (Map.Entry entry : sortedMap.entrySet()) { key = entry.getKey(); val = entry.getValue(); tableModel.addRow(new Object[]{key, val}); } tableModel.fireTableRowsInserted(0, num_rows - 1); validate(); } } private void setTableColumnWidths() { table.sizeColumnsToFit(JTable.AUTO_RESIZE_NEXT_COLUMN); TableColumn column; column = table.getColumnModel().getColumn(0); column.setMinWidth(KEY_COL_WIDTH); column.setPreferredWidth(KEY_COL_WIDTH); column = table.getColumnModel().getColumn(1); column.setPreferredWidth(VAL_COL_WIDTH); } private void createMenus() { JMenuBar menubar = new JMenuBar(); JMenu operationsMenu = new JMenu("Operations"); JBossCacheGUI.AddNodeAction addNode = new JBossCacheGUI.AddNodeAction(); addNode.putValue(AbstractAction.NAME, "Add to this node"); JBossCacheGUI.LoadAction load_action = new JBossCacheGUI.LoadAction(); load_action.putValue(AbstractAction.NAME, "Load from the CacheLoader"); JBossCacheGUI.RemoveNodeAction removeNode = new JBossCacheGUI.RemoveNodeAction(); removeNode.putValue(AbstractAction.NAME, "Remove this node"); JBossCacheGUI.EvictAction evict_action = new JBossCacheGUI.EvictAction(); evict_action.putValue(AbstractAction.NAME, "Evict from the Cache"); JBossCacheGUI.AddModifyDataForNodeAction addModAction = new JBossCacheGUI.AddModifyDataForNodeAction(); addModAction.putValue(AbstractAction.NAME, "Add/Modify data"); JBossCacheGUI.PrintLockInfoAction print_locks = new JBossCacheGUI.PrintLockInfoAction(); print_locks.putValue(AbstractAction.NAME, "Print lock information" + (useConsole ? "" : " (stdout)")); JBossCacheGUI.ExitAction exitAction = new JBossCacheGUI.ExitAction(); exitAction.putValue(AbstractAction.NAME, "Exit"); JBossCacheGUI.StartTransaction start_tx = new JBossCacheGUI.StartTransaction(); start_tx.putValue(AbstractAction.NAME, "Start TX"); JBossCacheGUI.CommitTransaction commit_tx = new JBossCacheGUI.CommitTransaction(); commit_tx.putValue(AbstractAction.NAME, "Commit TX"); JBossCacheGUI.RollbackTransaction rollback_tx = new JBossCacheGUI.RollbackTransaction(); rollback_tx.putValue(AbstractAction.NAME, "Rollback TX"); operationsMenu.add(addNode); operationsMenu.add(load_action); operationsMenu.add(removeNode); operationsMenu.add(evict_action); operationsMenu.add(addModAction); operationsMenu.add(print_locks); operationsMenu.add(start_tx); operationsMenu.add(commit_tx); operationsMenu.add(rollback_tx); operationsMenu.add(exitAction); menubar.add(operationsMenu); setJMenuBar(menubar); operationsPopup = new JPopupMenu(); operationsPopup.add(addNode); operationsPopup.add(load_action); operationsPopup.add(evict_action); operationsPopup.add(removeNode); operationsPopup.add(addModAction); JBossCacheGUI.InsertRowAction insertRow = new JBossCacheGUI.InsertRowAction(); insertRow.putValue(AbstractAction.NAME, "Insert New Row"); JBossCacheGUI.RemoveLastRowAction removeLastRow = new JBossCacheGUI.RemoveLastRowAction(); removeLastRow.putValue(AbstractAction.NAME, "Remove Last Row"); dataModificationsPopup = new JPopupMenu(); dataModificationsPopup.add(insertRow); dataModificationsPopup.add(removeLastRow); } private Object getLocalAddress() { try { return cache.getLocalAddress(); } catch (Throwable t) { log.error("JBossCacheGUI.getLocalAddress(): ", t); return null; } } private void load(Fqn fqn) { try { // this will cause the cache to load the relevant node from a cache loader. cache.getRoot().getChild(fqn); } catch (Throwable t) { log.error("JBossCacheGUI.load(): " + t); } } private List
getMembers() { try { return new ArrayList
(cache.getMembers()); } catch (Throwable t) { log.error("TreeCacheGui.getMembers(): ", t); return null; } } private String[] getRowPlaceHolderData() { Collection keys = extractKeys(tableModel.getDataVector()); // try all place holders int count = 0; String placeHolderKey = "{ --- Add Key --- }"; while (keys.contains(placeHolderKey)) { count++; placeHolderKey = "{ --- Add Key " + count + " --- }"; } String placeHolderValue = "{ --- Add Value " + (count == 0 ? "" : count) + " --- }"; return new String[]{placeHolderKey, placeHolderValue}; } private boolean isPlaceHolder(String s) { if (s.startsWith("{ --- Add Key ") && s.endsWith(" --- }")) return true; if (s.startsWith("{ --- Add Value ") && s.endsWith(" --- }")) return true; return false; } private Collection extractKeys(Vector v) { // very odd data structure. Entire table is represented as a Vector. Each row (element in the Vector) is a Vector of 2 elements (key and value) List l = new LinkedList(); for (Vector row : v) { // just add keys l.add(row.get(0)); } return l; } private void printAndLogStacktrace(String message, Throwable t) { t.printStackTrace(); log.error(message, t); } /* -------------------------- End of Private Methods ------------------------------ */ /*----------------------- Actions ---------------------------*/ class ExitAction extends AbstractAction { private static final long serialVersionUID = -5364163916172148038L; public void actionPerformed(ActionEvent e) { stopGui(); } } void stopGui() { if (cache != null) { try { cache.stop(); cache.destroy(); cache = null; } catch (Throwable t) { printAndLogStacktrace("Stopping and destroying cache failed: ", t); } } dispose(); System.exit(0); } class InsertRowAction extends AbstractAction { private static final long serialVersionUID = 7084928639244438800L; public void actionPerformed(ActionEvent e) { tableModel.addRow(getRowPlaceHolderData()); } } class RemoveLastRowAction extends AbstractAction { private static final long serialVersionUID = 7084928639244438800L; public void actionPerformed(ActionEvent e) { int lastRow = tableModel.getRowCount() - 1; if (lastRow > -1) { String keyToRemove = (String) tableModel.getValueAt(lastRow, 0); tableModel.removeRow(lastRow); selected_node.remove(keyToRemove); } } } class AddNodeAction extends AbstractAction { private static final long serialVersionUID = 7084928639244438800L; public void actionPerformed(ActionEvent e) { JTextField fqnTextField = new JTextField(); if (selected_node != null) { fqnTextField.setText(selected_node.getFqn().toString()); } else { fqnTextField.setText(Fqn.SEPARATOR); } Object[] information = {"Enter fully qualified name", fqnTextField}; final String btnString1 = "OK"; final String btnString2 = "Cancel"; Object[] options = {btnString1, btnString2}; int userChoice = JOptionPane.showOptionDialog(null, information, "Add DataNode", JOptionPane.YES_NO_OPTION, JOptionPane.PLAIN_MESSAGE, null, options, options[0]); if (userChoice == 0) { String userInput = fqnTextField.getText(); cache.put(Fqn.fromString(userInput), null); } } } class LoadAction extends AbstractAction { private static final long serialVersionUID = -6998760732995584428L; public void actionPerformed(ActionEvent e) { JTextField fqnTextField = new JTextField(); if (selected_node != null) { fqnTextField.setText(selected_node.getFqn().toString()); } else { fqnTextField.setText(Fqn.SEPARATOR); } Object[] information = {"Enter fully qualified name", fqnTextField}; final String btnString1 = "OK"; final String btnString2 = "Cancel"; Object[] options = {btnString1, btnString2}; int userChoice = JOptionPane.showOptionDialog(null, information, "Load DataNode", JOptionPane.YES_NO_OPTION, JOptionPane.PLAIN_MESSAGE, null, options, options[0]); if (userChoice == 0) { String userInput = fqnTextField.getText(); load(Fqn.fromString(userInput)); } } } class EvictAction extends AbstractAction { private static final long serialVersionUID = 6007500908549034215L; public void actionPerformed(ActionEvent e) { JTextField fqnTextField = new JTextField(); if (selected_node != null) { fqnTextField.setText(selected_node.getFqn().toString()); } else { fqnTextField.setText(Fqn.SEPARATOR); } Object[] information = {"Enter fully qualified name", fqnTextField}; final String btnString1 = "OK"; final String btnString2 = "Cancel"; Object[] options = {btnString1, btnString2}; int userChoice = JOptionPane.showOptionDialog(null, information, "Evict DataNode", JOptionPane.YES_NO_OPTION, JOptionPane.PLAIN_MESSAGE, null, options, options[0]); if (userChoice == 0) { String userInput = fqnTextField.getText(); cache.evict(Fqn.fromString(userInput), true); } } } class StartTransaction extends AbstractAction { private static final long serialVersionUID = 7059131008813144857L; public void actionPerformed(ActionEvent e) { if (tx_mgr == null) { log.error("no TransactionManager specified"); return; } if (tx != null) { log.error("transaction is already running: " + tx); return; } try { tx_mgr.begin(); tx = tx_mgr.getTransaction(); } catch (Throwable t) { printAndLogStacktrace("Creating transaction failed: ", t); } } } class CommitTransaction extends AbstractAction { private static final long serialVersionUID = 5426108920883879873L; public void actionPerformed(ActionEvent e) { if (tx == null) { log.error("transaction is not running"); return; } try { tx.commit(); } catch (Throwable t) { printAndLogStacktrace("Commiting transaction failed: ", t); } finally { tx = null; } } } class RollbackTransaction extends AbstractAction { private static final long serialVersionUID = -4836748411400541430L; public void actionPerformed(ActionEvent e) { if (tx == null) { log.error("transaction is not running"); return; } try { tx.rollback(); } catch (Throwable t) { printAndLogStacktrace("Transaction rollback failed: ", t); } finally { tx = null; } } } class PrintLockInfoAction extends AbstractAction { private static final long serialVersionUID = -2171307516592250436L; public void actionPerformed(ActionEvent e) { if (bshConsole != null) { new Thread() { public void run() { bshConsole.getOut().println("\n*** lock information ****\n" + CachePrinter.printCacheLockingInfo(cache)); } }.start(); } else { new Thread() { public void run() { System.out.println("\n*** lock information ****\n" + CachePrinter.printCacheLockingInfo(cache)); } }.start(); } } } class RemoveNodeAction extends AbstractAction { private static final long serialVersionUID = 3746013603940497991L; public void actionPerformed(ActionEvent e) { try { cache.removeNode(selected_node.getFqn()); } catch (Throwable t) { log.error("RemoveNodeAction.actionPerformed(): " + t); } } } class AddModifyDataForNodeAction extends AbstractAction { private static final long serialVersionUID = -7656592171312920825L; public void actionPerformed(ActionEvent e) { if (log.isTraceEnabled()) { log.trace("node added/modified, updating GUI: " + e); } if (selected_node == null) return; clearTable(); Map data = selected_node.getData(); if (data == null || data.isEmpty()) { data = new HashMap(); String[] placeHolder = getRowPlaceHolderData(); data.put(placeHolder[0], placeHolder[1]); } populateTable(data); mainPanel.add(tablePanel, BorderLayout.SOUTH); validate(); } } class DisplayNode extends DefaultMutableTreeNode { private static final long serialVersionUID = 4882445905140460053L; String name = ""; DisplayNode(String name) { this.name = name; } /** * Adds a new node to the view. Intermediary nodes will be created if they don't yet exist. * Returns the first node that was created or null if node already existed */ public JBossCacheGUI.DisplayNode add(Fqn fqn) { JBossCacheGUI.DisplayNode curr, n, ret = null; if (fqn == null) return null; curr = this; for (String child_name : fqn.peekElements()) { n = curr.findChild(child_name); if (n == null) { n = new JBossCacheGUI.DisplayNode(child_name); if (ret == null) ret = n; curr.add(n); } curr = n; } return ret; } /** * Removes a node from the view. Child nodes will be removed as well */ public void remove() { removeFromParent(); } private JBossCacheGUI.DisplayNode findNode(Fqn fqn) { JBossCacheGUI.DisplayNode curr, n; if (fqn == null) return null; curr = this; for (String child_name : fqn.peekElements()) { n = curr.findChild(child_name); if (n == null) { return null; } curr = n; } return curr; } private JBossCacheGUI.DisplayNode findChild(String relative_name) { JBossCacheGUI.DisplayNode child; if (relative_name == null || getChildCount() == 0) { return null; } for (int i = 0; i < getChildCount(); i++) { child = (JBossCacheGUI.DisplayNode) getChildAt(i); if (child.name == null) { continue; } if (child.name.equals(relative_name)) { return child; } } return null; } public String toString() { return name; } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/demo/JBossCacheModelDelegate.java0000644000175000017500000000126010622575067030614 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.demo; import org.jboss.cache.Cache; /** * Model delegate implementation for JBossCache demo * * @author Galder Zamarreno */ public class JBossCacheModelDelegate implements CacheModelDelegate { private Cache cache; public void setCacheShellVariable(Object cache) { this.cache = (Cache) cache; } public Object getCacheShellVariable() { return cache; } public Cache getGenericCache() { return cache; } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/factories/0000755000175000017500000000000011376173732024425 5ustar twernertwerner././@LongLink0000000000000000000000000000014600000000000011566 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/factories/ComponentRegistryFunctionalTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/factories/ComponentRegistryFunctionalTest.jav0000644000175000017500000001735211010537324033477 0ustar twernertwernerpackage org.jboss.cache.factories; import org.testng.annotations.Test; /** * @author Manik Surtani (manik@jboss.org) * @since 2.1.0 */ @Test(groups = {"functional"}) public class ComponentRegistryFunctionalTest { // private ComponentRegistry cr; // private Configuration configuration; // // @BeforeMethod // public void setUp() throws Exception // { // CacheFactory cf = new DefaultCacheFactory(); // // Cache cache = cf.createCache(false); // cr = TestingUtil.extractComponentRegistry(cache); // configuration = cache.getConfiguration(); // } // // public void testDefaultFactoryScanning() // { // cr.scanDefaultFactories(); // // assert cr.defaultFactories != null : "Should be populated"; // // // at very least, expecting a Marshaller factory and a DefaultCacheFactory. // assert cr.defaultFactories.containsKey(Marshaller.class); // assert cr.defaultFactories.get(Marshaller.class).equals(EmptyConstructorFactory.class); // assert cr.defaultFactories.containsKey(Notifier.class); // assert cr.defaultFactories.get(Notifier.class).equals(EmptyConstructorFactory.class); // // } // // public void testDependencyConsistency() // { // for (ComponentRegistry.Component component : cr.componentLookup.values()) // { // // test that this component appears in all dependencies' dependencyFor collection. // for (ComponentRegistry.Component dep : component.dependencies) // { // assert cr.componentLookup.get(dep.name).dependencyFor.contains(component) : "Dependency " + dep.name + " does not have component " + component.name + " in it's dependencyFor collection."; // } // } // // for (ComponentRegistry.Component component : cr.componentLookup.values()) // { // // test that this component appears in all dependencies' dependencyFor collection. // for (ComponentRegistry.Component dep : component.dependencyFor) // { // assert cr.componentLookup.get(dep.name).dependencies.contains(component) : "Dependency " + dep.name + " does not have component " + component.name + " in it's dependencies collection."; // } // } // } // // // public void testNamedComponents() // { // cr.registerComponent("blah", new Object(), Object.class); // Object namedComponent1 = cr.getOrCreateComponent("blah", Object.class); // Object namedComponent2 = cr.getOrCreateComponent("blah", Object.class); // // assert namedComponent1 == namedComponent2; // } // // /** // * Case 1: // * nothing injected, nothing specified in Configuration. Should use default factory. // */ // public void testConstructionOrder1() // { // Class componentToTest = Marshaller.class; // Marshaller m = cr.getOrCreateComponent(null, componentToTest); // assert m instanceof VersionAwareMarshaller; // VersionAwareMarshaller vam = (VersionAwareMarshaller) m; // vam.initReplicationVersions(); // m = (Marshaller) TestingUtil.extractField(vam, "defaultMarshaller"); // assert m instanceof CacheMarshaller210; // } // // /** // * Case 2: // * instance injected, class specified in Configuration. Should use injected. // */ // public void testConstructionOrder2() // { // Class componentToTest = Marshaller.class; // configuration.setMarshallerClass(CacheMarshaller200.class.getName()); // Marshaller instance = new CacheMarshaller210(); // configuration.setCacheMarshaller(instance); // // // the setup() would have wired the default marshaller. Need to update deps. // cr.unregisterComponent(Marshaller.class); // cr.updateDependencies(); // // Marshaller m = cr.getOrCreateComponent(null, componentToTest); // assert m == instance : "m is " + m + " but expected " + instance; // } // // /** // * Case 3: // * instance injected, no class specified in Configuration. Should use injected. // */ // public void testConstructionOrder3() // { // Class componentToTest = Marshaller.class; // Marshaller instance = new CacheMarshaller210(); // configuration.setCacheMarshaller(instance); // // // the setup() would have wired the default marshaller. Need to update deps. // cr.unregisterComponent(Marshaller.class); // cr.updateDependencies(); // // Marshaller m = cr.getOrCreateComponent(null, componentToTest); // assert m == instance : "m is " + m + " but expected " + instance; // } // // /** // * Case 4: // * nothing injected, class specified in Configuration. Should use class specified. // */ // public void testConstructionOrder4() // { // Class componentToTest = Marshaller.class; // configuration.setMarshallerClass(CacheMarshaller200.class.getName()); // Marshaller m = cr.getOrCreateComponent(null, componentToTest); // assert m instanceof VersionAwareMarshaller; // VersionAwareMarshaller vam = (VersionAwareMarshaller) m; // vam.initReplicationVersions(); // m = (Marshaller) TestingUtil.extractField(vam, "defaultMarshaller"); // assert m instanceof CacheMarshaller200; // } // // public void testTransitiveDependencies() // { // Class componentToTest = BuddyManager.class; // // // configure the cfg to use BR // BuddyReplicationConfig brc = new BuddyReplicationConfig(); // brc.setEnabled(true); // BuddyReplicationConfig.BuddyLocatorConfig blc = new BuddyReplicationConfig.BuddyLocatorConfig(); // blc.setBuddyLocatorClass(NextMemberBuddyLocator.class.getName()); // brc.setBuddyLocatorConfig(blc); // configuration.setBuddyReplicationConfig(brc); // // // needs to be a non-LOCAL configuration // configuration.setCacheMode(Configuration.CacheMode.REPL_ASYNC); // BuddyManager bm = cr.getOrCreateComponent(null, componentToTest); // assert bm != null; // // StateTransferManager stm = (StateTransferManager) TestingUtil.extractField(bm, "stateTransferManager"); // assert stm != null; // // RPCManager rpcm = (RPCManager) TestingUtil.extractField(bm, "rpcManager"); // assert rpcm != null; // // RegionManager rm = (RegionManager) TestingUtil.extractField(bm, "regionManager"); // assert rm != null; // // Configuration cfg = (Configuration) TestingUtil.extractField(bm, "configuration"); // assert cfg == configuration; // } // // public void testInjectionOrder() // { // // injection should only occur after dependent components have been fully wired. // // // E.g. Test1 depends on Test2 and Test2 depends on Test3. // //cr.reset(); // // // DefaultFactoryFor annotation won't work since tests are compiled into a separate classpath // cr.defaultFactories.put(Test1.class, EmptyConstructorFactory.class); // cr.defaultFactories.put(Test2.class, EmptyConstructorFactory.class); // cr.defaultFactories.put(Test3.class, EmptyConstructorFactory.class); // // Test1 t1 = cr.getOrCreateComponent(null, Test1.class); // // assert t1 != null; // assert t1.test2 != null; // assert t1.test2.test3 != null; // assert t1.someValue == t1.test2.test3.someValue; // } // // public static class Test1 // { // private Test2 test2; // private boolean someValue = false; // // @Inject // public void setTest2(Test2 test2) // { // this.test2 = test2; // someValue = test2.test3.someValue; // } // } // // public static class Test2 // { // private Test3 test3; // // @Inject // public void setTest3(Test3 test3) // { // this.test3 = test3; // } // } // // public static class Test3 // { // private boolean someValue = true; // } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/factories/LateConfigurationTest.java0000644000175000017500000001070211017455042031532 0ustar twernertwernerpackage org.jboss.cache.factories; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.config.BuddyReplicationConfig; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.config.Configuration; import org.jboss.cache.interceptors.OptimisticLockingInterceptor; import org.jboss.cache.loader.DummyInMemoryCacheLoader; import org.jboss.cache.marshall.AbstractMarshaller; import org.jboss.cache.marshall.VersionAwareMarshaller; import org.jboss.cache.transaction.DummyTransactionManager; import org.jboss.cache.transaction.DummyTransactionManagerLookup; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; /** * @author Manik Surtani (manik@jboss.org) * @since 2.1.0 */ @Test(groups = "functional") public class LateConfigurationTest { CacheSPI c; @BeforeMethod public void setUp() { c = (CacheSPI) new DefaultCacheFactory().createCache(false); } @AfterMethod public void tearDown() { TestingUtil.killCaches(c); } public void testTransactionManager() { assert c.getTransactionManager() == null; c.getConfiguration().setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName()); assert c.getTransactionManager() == null; c.start(); assert c.getTransactionManager() == DummyTransactionManager.getInstance(); } public void testTransactionManagerinRuntime() { assert c.getTransactionManager() == null; c.getConfiguration().getRuntimeConfig().setTransactionManager(DummyTransactionManager.getInstance()); assert c.getTransactionManager() == null; c.start(); assert c.getTransactionManager() == DummyTransactionManager.getInstance(); } public void testCacheLoader() { assert c.getCacheLoaderManager() != null; assert c.getCacheLoaderManager().getCacheLoader() == null; CacheLoaderConfig clc = new CacheLoaderConfig(); CacheLoaderConfig.IndividualCacheLoaderConfig iclc = new CacheLoaderConfig.IndividualCacheLoaderConfig(); iclc.setCacheLoader(new DummyInMemoryCacheLoader()); clc.addIndividualCacheLoaderConfig(iclc); c.getConfiguration().setCacheLoaderConfig(clc); c.start(); assert c.getCacheLoaderManager() != null; assert c.getCacheLoaderManager().getCacheLoader() instanceof DummyInMemoryCacheLoader; } public void testBuddyManagerLocal() { // leaving cache mode as local assert c.getBuddyManager() == null; BuddyReplicationConfig brc = new BuddyReplicationConfig(); brc.setEnabled(true); c.getConfiguration().setBuddyReplicationConfig(brc); c.start(); assert c.getBuddyManager() == null; } public void testBuddyManager() { // we need to not be LOCAL if we want things to work with BR! c.getConfiguration().setCacheMode(Configuration.CacheMode.REPL_SYNC); assert c.getBuddyManager() == null; BuddyReplicationConfig brc = new BuddyReplicationConfig(); brc.setEnabled(true); c.getConfiguration().setBuddyReplicationConfig(brc); c.start(); assert c.getBuddyManager() != null; assert c.getBuddyManager().isEnabled(); } public void testInterceptors() { assert TestingUtil.findInterceptor(c, OptimisticLockingInterceptor.class) == null; c.getConfiguration().setNodeLockingScheme("OPTIMISTIC"); assert TestingUtil.findInterceptor(c, OptimisticLockingInterceptor.class) == null; c.start(); assert TestingUtil.findInterceptor(c, OptimisticLockingInterceptor.class) != null; } public void testCacheMarshaller() { assert c.getMarshaller() instanceof VersionAwareMarshaller; c.getConfiguration().setCacheMarshaller(new AbstractMarshaller() { public void objectToObjectStream(Object obj, ObjectOutputStream out) throws Exception { } public Object objectFromObjectStream(ObjectInputStream in) throws Exception { return null; } public void objectToObjectStream(Object obj, ObjectOutputStream out, Fqn region) { } }); c.start(); assert !(c.getMarshaller() instanceof VersionAwareMarshaller) && c.getMarshaller() != null; } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/factories/InterceptorChainTestBase.java0000644000175000017500000000131311005354216032145 0ustar twernertwernerpackage org.jboss.cache.factories; import org.jboss.cache.interceptors.base.CommandInterceptor; import static org.testng.AssertJUnit.assertEquals; import java.util.List; /** * @author Manik Surtani */ public abstract class InterceptorChainTestBase { protected void assertInterceptorLinkage(List list) { CommandInterceptor previous = null; for (CommandInterceptor i : list) { if (previous == null) { previous = i; continue; } assertEquals("Expecting the next interceptor after " + previous + " to be " + i, i, previous.getNext()); previous = i; } } }jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/factories/ComponentRegistryUnitTest.java0000644000175000017500000000653611010537324032457 0ustar twernertwernerpackage org.jboss.cache.factories; import org.jboss.cache.Cache; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.config.Configuration; import org.testng.annotations.Test; /** * @author Manik Surtani (manik@jboss.org) * @since 2.1.0 */ @Test(groups = "functional") public class ComponentRegistryUnitTest { public void testConstruction() { Cache c = new DefaultCacheFactory().createCache(UnitTestCacheConfigurationFactory.createConfiguration(Configuration.CacheMode.REPL_SYNC)); c.put("/a", "b", "c"); c.stop(); c.destroy(); } // ComponentRegistry cr; // Configuration cfg; // // @BeforeMethod // public void setUp() // { // cr = new ComponentRegistry(new Configuration()); // cfg = cr.getConfiguration(); // } // // public void testChangingComponentState() // { // cr.registerComponent("c2", new C2(), C2.class); // cr.registerComponent("c1", new C1(), C1.class); // cr.registerComponent("c3", new C3(), C3.class); // // ComponentRegistry.Component c1 = cr.componentLookup.get("c1"); // ComponentRegistry.Component c2 = cr.componentLookup.get("c2"); // ComponentRegistry.Component c3 = cr.componentLookup.get("c3"); // // // add some dependencies // ComponentRegistry.Component d1 = cr.new Component("c1", null); // ComponentRegistry.Component d2 = cr.new Component("c2", null); // ComponentRegistry.Component d3 = cr.new Component("c3", null); // // // c1 depends on c2 // // c3 depends on c1 // // // test dependency and dependencyFor // // assert c2.dependencies.isEmpty(); // assert c1.dependencies.contains(d2); // assert c1.dependencies.size() == 1; // assert c3.dependencies.contains(d1); // assert c3.dependencies.size() == 1; // // assert c2.dependencyFor.contains(d1); // assert c2.dependencyFor.size() == 1; // assert c1.dependencyFor.contains(d3); // assert c1.dependencyFor.size() == 1; // assert c3.dependencyFor.isEmpty(); // // assert c1.state == ComponentRegistry.State.CONSTRUCTED; // assert c2.state == ComponentRegistry.State.CONSTRUCTED; // assert c3.state == ComponentRegistry.State.CONSTRUCTED; // // c1.changeState(ComponentRegistry.State.WIRED); // // assert c1.state == ComponentRegistry.State.WIRED; // assert c2.state == ComponentRegistry.State.WIRED; // assert c3.state == ComponentRegistry.State.CONSTRUCTED; // // c3.changeState(ComponentRegistry.State.STARTED); // // assert c1.state == ComponentRegistry.State.STARTED; // assert c2.state == ComponentRegistry.State.STARTED; // assert c3.state == ComponentRegistry.State.STARTED; // // c1.changeState(ComponentRegistry.State.CONSTRUCTED); // // assert c1.state == ComponentRegistry.State.CONSTRUCTED; // assert c2.state == ComponentRegistry.State.STARTED; // assert c3.state == ComponentRegistry.State.CONSTRUCTED; // } // // public static class C1 // { // C2 c2; // // @Inject // private void inject(@ComponentName("c2")C2 c2) // { // this.c2 = c2; // } // } // // public static class C2 // { // // } // // public static class C3 // { // C1 c1; // // @Inject // private void inject(@ComponentName("c1")C1 c1) // { // this.c1 = c1; // } // } } ././@LongLink0000000000000000000000000000015000000000000011561 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/factories/UnitTestCacheConfigurationFactory.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/factories/UnitTestCacheConfigurationFactory.j0000644000175000017500000002271210776507472033374 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.factories; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.config.ConfigurationException; import org.jboss.cache.config.EvictionRegionConfig; import org.jboss.cache.eviction.LRUConfiguration; import org.jboss.cache.transaction.TransactionSetup; import org.jboss.cache.xml.XmlHelper; import org.jgroups.conf.XmlConfigurator; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import java.io.InputStream; import java.net.URL; /** * Cache configuration factory used by unit tests. */ public class UnitTestCacheConfigurationFactory { public static final String JGROUPS_CHANNEL; public static final String JGROUPS_STACK_TYPE = "jgroups.stack"; public static final String DEFAULT_CONFIGURATION_FILE = "META-INF/unit-test-cache-service.xml"; static { JGROUPS_CHANNEL = System.getProperty(JGROUPS_STACK_TYPE, "udp"); } public static Configuration createConfiguration(CacheMode mode) throws ConfigurationException { return createConfiguration(mode, false, false); } public static Configuration createConfiguration(CacheMode mode, boolean useEviction) throws ConfigurationException { return createConfiguration(mode, useEviction, false); } public static Configuration createConfiguration(CacheMode mode, boolean useEviction, boolean usePassivation) throws ConfigurationException { return createConfiguration(mode, useEviction, usePassivation, false); } public static Configuration createConfiguration(CacheMode mode, boolean useEviction, boolean usePassivation, boolean killable) throws ConfigurationException { UnitTestXmlConfigurationParser parser = new UnitTestXmlConfigurationParser(); Configuration c = parser.parseFile(DEFAULT_CONFIGURATION_FILE, mode); if (!useEviction) { c.setEvictionConfig(null); } if (!usePassivation) { c.setCacheLoaderConfig(null); } c.setTransactionManagerLookupClass(TransactionSetup.getManagerLookup()); if (mode != CacheMode.LOCAL && killable) { String clusterConfig = c.getClusterConfig(); c.setClusterConfig(injectDiscard(clusterConfig, 0, 0)); } return c; } public static CacheLoaderConfig getSingleCacheLoaderConfig(String preload, String cacheloaderClass, String properties, boolean async, boolean fetchPersistentState, boolean shared) throws Exception { return getSingleCacheLoaderConfig(preload, cacheloaderClass, properties, async, fetchPersistentState, shared, false); } public static CacheLoaderConfig getSingleCacheLoaderConfig(String preload, String cacheloaderClass, String properties, boolean async, boolean fetchPersistentState, boolean shared, boolean purgeOnStartup) throws Exception { return getSingleCacheLoaderConfig(false, preload, cacheloaderClass, properties, async, fetchPersistentState, shared, purgeOnStartup); } protected static CacheLoaderConfig getSingleCacheLoaderConfig(boolean passivation, String preload, String cacheloaderClass, String properties, boolean async, boolean fetchPersistentState, boolean shared, boolean purgeOnStartup) throws Exception { String xml = "\n" + "" + passivation + "\n" + "" + preload + "\n" + "\n" + "" + cacheloaderClass + "\n" + "" + properties + "\n" + "" + async + "\n" + "" + shared + "\n" + "" + fetchPersistentState + "\n" + "" + purgeOnStartup + "\n" + "\n" + ""; Element element = XmlHelper.stringToElement(xml); return XmlConfigurationParser.parseCacheLoaderConfig(element); } /** * Helper method that takes a JGroups configuration file and creates an old-style JGroups config {@link String} that can be used * in {@link org.jboss.cache.config.Configuration#setClusterConfig(String)}. Note that expressions * in the file - such as ${jgroups.udp.mcast_port:45588} are expanded out accordingly. * * @param url url to the cfg file * @return a String */ public static String getClusterConfigFromFile(URL url) { try { XmlConfigurator conf = XmlConfigurator.getInstance(url); String tmp = conf.getProtocolStackString(); // parse this string for ${} substitutions // Highly crappy approach!! tmp = tmp.replace("${jgroups.udp.mcast_addr:228.10.10.10}", "228.10.10.10"); tmp = tmp.replace("${jgroups.udp.mcast_port:45588}", "45588"); tmp = tmp.replace("${jgroups.udp.ip_ttl:2}", "2"); System.out.println("config string: " + tmp); return tmp; } catch (Exception e) { throw new RuntimeException("Problems with url " + url, e); } } /** * Takes a JGroups configuration "old-style" String and injects the "DELAY" protcol. * * @param jgroupsConfigString JGroups config string * @param incomingDelay incoming delay * @param outgoingDelay outgoing delay * @return new string */ public static String injectDelay(String jgroupsConfigString, int incomingDelay, int outgoingDelay) { String delay = ":DELAY(in_delay=" + incomingDelay + ";out_delay=" + outgoingDelay + ")"; return jgroupsConfigString.substring(0, jgroupsConfigString.indexOf(":")) + delay + jgroupsConfigString.substring(jgroupsConfigString.indexOf(":")); } /** * Takes a JGroups configuration "old-style" String and injects the "DISCARD" protcol. * * @param jgroupsConfigString JGroups config string * @param up factor of incoming messages to discard. 0 is none, 1 is all. * @param down factor of outgoing messages to discard. 0 is none, 1 is all. * @return new string */ public static String injectDiscard(String jgroupsConfigString, double up, double down) { String delay = ":DISCARD(up=" + up + ";down=" + down + ")"; return jgroupsConfigString.substring(0, jgroupsConfigString.indexOf(":")) + delay + jgroupsConfigString.substring(jgroupsConfigString.indexOf(":")); } public static EvictionRegionConfig buildLruEvictionRegionConfig(String regionNaame, int maxNodes, int timeToLive) { EvictionRegionConfig erc = new EvictionRegionConfig(); erc.setRegionName(regionNaame); LRUConfiguration lruConfig = new LRUConfiguration(); lruConfig.setEvictionPolicyClass("org.jboss.cache.eviction.LRUPolicy"); if (maxNodes >= 0) lruConfig.setMaxNodes(maxNodes); if (timeToLive >= 0) lruConfig.setTimeToLiveSeconds(timeToLive); erc.setEvictionPolicyConfig(lruConfig); return erc; } private static class UnitTestXmlConfigurationParser extends XmlConfigurationParser { public Configuration parseFile(String filename, CacheMode mode) { return parseStream(getAsInputStreamFromClassLoader(filename == null ? DEFAULT_CONFIGURATION_FILE : filename), mode); } public Configuration parseStream(InputStream stream, CacheMode mode) { // loop through all elements in XML. if (stream == null) throw new ConfigurationException("Input stream for configuration xml is null!"); Element root = XmlHelper.getDocumentRoot(stream); Element mbeanElement = getMBeanElement(root); ParsedAttributes attributes = extractAttributes(mbeanElement); // Deal with rename of the old property that controlled MBean registration String keepStats = attributes.stringAttribs.remove("UseMbean"); if (keepStats != null && attributes.stringAttribs.get("ExposeManagementStatistics") == null) { attributes.stringAttribs.put("ExposeManagementStatistics", keepStats); } Configuration c = new Configuration(); setValues(c, attributes.stringAttribs, false); // Special handling for XML elements -- we hard code the parsing setXmlValues(c, attributes.xmlAttribs); Element list = (Element) root.getElementsByTagName("protocol_stacks").item(0); NodeList stacks = list.getElementsByTagName("stack"); for (int i = 0; i < stacks.getLength(); i++) { Element stack = (Element) stacks.item(i); String stackName = stack.getAttribute("name"); if (stackName.startsWith(JGROUPS_CHANNEL)) { Element jgroupsStack = (Element) stack.getElementsByTagName("config").item(0); if (mode == CacheMode.REPL_ASYNC && !stackName.contains("-")) { c.setClusterConfig(jgroupsStack); c.setCacheMode(CacheMode.REPL_ASYNC); break; } else if (mode == CacheMode.REPL_SYNC && stackName.contains("-")) { c.setClusterConfig(jgroupsStack); c.setCacheMode(CacheMode.REPL_SYNC); break; } } } // either way, set mode in the config!! c.setCacheMode(mode); return c; } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/factories/LifeCycleTest.java0000644000175000017500000003312611017455042027761 0ustar twernertwernerpackage org.jboss.cache.factories; import org.jboss.cache.Cache; import org.jboss.cache.CacheException; import org.jboss.cache.CacheSPI; import org.jboss.cache.CacheStatus; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.ReplicationException; import org.jboss.cache.SuspectException; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.config.Configuration; import org.jboss.cache.notifications.annotation.CacheListener; import org.jboss.cache.notifications.annotation.CacheStarted; import org.jboss.cache.notifications.annotation.CacheStopped; import org.jboss.cache.notifications.event.Event; import org.testng.AssertJUnit; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.Test; import javax.transaction.NotSupportedException; import javax.transaction.RollbackException; import javax.transaction.SystemException; import javax.transaction.TransactionManager; import java.util.Arrays; import java.util.LinkedList; import java.util.List; /** * Tests restart (stop-destroy-create-start) of ComponentRegistry * * @author Bela Ban * @version $Id: LifeCycleTest.java 5906 2008-05-29 07:24:18Z mircea.markus $ */ @Test(groups = "functional") public class LifeCycleTest { private CacheSPI[] c; @AfterMethod public void tearDown() { TestingUtil.killCaches(c); c = null; } private void createAndRegisterCache(Configuration.CacheMode mode, boolean start) throws Exception { Cache cache = createCache(mode); List caches = new LinkedList(); if (c != null) caches.addAll(Arrays.asList(c)); caches.add(cache); c = caches.toArray(new CacheSPI[]{}); if (start) { cache.start(); if (c.length > 1) TestingUtil.blockUntilViewsReceived(c, 10000); } } public void testLocalRestartNoTransactions() throws Exception { createAndRegisterCache(Configuration.CacheMode.LOCAL, true); c[0].put("/a/b/c", null); assertTrue(c[0].getNumberOfNodes() > 0); assertEquals(0, c[0].getNumberOfLocksHeld()); restartCache(c[0]); assertEquals(0, c[0].getNumberOfNodes()); assertEquals(0, c[0].getNumberOfLocksHeld()); } public void testLocalRestartWithTransactions() throws Exception { createAndRegisterCache(Configuration.CacheMode.LOCAL, true); TransactionManager tm = beginTransaction(); c[0].put("/a/b/c", null); assertTrue(c[0].getNumberOfNodes() > 0); assertEquals(4, c[0].getNumberOfLocksHeld()); restartCache(c[0]); //assertEquals(4, cache.getNumberOfLocksHeld()); assertEquals(0, c[0].getNumberOfNodes()); tm.rollback(); assertEquals(0, c[0].getNumberOfLocksHeld()); } public void testStartNoCreate() throws Exception { createAndRegisterCache(Configuration.CacheMode.LOCAL, false); c[0].start(); c[0].put("/a/b/c", null); assertTrue(c[0].getNumberOfNodes() > 0); assertEquals(0, c[0].getNumberOfLocksHeld()); restartCache(c[0]); assertEquals(0, c[0].getNumberOfNodes()); assertEquals(0, c[0].getNumberOfLocksHeld()); } public void testReStartNoCreate() throws Exception { createAndRegisterCache(Configuration.CacheMode.LOCAL, false); c[0].start(); c[0].stop(); c[0].start(); c[0].put("/a/b/c", null); assertTrue(c[0].getNumberOfNodes() > 0); assertEquals(0, c[0].getNumberOfLocksHeld()); restartCache(c[0]); assertEquals(0, c[0].getNumberOfNodes()); assertEquals(0, c[0].getNumberOfLocksHeld()); } public void testDuplicateInvocation() throws Exception { createAndRegisterCache(Configuration.CacheMode.LOCAL, false); c[0].create(); c[0].start(); c[0].create(); c[0].start(); c[0].put("/a/b/c", null); assertTrue(c[0].getNumberOfNodes() > 0); assertEquals(0, c[0].getNumberOfLocksHeld()); restartCache(c[0]); assertEquals(0, c[0].getNumberOfNodes()); assertEquals(0, c[0].getNumberOfLocksHeld()); c[0].stop(); c[0].destroy(); c[0].stop(); c[0].destroy(); } public void testFailedStart() throws Exception { createAndRegisterCache(Configuration.CacheMode.LOCAL, false); AssertJUnit.assertEquals("Correct state", CacheStatus.INSTANTIATED, c[0].getCacheStatus()); DisruptLifecycleListener listener = new DisruptLifecycleListener(); c[0].addCacheListener(listener); c[0].create(); listener.disrupt = true; assertEquals("Correct state", CacheStatus.CREATED, c[0].getCacheStatus()); try { c[0].start(); fail("Listener did not prevent start"); } catch (CacheException good) { } assertEquals("Correct state", CacheStatus.FAILED, c[0].getCacheStatus()); c[0].addCacheListener(listener); listener.disrupt = false; c[0].start(); assertEquals("Correct state", CacheStatus.STARTED, c[0].getCacheStatus()); c[0].put("/a/b/c", null); assertTrue(c[0].getNumberOfNodes() > 0); assertEquals(0, c[0].getNumberOfLocksHeld()); listener.disrupt = true; c[0].addCacheListener(listener); try { c[0].stop(); fail("Listener did not prevent stop"); } catch (CacheException good) { } assertEquals("Correct state", CacheStatus.FAILED, c[0].getCacheStatus()); listener.disrupt = false; c[0].stop(); assertEquals("Correct state", CacheStatus.STOPPED, c[0].getCacheStatus()); c[0].destroy(); assertEquals("Correct state", CacheStatus.DESTROYED, c[0].getCacheStatus()); } public void testInvalidStateInvocations() throws Exception { createAndRegisterCache(Configuration.CacheMode.LOCAL, false); try { c[0].get(Fqn.ROOT, "k"); fail("Cache isn't ready!"); } catch (IllegalStateException good) { } c[0].create(); try { c[0].get(Fqn.ROOT, "k"); fail("Cache isn't ready!"); } catch (IllegalStateException good) { } c[0].start(); c[0].get(Fqn.ROOT, "k"); // should work c[0].stop(); try { c[0].get(Fqn.ROOT, "k"); fail("Cache isn't ready!"); } catch (IllegalStateException good) { } c[0].destroy(); try { c[0].get(Fqn.ROOT, "k"); fail("Cache isn't ready!"); } catch (IllegalStateException good) { } } public void testRemoteInvalidStateInvocations() throws Exception { createAndRegisterCache(Configuration.CacheMode.REPL_SYNC, true); createAndRegisterCache(Configuration.CacheMode.REPL_SYNC, true); try { // now DIRECTLY change the status of c2. // emulate the race condition where the remote cache is stopping but hasn't disconnected from the channel. ComponentRegistry cr1 = TestingUtil.extractComponentRegistry(c[1]); cr1.state = CacheStatus.STOPPING; // Thanks to JBCACHE-1179, this should only log a warning and not throw an exception c[0].put(Fqn.ROOT, "k", "v"); } finally { // reset c[1] to running so the tearDown method can clean it up ComponentRegistry cr1 = TestingUtil.extractComponentRegistry(c[1]); cr1.state = CacheStatus.STARTED; } } public void testRemoteInvalidStateInvocations2() throws Exception { createAndRegisterCache(Configuration.CacheMode.REPL_SYNC, true); createAndRegisterCache(Configuration.CacheMode.REPL_SYNC, true); TestingUtil.blockUntilViewsReceived(c, 10000); try { // now DIRECTLY change the status of c2. // emulate the race condition where the remote cache is stopping but hasn't disconnected from the channel. // there is a lousy race condition here - we need to make sure cache[1]'s start() method doesn't set status to STARTED // after we attempt to change this. TestingUtil.blockUntilCacheStatusAchieved(c[1], CacheStatus.STARTED, 1000); ComponentRegistry cr1 = TestingUtil.extractComponentRegistry(c[1]); cr1.state = CacheStatus.STARTING; try { // This call should wait for up to StateRetrievalTimeout secs or until c[1] has entered the STARTED state, and then barf. c[0].put(Fqn.ROOT, "k", "v"); fail("Should barf!"); } catch (Exception good) { } // now kick off another thread to sleep for a few secs and then set c[1] to STARTED final int sleepTime = 500; new Thread() { public void run() { TestingUtil.sleepThread(sleepTime); ComponentRegistry cr1 = TestingUtil.extractComponentRegistry(c[1]); cr1.state = CacheStatus.STARTED; } }.start(); // should succeed but should take at least 1000ms. long startTime = System.currentTimeMillis(); c[0].put(Fqn.ROOT, "k", "v"); assert System.currentTimeMillis() > (startTime + sleepTime) : "Should wait till c[1] has STARTED state"; } finally { // reset c[1] to running so the tearDown method can clean it up ComponentRegistry cr1 = TestingUtil.extractComponentRegistry(c[1]); cr1.state = CacheStatus.STARTED; } } public void testInvalidStateTxCommit() throws Exception { createAndRegisterCache(Configuration.CacheMode.LOCAL, true); c[0].getTransactionManager().begin(); c[0].put(Fqn.ROOT, "k1", "v1"); c[0].put(Fqn.ROOT, "k2", "v2"); // now DIRECTLY change the status of c. ComponentRegistry cr0 = TestingUtil.extractComponentRegistry(c[0]); cr0.state = CacheStatus.STOPPING; try { c[0].getTransactionManager().commit(); fail("Cache isn't STARTED!"); } catch (RollbackException good) { } } @SuppressWarnings("unchecked") public void testStopInstanceWhileOtherInstanceSends() throws Exception { final Fqn fqn = Fqn.fromString("/a"); final List running = new LinkedList(); final List exceptions = new LinkedList(); running.add(true); createAndRegisterCache(Configuration.CacheMode.REPL_SYNC, true); createAndRegisterCache(Configuration.CacheMode.REPL_SYNC, true); c[0].put(fqn, "k", "v"); assert "v".equals(c[0].get(fqn, "k")); assert "v".equals(c[1].get(fqn, "k")); // now kick start a thread on c[1] that will constantly update the fqn Thread updater = new Thread() { public void run() { int i = 0; while (running.get(0)) { try { i++; if (running.get(0)) c[1].put(fqn, "k", "v" + i); } catch (ReplicationException re) { // this sometimes happens when JGroups suspects the remote node. This is ok, as long as we don't get an ISE. } catch (SuspectException se) { // this sometimes happens when JGroups suspects the remote node. This is ok, as long as we don't get an ISE. } catch (Exception e) { exceptions.add(e); } TestingUtil.sleepThread(20); } } }; updater.start(); c[0].stop(); running.add(false); running.remove(true); updater.join(); for (Exception e : exceptions) throw e; } public void testInvalidStateTxRollback() throws Exception { createAndRegisterCache(Configuration.CacheMode.LOCAL, true); c[0].getTransactionManager().begin(); c[0].put(Fqn.ROOT, "k1", "v1"); c[0].put(Fqn.ROOT, "k2", "v2"); // now DIRECTLY change the status of c. ComponentRegistry cr0 = TestingUtil.extractComponentRegistry(c[0]); cr0.state = CacheStatus.STOPPING; // rollbacks should just log a message c[0].getTransactionManager().rollback(); } private CacheSPI createCache(Configuration.CacheMode cache_mode) { CacheSPI retval = (CacheSPI) new DefaultCacheFactory().createCache(false); retval.getConfiguration().setCacheMode(cache_mode); retval.getConfiguration().setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); return retval; } private TransactionManager beginTransaction() throws SystemException, NotSupportedException { TransactionManager mgr = c[0].getConfiguration().getRuntimeConfig().getTransactionManager(); mgr.begin(); return mgr; } private void startCache(CacheSPI c) { c.create(); c.start(); } private void stopCache(CacheSPI c) { c.stop(); c.destroy(); } private void restartCache(CacheSPI c) throws Exception { stopCache(c); startCache(c); } @CacheListener public class DisruptLifecycleListener { private boolean disrupt; @CacheStarted public void cacheStarted(Event e) { if (disrupt) throw new IllegalStateException("I don't want to start"); } @CacheStopped public void cacheStopped(Event e) { if (disrupt) throw new IllegalStateException("I don't want to stop"); } public void setDisrupt(boolean disrupt) { this.disrupt = disrupt; } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/factories/InterceptorChainFactoryTest.java0000644000175000017500000005457511017455042032726 0ustar twernertwernerpackage org.jboss.cache.factories; import org.jboss.cache.Cache; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.config.BuddyReplicationConfig; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.config.EvictionConfig; import org.jboss.cache.interceptors.*; import org.jboss.cache.interceptors.base.CommandInterceptor; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.xml.XmlHelper; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertNotNull; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import org.w3c.dom.Element; import java.util.Iterator; import java.util.List; @Test(groups = {"functional"}) public class InterceptorChainFactoryTest extends InterceptorChainTestBase { CacheSPI cache = null; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { cache = (CacheSPI) new DefaultCacheFactory().createCache(false); cache.getConfiguration().setCacheMode("LOCAL"); cache.getConfiguration().setUseLazyDeserialization(false); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { if (cache != null) { cache.stop(); cache.destroy(); } } public void testBareConfig() throws Exception { cache.getConfiguration().setExposeManagementStatistics(false); InterceptorChain chain = getInterceptorChainFactory(cache).buildInterceptorChain(); List list = chain.asList(); Iterator interceptors = list.iterator(); System.out.println("testBareConfig interceptors are:\n" + list); assertNotNull(list); assertEquals(5, list.size()); assertEquals(InvocationContextInterceptor.class, interceptors.next().getClass()); assertEquals(TxInterceptor.class, interceptors.next().getClass()); assertEquals(NotificationInterceptor.class, interceptors.next().getClass()); assertEquals(PessimisticLockInterceptor.class, interceptors.next().getClass()); assertEquals(CallInterceptor.class, interceptors.next().getClass()); assertInterceptorLinkage(list); } public void testTxConfig() throws Exception { cache.getConfiguration().setExposeManagementStatistics(false); cache.getConfiguration().setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); InterceptorChain chain = getInterceptorChainFactory(cache).buildInterceptorChain(); List list = chain.asList(); Iterator interceptors = list.iterator(); System.out.println("testTxConfig interceptors are:\n" + list); assertNotNull(list); assertEquals(5, list.size()); assertEquals(InvocationContextInterceptor.class, interceptors.next().getClass()); assertEquals(TxInterceptor.class, interceptors.next().getClass()); assertEquals(NotificationInterceptor.class, interceptors.next().getClass()); assertEquals(PessimisticLockInterceptor.class, interceptors.next().getClass()); assertEquals(CallInterceptor.class, interceptors.next().getClass()); assertInterceptorLinkage(list); } protected CacheLoaderConfig getCacheLoaderConfig(boolean pasv, boolean fetchPersistentState) throws Exception { String xml = " \n" + " \n" + " " + pasv + "\n" + " \n" + "\n" + " \n" + " org.jboss.cache.loader.FileCacheLoader\n" + " \n" + " location=/tmp\n" + " \n" + " false\n" + " " + fetchPersistentState + "\n" + " false\n" + " \n" + " \n" + " "; Element element = XmlHelper.stringToElement(xml); return XmlConfigurationParser.parseCacheLoaderConfig(element); } public void testSharedCacheLoaderConfig() throws Exception { cache.getConfiguration().setExposeManagementStatistics(false); cache.getConfiguration().setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); cache.getConfiguration().setCacheLoaderConfig(getCacheLoaderConfig(false, false)); cache.getConfiguration().setCacheMode("REPL_ASYNC"); cache.getConfiguration().setFetchInMemoryState(false); cache.create(); InterceptorChain chain = getInterceptorChainFactory(cache).buildInterceptorChain(); List list = chain.asList(); Iterator interceptors = list.iterator(); System.out.println("testSharedCacheLoaderConfig interceptors are:\n" + list); assertNotNull(list); assertEquals(8, list.size()); assertEquals(InvocationContextInterceptor.class, interceptors.next().getClass()); assertEquals(TxInterceptor.class, interceptors.next().getClass()); assertEquals(NotificationInterceptor.class, interceptors.next().getClass()); assertEquals(ReplicationInterceptor.class, interceptors.next().getClass()); assertEquals(PessimisticLockInterceptor.class, interceptors.next().getClass()); assertEquals(CacheLoaderInterceptor.class, interceptors.next().getClass()); assertEquals(CacheStoreInterceptor.class, interceptors.next().getClass()); assertEquals(CallInterceptor.class, interceptors.next().getClass()); assertInterceptorLinkage(list); } public void testUnsharedCacheLoaderConfig() throws Exception { cache.getConfiguration().setExposeManagementStatistics(false); cache.getConfiguration().setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); cache.getConfiguration().setCacheLoaderConfig(getCacheLoaderConfig(false, true)); cache.getConfiguration().setCacheMode("REPL_ASYNC"); cache.getConfiguration().setFetchInMemoryState(false); cache.create(); InterceptorChain chain = getInterceptorChainFactory(cache).buildInterceptorChain(); List list = chain.asList(); Iterator interceptors = list.iterator(); System.out.println("testUnsharedCacheLoaderConfig interceptors are:\n" + list); assertNotNull(list); assertEquals(8, list.size()); assertEquals(InvocationContextInterceptor.class, interceptors.next().getClass()); assertEquals(TxInterceptor.class, interceptors.next().getClass()); assertEquals(NotificationInterceptor.class, interceptors.next().getClass()); assertEquals(ReplicationInterceptor.class, interceptors.next().getClass()); assertEquals(PessimisticLockInterceptor.class, interceptors.next().getClass()); assertEquals(CacheLoaderInterceptor.class, interceptors.next().getClass()); assertEquals(CacheStoreInterceptor.class, interceptors.next().getClass()); assertEquals(CallInterceptor.class, interceptors.next().getClass()); assertInterceptorLinkage(list); } public void testTxAndRepl() throws Exception { cache.getConfiguration().setExposeManagementStatistics(false); cache.getConfiguration().setCacheMode("repl_sync"); cache.getConfiguration().setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); InterceptorChain chain = getInterceptorChainFactory(cache).buildInterceptorChain(); List list = chain.asList(); Iterator interceptors = list.iterator(); System.out.println("testTxAndRepl interceptors are:\n" + list); assertNotNull(list); assertEquals(6, list.size()); assertEquals(InvocationContextInterceptor.class, interceptors.next().getClass()); assertEquals(TxInterceptor.class, interceptors.next().getClass()); assertEquals(NotificationInterceptor.class, interceptors.next().getClass()); assertEquals(ReplicationInterceptor.class, interceptors.next().getClass()); assertEquals(PessimisticLockInterceptor.class, interceptors.next().getClass()); assertEquals(CallInterceptor.class, interceptors.next().getClass()); assertInterceptorLinkage(list); } public void testOptimisticChain() throws Exception { cache.getConfiguration().setExposeManagementStatistics(false); cache.getConfiguration().setNodeLockingOptimistic(true); InterceptorChain chain = getInterceptorChainFactory(cache).buildInterceptorChain(); List list = chain.asList(); Iterator interceptors = list.iterator(); assertEquals(8, list.size()); assertEquals(InvocationContextInterceptor.class, interceptors.next().getClass()); assertEquals(OptimisticTxInterceptor.class, interceptors.next().getClass()); assertEquals(NotificationInterceptor.class, interceptors.next().getClass()); assertEquals(OptimisticLockingInterceptor.class, interceptors.next().getClass()); assertEquals(OptimisticValidatorInterceptor.class, interceptors.next().getClass()); assertEquals(OptimisticCreateIfNotExistsInterceptor.class, interceptors.next().getClass()); assertEquals(OptimisticNodeInterceptor.class, interceptors.next().getClass()); assertEquals(CallInterceptor.class, interceptors.next().getClass()); assertInterceptorLinkage(list); } public void testOptimisticReplicatedChain() throws Exception { cache.getConfiguration().setExposeManagementStatistics(false); cache.getConfiguration().setNodeLockingOptimistic(true); cache.getConfiguration().setCacheMode("REPL_SYNC"); InterceptorChain chain = getInterceptorChainFactory(cache).buildInterceptorChain(); List list = chain.asList(); Iterator interceptors = list.iterator(); assertEquals(9, list.size()); assertEquals(InvocationContextInterceptor.class, interceptors.next().getClass()); assertEquals(OptimisticTxInterceptor.class, interceptors.next().getClass()); assertEquals(NotificationInterceptor.class, interceptors.next().getClass()); assertEquals(OptimisticReplicationInterceptor.class, interceptors.next().getClass()); assertEquals(OptimisticLockingInterceptor.class, interceptors.next().getClass()); assertEquals(OptimisticValidatorInterceptor.class, interceptors.next().getClass()); assertEquals(OptimisticCreateIfNotExistsInterceptor.class, interceptors.next().getClass()); assertEquals(OptimisticNodeInterceptor.class, interceptors.next().getClass()); assertEquals(CallInterceptor.class, interceptors.next().getClass()); assertInterceptorLinkage(list); } public void testOptimisticCacheLoaderChain() throws Exception { cache.getConfiguration().setExposeManagementStatistics(false); cache.getConfiguration().setNodeLockingOptimistic(true); cache.getConfiguration().setCacheLoaderConfig(getCacheLoaderConfig(false, false)); cache.create(); InterceptorChain chain = getInterceptorChainFactory(cache).buildInterceptorChain(); List list = chain.asList(); Iterator interceptors = list.iterator(); assertEquals(10, list.size()); assertEquals(InvocationContextInterceptor.class, interceptors.next().getClass()); assertEquals(OptimisticTxInterceptor.class, interceptors.next().getClass()); assertEquals(NotificationInterceptor.class, interceptors.next().getClass()); assertEquals(CacheLoaderInterceptor.class, interceptors.next().getClass()); assertEquals(CacheStoreInterceptor.class, interceptors.next().getClass()); assertEquals(OptimisticLockingInterceptor.class, interceptors.next().getClass()); assertEquals(OptimisticValidatorInterceptor.class, interceptors.next().getClass()); assertEquals(OptimisticCreateIfNotExistsInterceptor.class, interceptors.next().getClass()); assertEquals(OptimisticNodeInterceptor.class, interceptors.next().getClass()); assertEquals(CallInterceptor.class, interceptors.next().getClass()); assertInterceptorLinkage(list); } public void testOptimisticPassivationCacheLoaderChain() throws Exception { cache.getConfiguration().setExposeManagementStatistics(false); cache.getConfiguration().setNodeLockingOptimistic(true); cache.getConfiguration().setCacheLoaderConfig(getCacheLoaderConfig(true, false)); cache.create(); InterceptorChain chain = getInterceptorChainFactory(cache).buildInterceptorChain(); List list = chain.asList(); Iterator interceptors = list.iterator(); assertEquals(10, list.size()); assertEquals(InvocationContextInterceptor.class, interceptors.next().getClass()); assertEquals(OptimisticTxInterceptor.class, interceptors.next().getClass()); assertEquals(NotificationInterceptor.class, interceptors.next().getClass()); assertEquals(ActivationInterceptor.class, interceptors.next().getClass()); assertEquals(PassivationInterceptor.class, interceptors.next().getClass()); assertEquals(OptimisticLockingInterceptor.class, interceptors.next().getClass()); assertEquals(OptimisticValidatorInterceptor.class, interceptors.next().getClass()); assertEquals(OptimisticCreateIfNotExistsInterceptor.class, interceptors.next().getClass()); assertEquals(OptimisticNodeInterceptor.class, interceptors.next().getClass()); assertEquals(CallInterceptor.class, interceptors.next().getClass()); assertInterceptorLinkage(list); } public void testInvalidationInterceptorChain() throws Exception { cache.getConfiguration().setExposeManagementStatistics(false); cache.getConfiguration().setCacheMode("REPL_ASYNC"); InterceptorChain chain = getInterceptorChainFactory(cache).buildInterceptorChain(); List list = chain.asList(); Iterator interceptors = list.iterator(); assertEquals(6, list.size()); assertEquals(InvocationContextInterceptor.class, interceptors.next().getClass()); assertEquals(TxInterceptor.class, interceptors.next().getClass()); assertEquals(NotificationInterceptor.class, interceptors.next().getClass()); assertEquals(ReplicationInterceptor.class, interceptors.next().getClass()); assertEquals(PessimisticLockInterceptor.class, interceptors.next().getClass()); assertEquals(CallInterceptor.class, interceptors.next().getClass()); // ok, my replication chain looks good. // now for my invalidation chain. cache.getConfiguration().setExposeManagementStatistics(false); cache.getConfiguration().setCacheMode("INVALIDATION_ASYNC"); chain = getInterceptorChainFactory(cache).buildInterceptorChain(); list = chain.asList(); interceptors = list.iterator(); assertEquals(6, list.size()); assertEquals(InvocationContextInterceptor.class, interceptors.next().getClass()); assertEquals(TxInterceptor.class, interceptors.next().getClass()); assertEquals(NotificationInterceptor.class, interceptors.next().getClass()); assertEquals(InvalidationInterceptor.class, interceptors.next().getClass()); assertEquals(PessimisticLockInterceptor.class, interceptors.next().getClass()); assertEquals(CallInterceptor.class, interceptors.next().getClass()); assertInterceptorLinkage(list); } public void testCacheMgmtConfig() throws Exception { cache.getConfiguration().setExposeManagementStatistics(true); InterceptorChain chain = getInterceptorChainFactory(cache).buildInterceptorChain(); List list = chain.asList(); Iterator interceptors = list.iterator(); System.out.println("testCacheMgmtConfig interceptors are:\n" + list); assertNotNull(list); assertEquals(6, list.size()); assertEquals(InvocationContextInterceptor.class, interceptors.next().getClass()); assertEquals(CacheMgmtInterceptor.class, interceptors.next().getClass()); assertEquals(TxInterceptor.class, interceptors.next().getClass()); assertEquals(NotificationInterceptor.class, interceptors.next().getClass()); assertEquals(PessimisticLockInterceptor.class, interceptors.next().getClass()); assertEquals(CallInterceptor.class, interceptors.next().getClass()); assertInterceptorLinkage(list); } public void testEvictionInterceptorConfig() throws Exception { cache.getConfiguration().setEvictionConfig(new EvictionConfig() { private static final long serialVersionUID = -6644183636899605065L; public boolean isValidConfig() { return true; } } ); InterceptorChain chain = getInterceptorChainFactory(cache).buildInterceptorChain(); List list = chain.asList(); Iterator interceptors = list.iterator(); System.out.println("testEvictionInterceptorConfig interceptors are:\n" + list); assertNotNull(list); assertEquals(7, list.size()); assertEquals(InvocationContextInterceptor.class, interceptors.next().getClass()); assertEquals(CacheMgmtInterceptor.class, interceptors.next().getClass()); assertEquals(TxInterceptor.class, interceptors.next().getClass()); assertEquals(NotificationInterceptor.class, interceptors.next().getClass()); assertEquals(PessimisticLockInterceptor.class, interceptors.next().getClass()); assertEquals(EvictionInterceptor.class, interceptors.next().getClass()); assertEquals(CallInterceptor.class, interceptors.next().getClass()); assertInterceptorLinkage(list); } public void testBuddyReplicationOptLocking() throws Exception { String xmlString = "true\n" + "600000\n" + " org.jboss.cache.buddyreplication.NextMemberBuddyLocator\n" + " numBuddies = 1\n"; xmlString += "buddyPoolName"; xmlString += ""; Element element = XmlHelper.stringToElement(xmlString); BuddyReplicationConfig brc = XmlConfigurationParser.parseBuddyReplicationConfig(element); cache.getConfiguration().setBuddyReplicationConfig(brc); cache.getConfiguration().setCacheMode("REPL_SYNC"); cache.getConfiguration().setNodeLockingScheme("OPTIMISTIC"); cache.create();// initialise various subsystems such as BRManager InterceptorChain chain = getInterceptorChainFactory(cache).buildInterceptorChain(); List list = chain.asList(); Iterator interceptors = list.iterator(); System.out.println("testEvictionInterceptorConfig interceptors are:\n" + list); assertNotNull(list); assertEquals(11, list.size()); assertEquals(InvocationContextInterceptor.class, interceptors.next().getClass()); assertEquals(CacheMgmtInterceptor.class, interceptors.next().getClass()); assertEquals(OptimisticTxInterceptor.class, interceptors.next().getClass()); assertEquals(NotificationInterceptor.class, interceptors.next().getClass()); assertEquals(OptimisticReplicationInterceptor.class, interceptors.next().getClass()); assertEquals(DataGravitatorInterceptor.class, interceptors.next().getClass()); assertEquals(OptimisticLockingInterceptor.class, interceptors.next().getClass()); assertEquals(OptimisticValidatorInterceptor.class, interceptors.next().getClass()); assertEquals(OptimisticCreateIfNotExistsInterceptor.class, interceptors.next().getClass()); assertEquals(OptimisticNodeInterceptor.class, interceptors.next().getClass()); assertEquals(CallInterceptor.class, interceptors.next().getClass()); assertInterceptorLinkage(list); } public void testBuddyReplicationPessLocking() throws Exception { String xmlString = "true\n" + "600000\n" + " org.jboss.cache.buddyreplication.NextMemberBuddyLocator\n" + " numBuddies = 1\n"; xmlString += "buddyPoolName"; xmlString += ""; Element element = XmlHelper.stringToElement(xmlString); BuddyReplicationConfig brc = XmlConfigurationParser.parseBuddyReplicationConfig(element); cache.getConfiguration().setBuddyReplicationConfig(brc); cache.getConfiguration().setCacheMode("REPL_SYNC"); cache.create();// initialise various subsystems such as BRManager InterceptorChain chain = getInterceptorChainFactory(cache).buildInterceptorChain(); List list = chain.asList(); Iterator interceptors = list.iterator(); System.out.println("testEvictionInterceptorConfig interceptors are:\n" + list); assertNotNull(list); assertEquals(8, list.size()); assertEquals(InvocationContextInterceptor.class, interceptors.next().getClass()); assertEquals(CacheMgmtInterceptor.class, interceptors.next().getClass()); assertEquals(TxInterceptor.class, interceptors.next().getClass()); assertEquals(NotificationInterceptor.class, interceptors.next().getClass()); assertEquals(ReplicationInterceptor.class, interceptors.next().getClass()); assertEquals(PessimisticLockInterceptor.class, interceptors.next().getClass()); assertEquals(DataGravitatorInterceptor.class, interceptors.next().getClass()); assertEquals(CallInterceptor.class, interceptors.next().getClass()); assertInterceptorLinkage(list); } private InterceptorChainFactory getInterceptorChainFactory(Cache cache) { return InterceptorChainFactory.getInstance(TestingUtil.extractComponentRegistry(cache), cache.getConfiguration()); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/factories/CustomInterceptorChainTest.java0000644000175000017500000001301211005354216032544 0ustar twernertwernerpackage org.jboss.cache.factories; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.config.Configuration; import org.jboss.cache.interceptors.base.CommandInterceptor; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.fail; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.List; /** * @author Manik Surtani */ @Test(groups = {"functional"}) public class CustomInterceptorChainTest extends InterceptorChainTestBase { private CacheSPI cache; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { Configuration c = new Configuration(); cache = (CacheSPI) new DefaultCacheFactory().createCache(c); cache.create(); } @AfterMethod(alwaysRun = true) public void tearDown() { if (cache != null) { cache.stop(); cache = null; } } public void testChainImmutability() { try { cache.getInterceptorChain().add(new TestInterceptor()); fail("unsupportedException should have been thrown as the chain obtained from the cache should be immutable"); } catch (UnsupportedOperationException uoe) { // this is expected. } } public void testInjectionAtHead() { List interceptors = cache.getInterceptorChain(); assertEquals("Expecting 6 interceptors", 6, interceptors.size()); assertInterceptorLinkage(interceptors); CommandInterceptor x = new TestInterceptor(); cache.addInterceptor(x, 0); interceptors = cache.getInterceptorChain(); assertEquals("Expecting 7 interceptors", 7, interceptors.size()); assertInterceptorLinkage(interceptors); assertEquals(x, interceptors.get(0)); } public void testInjectionAtTail() { List interceptors = cache.getInterceptorChain(); assertEquals("Expecting 6 interceptors", 6, interceptors.size()); assertInterceptorLinkage(interceptors); CommandInterceptor x = new TestInterceptor(); cache.addInterceptor(x, 6); interceptors = cache.getInterceptorChain(); assertEquals("Expecting 7 interceptors", 7, interceptors.size()); assertInterceptorLinkage(interceptors); assertEquals(x, interceptors.get(6)); } public void testInjectionInMiddle() { List interceptors = cache.getInterceptorChain(); assertEquals("Expecting 6 interceptors", 6, interceptors.size()); assertInterceptorLinkage(interceptors); CommandInterceptor x = new TestInterceptor(); cache.addInterceptor(x, 3); interceptors = cache.getInterceptorChain(); assertEquals("Expecting 7 interceptors", 7, interceptors.size()); assertInterceptorLinkage(interceptors); assertEquals(x, interceptors.get(3)); } public void testInjectionBeyondTail() { List interceptors = cache.getInterceptorChain(); assertEquals("Expecting 6 interceptors", 6, interceptors.size()); assertInterceptorLinkage(interceptors); CommandInterceptor x = new TestInterceptor(); try { cache.addInterceptor(x, 9); fail("Should throw an exception"); } catch (IllegalArgumentException e) { // expected } } public void testRemoveAtHead() { List interceptors = cache.getInterceptorChain(); CommandInterceptor afterHead = interceptors.get(1); assertEquals("Expecting 6 interceptors", 6, interceptors.size()); assertInterceptorLinkage(interceptors); cache.removeInterceptor(0); interceptors = cache.getInterceptorChain(); assertEquals("Expecting 5 interceptors", 5, interceptors.size()); assertInterceptorLinkage(interceptors); assertEquals(afterHead, interceptors.get(0)); } public void testRemoveAtTail() { List interceptors = cache.getInterceptorChain(); CommandInterceptor beforeTail = interceptors.get(4); assertEquals("Expecting 6 interceptors", 6, interceptors.size()); assertInterceptorLinkage(interceptors); cache.removeInterceptor(5); interceptors = cache.getInterceptorChain(); System.out.println(interceptors); assertEquals("Expecting 5 interceptors", 5, interceptors.size()); assertInterceptorLinkage(interceptors); assertEquals(beforeTail, interceptors.get(4)); } public void testRemoveAtMiddle() { List interceptors = cache.getInterceptorChain(); assertEquals("Expecting 6 interceptors", 6, interceptors.size()); assertInterceptorLinkage(interceptors); cache.removeInterceptor(3); interceptors = cache.getInterceptorChain(); assertEquals("Expecting 5 interceptors", 5, interceptors.size()); assertInterceptorLinkage(interceptors); } public void testRemoveBeyondTail() { List interceptors = cache.getInterceptorChain(); assertEquals("Expecting 6 interceptors", 6, interceptors.size()); assertInterceptorLinkage(interceptors); try { cache.removeInterceptor(9); fail("Should throw an exception"); } catch (IllegalArgumentException e) { // expected } } public static class TestInterceptor extends CommandInterceptor { } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/CallbackTest.java0000644000175000017500000001356111024235313025633 0ustar twernertwernerpackage org.jboss.cache; import org.jboss.cache.config.Configuration; import org.jboss.cache.lock.IsolationLevel; import org.jboss.cache.notifications.annotation.CacheListener; import org.jboss.cache.notifications.annotation.NodeCreated; import org.jboss.cache.notifications.event.Event; import org.jboss.cache.util.CachePrinter; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.Test; import javax.transaction.TransactionManager; /** * Tests whether modifications within callbacks (TreeCacheListener) are handled correctly * * @author Bela Ban * @version $Id: CallbackTest.java 5979 2008-06-12 15:03:39Z manik.surtani@jboss.com $ */ @Test(groups = "functional") public class CallbackTest { CacheSPI cache = null, cache2; final Fqn FQN_A = Fqn.fromString("/a"); final Fqn FQN_B = Fqn.fromString("/b"); static final String KEY = "key"; static final String VALUE = "value"; @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { if (cache != null) { cache.stop(); cache.destroy(); cache = null; } } public void testLocalPutCallbackWithoutTransaction() throws Exception { cache = createCache(Configuration.CacheMode.LOCAL, IsolationLevel.SERIALIZABLE); cache.addCacheListener(new PutListener(cache)); cache.put(FQN_A, null); assertTrue(cache.exists(FQN_A)); assertTrue(cache.exists(FQN_B));//created by callback System.out.println("cache locks:\n" + CachePrinter.printCacheLockingInfo(cache)); assertEquals(0, cache.getNumberOfLocksHeld()); } public void testLocalGetCallbackSameFqnWithoutTransaction() throws Exception { cache = createCache(Configuration.CacheMode.LOCAL, IsolationLevel.SERIALIZABLE); cache.getNotifier().addCacheListener(new GetListener(cache, FQN_A)); cache.put(FQN_A, null); assertTrue(cache.exists(FQN_A)); System.out.println("cache locks:\n" + CachePrinter.printCacheLockingInfo(cache)); assertEquals(0, cache.getNumberOfLocksHeld()); } public void testLocalGetCallbackDifferentFqnWithoutTransaction() throws Exception { cache = createCache(Configuration.CacheMode.LOCAL, IsolationLevel.SERIALIZABLE); cache.put(FQN_B, null); cache.getNotifier().addCacheListener(new GetListener(cache, FQN_B)); cache.put("/a", null); assertTrue(cache.exists(FQN_A)); assertTrue(cache.exists(FQN_B)); System.out.println("cache locks:\n" + CachePrinter.printCacheLockingInfo(cache)); assertEquals(0, cache.getNumberOfLocksHeld()); } public void testLocalCallbackWithTransaction() throws Exception { cache = createCache(Configuration.CacheMode.LOCAL, IsolationLevel.SERIALIZABLE); cache.getNotifier().addCacheListener(new PutListener(cache)); TransactionManager tm = startTransaction(); cache.put(FQN_A, null); tm.commit(); assertTrue(cache.exists(FQN_A)); assertEquals(0, cache.getNumberOfLocksHeld()); } public void testLocalCallbackWithException() throws Exception { cache = createCache(Configuration.CacheMode.LOCAL, IsolationLevel.SERIALIZABLE); cache.getNotifier().addCacheListener(new ExceptionListener()); TransactionManager tm = startTransaction(); try { cache.put(FQN_A, null); tm.rollback(); } catch (RuntimeException ex) { tm.rollback(); } assertFalse(cache.exists(FQN_A)); assertEquals(0, cache.getNumberOfLocksHeld()); } private CacheSPI createCache(Configuration.CacheMode mode, IsolationLevel level) { Configuration c = new Configuration(); c.setCacheMode(mode); c.setIsolationLevel(level); c.setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); return (CacheSPI) new DefaultCacheFactory().createCache(c); } private TransactionManager startTransaction() { TransactionManager mgr = cache.getConfiguration().getRuntimeConfig().getTransactionManager(); try { mgr.begin(); return mgr; } catch (Throwable t) { return null; } } @CacheListener public class ExceptionListener { @NodeCreated public void nodeCreated(Event e) { if (e.isPre()) throw new RuntimeException("this will cause the TX to rollback"); } } @CacheListener public class GetListener { CacheSPI c; Fqn my_fqn; public GetListener(CacheSPI c, Fqn my_fqn) { this.c = c; this.my_fqn = my_fqn; } @NodeCreated public void nodeCreated(Event e) { if (!e.isPre()) { try { Node n = c.getNode(this.my_fqn); assertNotNull(n); } catch (CacheException ex) { fail("listener was unable to do a get(" + my_fqn + ") during callback: " + ex); } } } } @CacheListener public class PutListener { CacheSPI c; public PutListener(CacheSPI c) { this.c = c; } @NodeCreated public void nodeCreated(Event e) { if (!e.isPre()) { try { if (!c.exists(FQN_B)) { System.out.println("PutListener: creating node " + FQN_B); c.put(FQN_B, KEY, VALUE); System.out.println("PutListener: created node " + FQN_B); } } catch (CacheException ex) { fail("listener was unable to update cache during callback: " + ex); } } } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/statetransfer/0000755000175000017500000000000011376173745025337 5ustar twernertwerner././@LongLink0000000000000000000000000000014700000000000011567 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/statetransfer/StateTransferConcurrencyTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/statetransfer/StateTransferConcurrencyTest.ja0000644000175000017500000006315511017455042033506 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.statetransfer; import org.jboss.cache.*; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.factories.UnitTestCacheConfigurationFactory; import org.jboss.cache.marshall.InactiveRegionException; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.util.internals.EvictionController; import org.jboss.cache.util.internals.ReplicationQueueNotifier; import static org.testng.AssertJUnit.*; import org.testng.annotations.Test; import java.util.Random; import java.util.Set; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; /** * Abstract superclass of "StateTransferVersion"-specific tests * of CacheSPI's state transfer capability. *

* * @author Brian Stansberry */ @Test(groups = "functional") public class StateTransferConcurrencyTest extends StateTransferTestBase { protected String getReplicationVersion() { return "2.2.0.GA"; } /** * Tests concurrent activation of the same subtree by multiple nodes in a * REPL_SYNC environment. The idea is to see what would happen with a * farmed deployment. See concurrentActivationTest for details. * * @throws Exception */ public void testConcurrentActivationSync() throws Exception { concurrentActivationTest(true); } /** * Tests concurrent activation of the same subtree by multiple nodes in a * REPL_ASYNC environment. The idea is to see what would happen with a * farmed deployment. See concurrentActivationTest for details. * * @throws Exception */ public void testConcurrentActivationAsync() throws Exception { concurrentActivationTest(false); } /** * Starts 5 caches and then concurrently activates the same region under * all 5, causing each to attempt a partial state transfer from the others. * As soon as each cache has activated its region, it does a put to a node * in the region, thus complicating the lives of the other caches trying * to get partial state. *

* Failure condition is if any node sees an exception or if the final state * of all caches is not consistent. * * @param sync use REPL_SYNC or REPL_ASYNC * @throws Exception */ private void concurrentActivationTest(boolean sync) { String[] names = {"A", "B", "C", "D", "E"}; int count = names.length; CacheActivator[] activators = new CacheActivator[count]; try { // Create a semaphore and take all its tickets Semaphore semaphore = new Semaphore(count); semaphore.acquire(count); // Create activation threads that will block on the semaphore CacheSPI[] caches = new CacheSPI[count]; for (int i = 0; i < count; i++) { activators[i] = new CacheActivator(semaphore, names[i], sync, caches); caches[i] = activators[i].getCacheSPI(); activators[i].start(); } // Make sure everyone is in sync TestingUtil.blockUntilViewsReceived(caches, 60000); // Release the semaphore to allow the threads to start work semaphore.release(count); // Sleep to ensure the threads get all the semaphore tickets TestingUtil.sleepThread((long) 1000); // Reacquire the semaphore tickets; when we have them all // we know the threads are done for (int i = 0; i < count; i++) { boolean acquired = semaphore.tryAcquire(60, TimeUnit.SECONDS); if (!acquired) { fail("failed to acquire semaphore " + i); } } // allow any async calls to clear if (!sync) { waitTillAllReplicationsFinish(count, caches); } // Ensure the caches held by the activators see all the values for (int i = 0; i < count; i++) { Exception aException = activators[i].getException(); boolean gotUnexpectedException = aException != null && !(aException instanceof InactiveRegionException || aException.getCause() instanceof InactiveRegionException); if (gotUnexpectedException) { fail("Activator " + names[i] + " caught an exception " + aException); } for (int j = 0; j < count; j++) { Fqn fqn = Fqn.fromRelativeElements(A_B, names[j]); assertEquals("Incorrect value for " + fqn + " on activator " + names[i], "VALUE", activators[i].getCacheValue(fqn)); // System.out.println(names[i] + ":" + fqn + " = " + activators[i].getCacheValue(fqn)); } } } catch (Exception ex) { ex.printStackTrace(); fail(ex.getLocalizedMessage()); } finally { for (int i = 0; i < count; i++) { activators[i].cleanup(); } } } private void waitTillAllReplicationsFinish(int count, CacheSPI[] caches) throws Exception { for (int i = 0; i < count; i++) { new ReplicationQueueNotifier(caches[i]).waitUntillAllReplicated(2000); } } /** * Starts two caches where each cache has N regions. We put some data in each of the regions. * We run two threads where each thread creates a cache then goes into a loop where it * activates the N regions, with a 1 sec pause between activations. *

* Threads are started with 10 sec difference. *

* This test simulates a 10 sec staggered start of 2 servers in a cluster, with each server * then deploying webapps. *

*

*

* Failure condition is if any node sees an exception or if the final state * of all caches is not consistent. * * @param sync use REPL_SYNC or REPL_ASYNC * @throws Exception */ private void concurrentActivationTest2(boolean sync) { String[] names = {"A", "B"}; int count = names.length; int regionsToActivate = 15; int sleepTimeBetweenNodeStarts = 10000; StaggeredWebDeployerActivator[] activators = new StaggeredWebDeployerActivator[count]; try { // Create a semaphore and take all its tickets Semaphore semaphore = new Semaphore(count); semaphore.acquire(count); // Create activation threads that will block on the semaphore CacheSPI[] caches = new CacheSPI[count]; for (int i = 0; i < count; i++) { activators[i] = new StaggeredWebDeployerActivator(semaphore, names[i], sync, regionsToActivate); caches[i] = activators[i].getCacheSPI(); // Release the semaphore to allow the thread to start working semaphore.release(1); activators[i].start(); TestingUtil.sleepThread(sleepTimeBetweenNodeStarts); } // Make sure everyone is in sync TestingUtil.blockUntilViewsReceived(caches, 60000); // Sleep to ensure the threads get all the semaphore tickets TestingUtil.sleepThread(1000); // Reacquire the semaphore tickets; when we have them all // we know the threads are done for (int i = 0; i < count; i++) { boolean acquired = semaphore.tryAcquire(60, TimeUnit.SECONDS); if (!acquired) { fail("failed to acquire semaphore " + i); } } // Sleep to allow any async calls to clear if (!sync) { waitTillAllReplicationsFinish(count, caches); } // Ensure the caches held by the activators see all the values for (int i = 0; i < count; i++) { Exception aException = activators[i].getException(); boolean gotUnexpectedException = aException != null && !(aException instanceof InactiveRegionException || aException.getCause() instanceof InactiveRegionException); if (gotUnexpectedException) { fail("Activator " + names[i] + " caught an exception " + aException); } for (int j = 0; j < regionsToActivate; j++) { Fqn fqn = Fqn.fromString("/a/" + i + "/" + names[i]); assertEquals("Incorrect value for " + fqn + " on activator " + names[i], "VALUE", activators[i].getCacheValue(fqn)); } } } catch (Exception ex) { fail(ex.getLocalizedMessage()); } finally { for (int i = 0; i < count; i++) { activators[i].cleanup(); } } } /** * Starts two caches where each cache has N regions. We put some data in each of the regions. * We run two threads where each thread creates a cache then goes into a loop where it * activates the N regions, with a 1 sec pause between activations. *

* Threads are started with 10 sec difference. *

* This test simulates a 10 sec staggered start of 2 servers in a cluster, with each server * then deploying webapps. *

*

*

* Failure condition is if any node sees an exception or if the final state * of all caches is not consistent. */ public void testConcurrentStartupActivationAsync() throws Exception { concurrentActivationTest2(false); } /** * Starts two caches where each cache has N regions. We put some data in each of the regions. * We run two threads where each thread creates a cache then goes into a loop where it * activates the N regions, with a 1 sec pause between activations. *

* Threads are started with 10 sec difference. *

* This test simulates a 10 sec staggered start of 2 servers in a cluster, with each server * then deploying webapps. *

*

*

* Failure condition is if any node sees an exception or if the final state * of all caches is not consistent. */ public void testConcurrentStartupActivationSync() throws Exception { concurrentActivationTest2(true); } /** * Tests partial state transfer under heavy concurrent load and REPL_SYNC. * See concurrentUseTest for details. * * @throws Exception */ public void testConcurrentUseSync() throws Exception { concurrentUseTest(true); } /** * Tests partial state transfer under heavy concurrent load and REPL_ASYNC. * See concurrentUseTest for details. * * @throws Exception */ public void testConcurrentUseAsync() throws Exception { concurrentUseTest(false); } /** * Initiates 5 caches, 4 with active trees and one with an inactive tree. * Each of the active caches begins rapidly generating puts against nodes * in a subtree for which it is responsible. The 5th cache activates * each subtree, and at the end confirms no node saw any exceptions and * that each node has consistent state. * * @param sync whether to use REPL_SYNC or REPL_ASYNCE * @throws Exception */ private void concurrentUseTest(boolean sync) throws Exception { String[] names = {"B", "C", "D", "E"}; int count = names.length; CacheStressor[] stressors = new CacheStressor[count]; try { // The first cache we create is inactivated. CacheSPI cacheA = createCache("cacheA", sync, true, false); CacheSPI[] caches = new CacheSPI[count + 1]; caches[0] = cacheA; // Create a semaphore and take all its tickets Semaphore semaphore = new Semaphore(count); semaphore.acquire(count); // Create stressor threads that will block on the semaphore for (int i = 0; i < count; i++) { stressors[i] = new CacheStressor(semaphore, names[i], sync); caches[i + 1] = stressors[i].getCacheSPI(); stressors[i].start(); } // Make sure everyone's views are in sync TestingUtil.blockUntilViewsReceived(caches, 60000); // Repeat the basic test four times //for (int x = 0; x < 4; x++) for (int x = 0; x < 1; x++) { // Reset things by inactivating the region // and enabling the stressors for (int i = 0; i < count; i++) { Region r = cacheA.getRegion(Fqn.fromString("/" + names[i]), true); r.registerContextClassLoader(getClass().getClassLoader()); r.deactivate(); System.out.println("Run " + x + "-- /" + names[i] + " deactivated on A"); stressors[i].startPuts(); } // Release the semaphore to allow the threads to start work semaphore.release(count); // Sleep to ensure the threads get all the semaphore tickets // and to ensure puts are actively in progress TestingUtil.sleepThread((long) 1000); // Activate cacheA for (CacheStressor stressor : stressors) { System.out.println("Activating /" + stressor.getName() + " on A"); cacheA.getRegion(Fqn.fromString("/" + stressor.getName()), true).activate(); stressor.stopPuts(); System.out.println("Run " + x + "-- /" + stressor.getName() + " activated on A"); // Reacquire one semaphore ticket boolean acquired = semaphore.tryAcquire(60, TimeUnit.SECONDS); if (!acquired) { fail("failed to acquire semaphore " + stressor.getName()); } // Pause to allow other work to proceed TestingUtil.sleepThread(100); } // Sleep to allow any async calls to clear if (!sync) { waitTillAllReplicationsFinish(count, caches); } // Ensure the stressors saw no exceptions for (int i = 0; i < count; i++) { if (stressors[i].getException() != null && !(stressors[i].getException() instanceof InactiveRegionException)) { fail("Stressor " + names[i] + " caught an exception " + stressors[i].getException()); } } // Compare cache contents for (int i = 0; i < count; i++) { for (int j = 0; j < SUBTREE_SIZE; j++) { Fqn fqn = Fqn.fromString("/" + names[i] + "/" + j); assertEquals("/A/" + j + " matches " + fqn, cacheA.get(fqn, "KEY"), stressors[i].getCacheSPI().get(fqn, "KEY")); } } } for (int i = 0; i < count; i++) { stressors[i].stopThread(); } } finally { for (int i = 0; i < count; i++) { if (stressors[i] != null) { stressors[i].cleanup(); } } } } /** * Test for JBCACHE-913 */ public void testEvictionSeesStateTransfer() throws Exception { Configuration c = UnitTestCacheConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC, true); Cache cache1 = new DefaultCacheFactory().createCache(c, false); cache1.start(); caches.put("evict1", cache1); cache1.put(Fqn.fromString("/a/b/c"), "key", "value"); c = UnitTestCacheConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC, true); Cache cache2 = new DefaultCacheFactory().createCache(c, false); cache2.start(); caches.put("evict2", cache2); Region region = cache2.getRegion(Fqn.ROOT, false); // We expect events for /a, /a/b and /a/b/c int nodeEventQueueSize = region.nodeEventQueueSize(); int i = 0; while (region.nodeEventQueueSize() > 0) { System.out.println(++i + ") Queue contains : " + region.takeLastEventNode()); } assertEquals("Saw the expected number of node events", 3, nodeEventQueueSize); } /** * Further test for JBCACHE-913 */ public void testEvictionAfterStateTransfer() throws Exception { Configuration c = UnitTestCacheConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC, true); Cache cache1 = new DefaultCacheFactory().createCache(c, false); cache1.start(); caches.put("evict1", cache1); for (int i = 0; i < 25000; i++) { cache1.put(Fqn.fromString("/org/jboss/data/" + i), "key", "base" + i); if (i < 5) { cache1.put(Fqn.fromString("/org/jboss/test/data/" + i), "key", "data" + i); if (i == 0) { cache1.getRoot().getChild(Fqn.fromString("/org/jboss/data")).setResident(true); //so that it won't be counted for eviction } } } EvictionController ec1 = new EvictionController(cache1); ec1.startEviction(); assert cache1.getRoot().getChild(Fqn.fromString("/org/jboss/data")).getChildren().size() == 5000; c = UnitTestCacheConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC, true); final Cache cache2 = new DefaultCacheFactory().createCache(c, false); cache2.start(); caches.put("evict2", cache2); Node parent = cache2.getRoot().getChild(Fqn.fromString("/org/jboss/test/data")); parent = cache2.getRoot().getChild(Fqn.fromString("/org/jboss/data")); Set children = parent.getChildren(); //4999 because the root of the region will also be counted, as it is not resident assertTrue("Minimum number of base children transferred", children.size() >= 4999); // Sleep 2.5 secs so the nodes we are about to create in data won't // exceed the 4 sec TTL when eviction thread runs TestingUtil.sleepThread(2500); class Putter extends Thread { Cache cache = null; boolean stopped = false; Exception ex = null; public void run() { int i = 25000; while (!stopped) { try { cache.put(Fqn.fromString("/org/jboss/data/" + i), "key", "base" + i); cache.put(Fqn.fromString("/org/jboss/test/data/" + i), "key", "data" + i); i++; } catch (Exception e) { ex = e; } } } } Putter p1 = new Putter(); p1.cache = cache1; p1.start(); Putter p2 = new Putter(); p2.cache = cache2; p2.start(); Random rnd = new Random(); TestingUtil.sleepThread(rnd.nextInt(200)); int maxCountBase = 0; int maxCountData = 0; boolean sawBaseDecrease = false; boolean sawDataDecrease = false; long start = System.currentTimeMillis(); while ((System.currentTimeMillis() - start) < 10000) { parent = cache2.getRoot().getChild(Fqn.fromString("/org/jboss/test/data")); children = parent.getChildren(); if (children != null) { int dataCount = children.size(); if (dataCount < maxCountData) { System.out.println("data " + dataCount + " < " + maxCountData + " elapsed = " + (System.currentTimeMillis() - start)); sawDataDecrease = true; } else { maxCountData = dataCount; } } parent = cache2.getRoot().getChild(Fqn.fromString("/org/jboss/data")); children = parent.getChildren(); if (children != null) { int baseCount = children.size(); if (baseCount < maxCountBase) { System.out.println("base " + baseCount + " < " + maxCountBase + " elapsed = " + (System.currentTimeMillis() - start)); sawBaseDecrease = true; } else { maxCountBase = baseCount; } } if (sawDataDecrease && sawBaseDecrease) { break; } TestingUtil.sleepThread(50); } p1.stopped = true; p2.stopped = true; p1.join(1000); p2.join(1000); assertTrue("Saw data decrease", sawDataDecrease); assertTrue("Saw base decrease", sawBaseDecrease); assertNull("No exceptions in p1", p1.ex); assertNull("No exceptions in p2", p2.ex); EvictionController ec2 = new EvictionController(cache2); ec2.startEviction(); parent = cache2.getRoot().getChild(Fqn.fromString("/org/jboss/test/data")); children = parent.getChildren(); if (children != null) { System.out.println(children.size()); assertTrue("Excess children evicted", children.size() <= 5); } parent = cache2.getRoot().getChild(Fqn.fromString("/org/jboss/data")); children = parent.getChildren(); if (children != null) { System.out.println(children.size()); assertTrue("Excess children evicted", children.size() <= 25000); } // Sleep more to let the eviction thread run again, // which will evict all data nodes due to their ttl of 4 secs ec2.evictRegionWithTimeToLive("/org/jboss/test/data"); parent = cache2.getRoot().getChild(Fqn.fromString("/org/jboss/test/data")); if (parent != null) { children = parent.getChildren(); if (children != null) { assertEquals("All data children evicted", 0, children.size()); } } } private class CacheActivator extends CacheUser { private CacheSPI[] caches; CacheActivator(Semaphore semaphore, String name, boolean sync, CacheSPI[] caches) throws Exception { super(semaphore, name, sync, false); this.caches = caches; } void useCache() throws Exception { System.out.println("---- Cache" + name + " = " + cache.getLocalAddress() + " being used"); TestingUtil.sleepRandom(5000); createAndActivateRegion(cache, A_B); System.out.println(name + " activated region" + " " + System.currentTimeMillis()); Fqn childFqn = Fqn.fromRelativeElements(A_B, name); cache.put(childFqn, "KEY", "VALUE"); } public Object getCacheValue(Fqn fqn) throws CacheException { return cache.get(fqn, "KEY"); } } private class StaggeredWebDeployerActivator extends CacheUser { int regionCount = 15; StaggeredWebDeployerActivator(Semaphore semaphore, String name, boolean sync, int regionCount) throws Exception { super(semaphore, name, sync, false); this.regionCount = regionCount; } void useCache() throws Exception { for (int i = 0; i < regionCount; i++) { createAndActivateRegion(cache, Fqn.fromString("/a/" + i)); Fqn childFqn = Fqn.fromString("/a/" + i + "/" + name); cache.put(childFqn, "KEY", "VALUE"); TestingUtil.sleepThread(1000); } } public Object getCacheValue(Fqn fqn) throws CacheException { return cache.get(fqn, "KEY"); } } private class CacheStressor extends CacheUser { private Random random = new Random(System.currentTimeMillis()); private boolean putsStopped = false; private boolean stopped = false; CacheStressor(Semaphore semaphore, String name, boolean sync) throws Exception { super(semaphore, name, sync, true); } void useCache() throws Exception { // Do continuous puts into the cache. Use our own nodes, // as we're not testing conflicts between writer nodes, // just whether activation causes problems int factor = 0; int i = 0; Fqn fqn = null; boolean acquired = false; while (!stopped) { if (i > 0) { acquired = semaphore.tryAcquire(60, TimeUnit.SECONDS); if (!acquired) { throw new Exception(name + " cannot acquire semaphore"); } } while (!putsStopped) { factor = random.nextInt(50); fqn = Fqn.fromString("/" + name + "/" + String.valueOf(factor % SUBTREE_SIZE)); Integer value = factor / SUBTREE_SIZE; cache.put(fqn, "KEY", value); TestingUtil.sleepThread((long) factor); i++; } System.out.println(name + ": last put [#" + i + "] -- " + fqn + " = " + (factor / SUBTREE_SIZE)); semaphore.release(); // Go to sleep until directed otherwise while (!stopped && putsStopped) { TestingUtil.sleepThread((long) 100); } } } public void stopPuts() { putsStopped = true; } public void startPuts() { putsStopped = false; } public void stopThread() { stopped = true; if (thread.isAlive()) { thread.interrupt(); } } } }././@LongLink0000000000000000000000000000014500000000000011565 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/statetransfer/StateTransferUnderLoadTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/statetransfer/StateTransferUnderLoadTest.java0000644000175000017500000001174111017455042033412 0ustar twernertwernerpackage org.jboss.cache.statetransfer; import org.jboss.cache.Cache; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.config.Configuration; import org.jboss.cache.transaction.DummyTransactionManager; import org.testng.AssertJUnit; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.naming.Context; import javax.naming.InitialContext; import javax.transaction.SystemException; import javax.transaction.UserTransaction; import java.util.Properties; /** * Tests state transfer while the other node keeps sending transactional, synchronous method calls * * @author Bela Ban * @version $Id: StateTransferUnderLoadTest.java 5906 2008-05-29 07:24:18Z mircea.markus $ */ @Test(groups = {"functional"}, enabled = false, description = "Disabled because this test depends on JBCACHE-315 being resolved.") public class StateTransferUnderLoadTest { Cache cache1, cache2; Properties p = null; String old_factory = null; static final String FACTORY = "org.jboss.cache.transaction.DummyContextFactory"; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { old_factory = System.getProperty(Context.INITIAL_CONTEXT_FACTORY); System.setProperty(Context.INITIAL_CONTEXT_FACTORY, FACTORY); DummyTransactionManager.getInstance(); if (p == null) { p = new Properties(); p.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.cache.transaction.DummyContextFactory"); } } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { if (cache2 != null) { cache2.stop(); cache2 = null; } if (cache1 != null) { cache1.stop(); cache1 = null; } // BW. kind of a hack to destroy jndi binding and thread local tx before next run. DummyTransactionManager.destroy(); if (old_factory != null) { System.setProperty(Context.INITIAL_CONTEXT_FACTORY, old_factory); old_factory = null; } } public void testStateTransferDeadlocksPessimistic() throws Exception { runTest(false); } public void testStateTransferDeadlocksOptimistic() throws Exception { runTest(true); } private void runTest(boolean optimistic) throws Exception { Writer writer; Configuration cfg1, cfg2; cfg1 = new Configuration(); cfg2 = new Configuration(); cfg1.setCacheMode(Configuration.CacheMode.REPL_SYNC); cfg2.setCacheMode(Configuration.CacheMode.REPL_SYNC); if (optimistic) { cfg1.setNodeLockingScheme(Configuration.NodeLockingScheme.OPTIMISTIC); cfg2.setNodeLockingScheme(Configuration.NodeLockingScheme.OPTIMISTIC); } cfg1.setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); cfg2.setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); cache1 = new DefaultCacheFactory().createCache(cfg1, true); cache2 = new DefaultCacheFactory().createCache(cfg2, false); UserTransaction tx1 = (UserTransaction) new InitialContext(p).lookup("UserTransaction"); writer = new Writer(cache1, tx1); try { writer.start(); cache2.create(); for (int i = 0; i < 100; i++) { cache2.start(); // gets state // check if state was retrieved successfully int num_nodes = ((CacheSPI) cache2).getNumberOfNodes(); AssertJUnit.assertTrue(num_nodes >= 1); TestingUtil.sleepThread(100); cache2.stop(); } } finally { writer.stop(); } } static class Writer implements Runnable { Thread thread; Cache cache; boolean running = false; UserTransaction tx; public Writer(Cache cache, UserTransaction tx) { this.cache = cache; this.tx = tx; } public void start() { thread = new Thread(this, "cache writer"); running = true; thread.start(); } public void stop() { running = false; } public void run() { Fqn fqn = Fqn.fromString("/a/b/c"); while (running) { try { tx.begin(); cache.put(fqn, "key", "value"); tx.commit(); } catch (Exception e) { e.printStackTrace(); try { tx.rollback(); } catch (SystemException e1) { } } finally { TestingUtil.sleepRandom(100); } } } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/statetransfer/StateTransfer200Test.java0000644000175000017500000004472111024205072032034 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.statetransfer; import org.jboss.cache.Cache; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.Region; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.buddyreplication.BuddyFqnTransformer; import org.jboss.cache.buddyreplication.BuddyManager; import org.jboss.cache.config.BuddyReplicationConfig; import org.jboss.cache.loader.CacheLoader; import org.jboss.cache.marshall.MarshalledValue; import static org.testng.AssertJUnit.*; import org.testng.annotations.Test; import java.lang.reflect.Method; /** * Tests that state transfer works properly if the version is 2.0.0.GA. * * @author Brian Stansberry * @version $Revision: 5976 $ */ @Test(groups = {"functional"}) public class StateTransfer200Test extends StateTransferTestBase { protected String getReplicationVersion() { return "2.0.0.GA"; } public void testBuddyBackupExclusion() throws Exception { CacheSPI cache1 = createCache("cache1", false, false, false, false, false, true); cache1.getConfiguration().setBuddyReplicationConfig(getBuddyConfig()); cache1.start(); Fqn backup = Fqn.fromRelativeElements(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN, "test"); cache1.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); cache1.put(backup, "name", JOE); cache1.put(A_B, "age", TWENTY); CacheSPI cache2 = createCache("cache2", false, false, false); // Pause to give caches time to see each other TestingUtil.blockUntilViewsReceived(new CacheSPI[]{cache1, cache2}, 60000); assertNull("_buddy_backup_ not transferred", cache2.get(backup, "test")); assertEquals("Correct age for /a/b", TWENTY, cache2.get(A_B, "age")); } public void testBuddyIntegration() throws Exception { CacheSPI cache1 = createCache("cache1", false, false, false, false, false, true); cache1.getConfiguration().setBuddyReplicationConfig(getBuddyConfig()); cache1.start(); // cache 1 won't have a buddy at this stage. // put some state in cache 1 cache1.put(A_B, "name", JOE); cache1.put(A_C, "name", JANE); // now start up cache 2 CacheSPI cache2 = createCache("cache2", false, false, false, false, false, true); cache2.getConfiguration().setBuddyReplicationConfig(getBuddyConfig()); cache2.start(); // Pause to give caches time to see each other TestingUtil.blockUntilViewsReceived(60000, cache1, cache2); BuddyFqnTransformer fqnTransformer = new BuddyFqnTransformer(); // now peek into cache 2 to check that this state has been transferred into the backup subtree Fqn test = fqnTransformer.getBackupFqn(cache1.getLocalAddress(), A_B); assertEquals("/a/b state should have integrated in backup region " + test, JOE, cache2.get(test, "name")); test = fqnTransformer.getBackupFqn(cache1.getLocalAddress(), A_C); assertEquals("/a/c state should have integrated in backup region " + test, JANE, cache2.get(test, "name")); } @SuppressWarnings("null") public void testCacheLoaderFailure() throws Exception { CacheSPI cache1 = createCache("cache1", false, false, CorruptedFileCacheLoader.class.getName(), false, true, true); cache1.put(A_B, "name", JOE); cache1.put(A_B, "age", TWENTY); cache1.put(A_C, "name", BOB); cache1.put(A_C, "age", FORTY); CacheSPI cache2 = null; try { cache2 = createCache("cache2", false, false, true, false, false, true); cache2.create(); cache2.start(); //Vladimir October 5th 2007 //failure of integration of persistent state is not considered to be fatal //to revisit with Manik //fail("Should have caused an exception"); } catch (Exception e) { } //when persistent transfer fails as in this case state recipient cacheloader should be wiped clean assertFalse("/a/b is not in cache loader ", cache2.getCacheLoaderManager().getCacheLoader().exists(A_B)); } public void testLoadEntireStateAfterStart() throws Exception { CacheSPI cache1 = createCache("cache1", false, true, true); createAndActivateRegion(cache1, Fqn.ROOT); cache1.put(A_B, "name", JOE); cache1.put(A_B, "age", TWENTY); cache1.put(A_C, "name", BOB); cache1.put(A_C, "age", FORTY); CacheSPI cache2 = createCache("cache2", false, true, true); // Pause to give caches time to see each other TestingUtil.blockUntilViewsReceived(new CacheSPI[]{cache1, cache2}, 60000); CacheLoader loader = cache2.getCacheLoaderManager().getCacheLoader(); assertNull("/a/b transferred to loader against policy", loader.get(A_B)); assertNull("/a/b name transferred against policy", cache2.get(A_B, "name")); assertNull("/a/b age transferred against policy", cache2.get(A_B, "age")); assertNull("/a/c name transferred against policy", cache2.get(A_C, "name")); assertNull("/a/c age transferred against policy", cache2.get(A_C, "age")); createAndActivateRegion(cache2, Fqn.ROOT); assertEquals("Incorrect name from loader for /a/b", JOE, loader.get(A_B).get("name")); assertEquals("Incorrect age from loader for /a/b", TWENTY, loader.get(A_B).get("age")); assertEquals("Incorrect name from loader for /a/c", BOB, loader.get(A_C).get("name")); assertEquals("Incorrect age from loader for /a/c", FORTY, loader.get(A_C).get("age")); assertEquals("Incorrect name for /a/b", JOE, cache2.get(A_B, "name")); assertEquals("Incorrect age for /a/b", TWENTY, cache2.get(A_B, "age")); assertEquals("Incorrect name for /a/c", BOB, cache2.get(A_C, "name")); assertEquals("Incorrect age for /a/c", FORTY, cache2.get(A_C, "age")); } public void testInitialStateTransfer() throws Exception { CacheSPI cache1 = createCache("cache1", false, false, false); cache1.put(A_B, "name", JOE); cache1.put(A_B, "age", TWENTY); cache1.put(A_C, "name", BOB); cache1.put(A_C, "age", FORTY); CacheSPI cache2 = createCache("cache2", false, false, false); // Pause to give caches time to see each other TestingUtil.blockUntilViewsReceived(new CacheSPI[]{cache1, cache2}, 60000); assertEquals("Incorrect name for /a/b", JOE, cache2.get(A_B, "name")); assertEquals("Incorrect age for /a/b", TWENTY, cache2.get(A_B, "age")); assertEquals("Incorrect name for /a/c", BOB, cache2.get(A_C, "name")); assertEquals("Incorrect age for /a/c", FORTY, cache2.get(A_C, "age")); } public void testInitialStateTferWithLoader() throws Exception { initialStateTferWithLoaderTest(false); } public void testInitialStateTferWithAsyncLoader() throws Exception { initialStateTferWithLoaderTest(true); } protected void initialStateTferWithLoaderTest(boolean asyncLoader) throws Exception { initialStateTferWithLoaderTest("org.jboss.cache.loader.FileCacheLoader", "org.jboss.cache.loader.FileCacheLoader", asyncLoader); } public void testPartialStateTransfer() throws Exception { CacheSPI cache1 = createCache("cache1", false, true, false); createAndActivateRegion(cache1, A); cache1.put(A_B, "name", JOE); cache1.put(A_B, "age", TWENTY); cache1.put(A_C, "name", BOB); cache1.put(A_C, "age", FORTY); CacheSPI cache2 = createCache("cache2", false, true, false); // Pause to give caches time to see each other TestingUtil.blockUntilViewsReceived(new CacheSPI[]{cache1, cache2}, 60000); assertNull("/a/b name transferred against policy", cache2.get(A_B, "name")); assertNull("/a/b age transferred against policy", cache2.get(A_B, "age")); assertNull("/a/c name transferred against policy", cache2.get(A_C, "name")); assertNull("/a/c age transferred against policy", cache2.get(A_C, "age")); createAndActivateRegion(cache2, A_B); assertEquals("Incorrect name for /a/b", JOE, cache2.get(A_B, "name")); assertEquals("Incorrect age for /a/b", TWENTY, cache2.get(A_B, "age")); assertNull("/a/c name transferred against policy", cache2.get(A_C, "name")); assertNull("/a/c age transferred against policy", cache2.get(A_C, "age")); cache1.put(A_D, "name", JANE); assertNull("/a/d name transferred against policy", cache2.get(A_D, "name")); createAndActivateRegion(cache2, A_C); assertEquals("Incorrect name for /a/b", JOE, cache2.get(A_B, "name")); assertEquals("Incorrect age for /a/b", TWENTY, cache2.get(A_B, "age")); assertEquals("Incorrect name for /a/c", BOB, cache2.get(A_C, "name")); assertEquals("Incorrect age for /a/c", FORTY, cache2.get(A_C, "age")); assertNull("/a/d name transferred against policy", cache2.get(A_D, "name")); createAndActivateRegion(cache2, A_D); assertEquals("Incorrect name for /a/b", JOE, cache2.get(A_B, "name")); assertEquals("Incorrect age for /a/b", TWENTY, cache2.get(A_B, "age")); assertEquals("Incorrect name for /a/c", BOB, cache2.get(A_C, "name")); assertEquals("Incorrect age for /a/c", FORTY, cache2.get(A_C, "age")); assertEquals("Incorrect name for /a/d", JANE, cache2.get(A_D, "name")); cache1.getRegion(A, false).deactivate(); createAndActivateRegion(cache1, A_B); createAndActivateRegion(cache1, A_C); createAndActivateRegion(cache1, A_D); assertEquals("Incorrect name for /a/b", JOE, cache1.get(A_B, "name")); assertEquals("Incorrect age for /a/b", TWENTY, cache1.get(A_B, "age")); assertEquals("Incorrect name for /a/c", BOB, cache1.get(A_C, "name")); assertEquals("Incorrect age for /a/c", FORTY, cache1.get(A_C, "age")); assertEquals("Incorrect name for /a/d", JANE, cache1.get(A_D, "name")); } public void testPartialStateTferWithLoader() throws Exception { CacheSPI cache1 = createCache("cache1", false, true, true); createAndActivateRegion(cache1, A); cache1.put(A_B, "name", JOE); cache1.put(A_B, "age", TWENTY); cache1.put(A_C, "name", BOB); cache1.put(A_C, "age", FORTY); CacheSPI cache2 = createCache("cache2", false, true, true); // Pause to give caches time to see each other TestingUtil.blockUntilViewsReceived(new CacheSPI[]{cache1, cache2}, 60000); CacheLoader loader = cache2.getCacheLoaderManager().getCacheLoader(); assertNull("/a/b transferred to loader against policy", loader.get(A_B)); assertNull("/a/b name transferred against policy", cache2.get(A_B, "name")); assertNull("/a/b age transferred against policy", cache2.get(A_B, "age")); assertNull("/a/c name transferred against policy", cache2.get(A_C, "name")); assertNull("/a/c age transferred against policy", cache2.get(A_C, "age")); createAndActivateRegion(cache2, A_B); assertEquals("Incorrect name from loader for /a/b", JOE, loader.get(A_B).get("name")); assertEquals("Incorrect age from loader for /a/b", TWENTY, loader.get(A_B).get("age")); assertNull("/a/c transferred to loader against policy", loader.get(A_C)); assertEquals("Incorrect name for /a/b", JOE, cache2.get(A_B, "name")); assertEquals("Incorrect age for /a/b", TWENTY, cache2.get(A_B, "age")); assertNull("/a/c name transferred against policy", cache2.get(A_C, "name")); assertNull("/a/c age transferred against policy", cache2.get(A_C, "age")); cache1.put(A_D, "name", JANE); assertNull("/a/d name transferred against policy", cache2.get(A_D, "name")); createAndActivateRegion(cache2, A_C); assertEquals("Incorrect name from loader for /a/b", JOE, loader.get(A_B).get("name")); assertEquals("Incorrect age from loader for /a/b", TWENTY, loader.get(A_B).get("age")); assertEquals("Incorrect name from loader for /a/c", BOB, loader.get(A_C).get("name")); assertEquals("Incorrect age from loader for /a/c", FORTY, loader.get(A_C).get("age")); assertEquals("Incorrect name for /a/b", JOE, cache2.get(A_B, "name")); assertEquals("Incorrect age for /a/b", TWENTY, cache2.get(A_B, "age")); assertEquals("Incorrect name for /a/c", BOB, cache2.get(A_C, "name")); assertEquals("Incorrect age for /a/c", FORTY, cache2.get(A_C, "age")); assertNull("/a/d name transferred against policy", cache2.get(A_D, "name")); createAndActivateRegion(cache2, A_D); assertEquals("Incorrect name from loader for /a/b", JOE, loader.get(A_B).get("name")); assertEquals("Incorrect age from loader for /a/b", TWENTY, loader.get(A_B).get("age")); assertEquals("Incorrect name from loader for /a/c", BOB, loader.get(A_C).get("name")); assertEquals("Incorrect age from loader for /a/c", FORTY, loader.get(A_C).get("age")); assertEquals("Incorrect name from loader for /a/d", JANE, loader.get(A_D).get("name")); assertEquals("Incorrect name for /a/b", JOE, cache2.get(A_B, "name")); assertEquals("Incorrect age for /a/b", TWENTY, cache2.get(A_B, "age")); assertEquals("Incorrect name for /a/c", BOB, cache2.get(A_C, "name")); assertEquals("Incorrect age for /a/c", FORTY, cache2.get(A_C, "age")); assertEquals("Incorrect name for /a/d", JANE, cache2.get(A_D, "name")); cache1.getRegion(A, false).deactivate(); createAndActivateRegion(cache1, A_B); createAndActivateRegion(cache1, A_C); createAndActivateRegion(cache1, A_D); loader = cache1.getCacheLoaderManager().getCacheLoader(); assertEquals("Incorrect name from loader for /a/b", JOE, loader.get(A_B).get("name")); assertEquals("Incorrect age from loader for /a/b", TWENTY, loader.get(A_B).get("age")); assertEquals("Incorrect name from loader for /a/c", BOB, loader.get(A_C).get("name")); assertEquals("Incorrect age from loader for /a/c", FORTY, loader.get(A_C).get("age")); assertEquals("Incorrect name from loader for /a/d", JANE, loader.get(A_D).get("name")); assertEquals("Incorrect name for /a/b", JOE, cache1.get(A_B, "name")); assertEquals("Incorrect age for /a/b", TWENTY, cache1.get(A_B, "age")); assertEquals("Incorrect name for /a/c", BOB, cache1.get(A_C, "name")); assertEquals("Incorrect age for /a/c", FORTY, cache1.get(A_C, "age")); assertEquals("Incorrect name for /a/d", JANE, cache1.get(A_D, "name")); } public void testPartialStateTferWithClassLoader() throws Exception { // FIXME: This test is meaningless because MarshalledValueInputStream // will find the classes w/ their own loader if TCL can't. Need // to find a way to test! // But, at least it tests JBCACHE-305 by registering a classloader // both before and after start() // Set the TCL to a classloader that can't see Person/Address Thread.currentThread().setContextClassLoader(getNotFoundClassLoader()); CacheSPI cache1 = createCache("cache1", false, // async true, // use marshaller true, // use cacheloader false, false, true);// don't start ClassLoader cl1 = getClassLoader(); cache1.getRegion(A, true).registerContextClassLoader(cl1); startCache(cache1); cache1.getRegion(A, true).activate(); Object ben = createBen(cl1); cache1.put(A_B, "person", ben); // For cache 2 we won't register loader until later CacheSPI cache2 = createCache("cache2", false, // async true, // use marshalling true, // use cacheloader false, true, true);// start // Pause to give caches time to see each other TestingUtil.blockUntilViewsReceived(new CacheSPI[]{cache1, cache2}, 60000); CacheLoader loader = cache2.getCacheLoaderManager().getCacheLoader(); assertNull("/a/b not transferred to loader", loader.get(A_B)); assertNull("/a/b not transferred to cache", cache2.get(A_B, "person")); ClassLoader cl2 = getClassLoader(); // cache2.registerClassLoader(A, cl2); Region r = cache2.getRegion(A, true); r.registerContextClassLoader(cl2); r.activate(); assertEquals("Correct state from loader for /a/b", ben.toString(), getUnmarshalled(loader.get(A_B).get("person")).toString()); assertEquals("Correct state from cache for /a/b", ben.toString(), getUnmarshalled(cache2.get(A_B, "person")).toString()); } private Object getUnmarshalled(Object o) throws Exception { return o instanceof MarshalledValue ? ((MarshalledValue) o).get() : o; } public void testStalePersistentState() throws Exception { CacheSPI c1 = createCache("1", true, false, true, false); c1.put(A, "K", "V"); assert c1.get(A, "K").equals("V"); CacheLoader l1 = c1.getCacheLoaderManager().getCacheLoader(); assert l1 != null; assert l1.exists(A); assert l1.get(A).get("K").equals("V"); // test persistence c1.stop(); assert l1.exists(A); assert l1.get(A).get("K").equals("V"); Cache c2 = createCache("2", true, false, true, false); c2.put(B, "K", "V"); c1.start(); assert c1.get(B, "K").equals("V"); assert c1.get(A, "K") == null; assert !l1.exists(A); } private Object createBen(ClassLoader loader) throws Exception { Class addrClazz = loader.loadClass(ADDRESS_CLASSNAME); Method setCity = addrClazz.getMethod("setCity", String.class); Method setStreet = addrClazz.getMethod("setStreet", String.class); Method setZip = addrClazz.getMethod("setZip", int.class); Object addr = addrClazz.newInstance(); setCity.invoke(addr, "San Jose"); setStreet.invoke(addr, "1007 Home"); setZip.invoke(addr, 90210); Class benClazz = loader.loadClass(PERSON_CLASSNAME); Method setName = benClazz.getMethod("setName", String.class); Method setAddress = benClazz.getMethod("setAddress", addrClazz); Object ben = benClazz.newInstance(); setName.invoke(ben, "Ben"); setAddress.invoke(ben, addr); return ben; } private BuddyReplicationConfig getBuddyConfig() throws Exception { BuddyReplicationConfig brc = new BuddyReplicationConfig(); brc.setEnabled(true); brc.setBuddyPoolName("TEST"); return brc; } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/statetransfer/StateTransferTestBase.java0000644000175000017500000004051311031017377032407 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * Copyright 2006, JBoss Inc., and individual contributors as indicated * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.cache.statetransfer; import org.jboss.cache.Cache; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.Region; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.factories.UnitTestCacheConfigurationFactory; import org.jboss.cache.loader.AbstractCacheLoaderTestBase; import org.jboss.cache.loader.CacheLoader; import org.jboss.cache.marshall.SelectedClassnameClassLoader; import org.jboss.cache.util.TestingUtil; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertFalse; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.io.File; import java.util.HashMap; import java.util.Map; import java.util.Properties; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; /** * Abstract superclass of the StateTransfer tests. * * @author Brian Stansberry * @version $Revision$ */ @Test(groups = {"functional"}) public abstract class StateTransferTestBase extends AbstractCacheLoaderTestBase { protected static final int SUBTREE_SIZE = 10; public static final Fqn A = Fqn.fromString("/a"); public static final Fqn B = Fqn.fromString("/b"); public static final Fqn C = Fqn.fromString("/c"); protected static final String ADDRESS_CLASSNAME = "org.jboss.cache.marshall.data.Address"; protected static final String PERSON_CLASSNAME = "org.jboss.cache.marshall.data.Person"; public static final Fqn A_B = Fqn.fromString("/a/b"); public static final Fqn A_C = Fqn.fromString("/a/c"); public static final Fqn A_D = Fqn.fromString("/a/d"); public static final String JOE = "JOE"; public static final String BOB = "BOB"; public static final String JANE = "JANE"; public static final Integer TWENTY = 20; public static final Integer FORTY = 40; protected Map caches; private ClassLoader orig_TCL; protected abstract String getReplicationVersion(); protected CacheSPI createCache(String cacheID, boolean sync, boolean useMarshalling, boolean useCacheLoader) throws Exception { return createCache(cacheID, sync, useMarshalling, useCacheLoader, false, true, true); } protected CacheSPI createCache(String cacheID, boolean sync, boolean useMarshalling, boolean useCacheLoader, boolean fetchPersistentState) throws Exception { return createCache(cacheID, sync, useMarshalling, useCacheLoader, false, true, fetchPersistentState); } protected CacheSPI createCache(String cacheID, boolean sync, boolean useMarshalling, boolean useCacheLoader, boolean cacheLoaderAsync, boolean startCache, boolean fetchPersistentState) throws Exception { if (useCacheLoader) { return createCache(cacheID, sync, useMarshalling, "org.jboss.cache.loader.FileCacheLoader", cacheLoaderAsync, startCache, fetchPersistentState); } else { return createCache(cacheID, sync, useMarshalling, null, cacheLoaderAsync, startCache, fetchPersistentState); } } protected CacheSPI createCache(String cacheID, boolean sync, boolean useMarshalling, String cacheLoaderClass, boolean cacheLoaderAsync, boolean startCache, boolean fetchPersistentState) throws Exception { if (caches.get(cacheID) != null) { throw new IllegalStateException(cacheID + " already created"); } CacheMode mode = sync ? CacheMode.REPL_SYNC : CacheMode.REPL_ASYNC; Configuration c = UnitTestCacheConfigurationFactory.createConfiguration(mode); if (sync) { c.setSyncRollbackPhase(true); c.setSyncCommitPhase(true); } c.setClusterName("VersionedTestBase"); c.setReplVersionString(getReplicationVersion()); // Use a long timeout to facilitate setting debugger breakpoints c.setStateRetrievalTimeout(60000); if (useMarshalling) { c.setUseRegionBasedMarshalling(true); c.setInactiveOnStartup(true); } if (cacheLoaderClass != null && cacheLoaderClass.length() > 0) { configureCacheLoader(c, cacheLoaderClass, cacheID, cacheLoaderAsync, fetchPersistentState); } // tree.setConfiguration(c); //c.setLockAcquisitionTimeout(60000); //c.setSyncReplTimeout(60000); CacheSPI tree = (CacheSPI) new DefaultCacheFactory().createCache(c, false); //c.setLockAcquisitionTimeout(60000); // a whole minute?!?? Lots of state to tfr? //c.setSyncReplTimeout(60000); configureMultiplexer(tree); // Put the cache in the map before starting, so if it fails in // start it can still be destroyed later caches.put(cacheID, tree); if (startCache) { tree.create(); tree.start(); } return tree; } protected void createAndActivateRegion(CacheSPI c, Fqn f) { Region r = c.getRegion(f, true); r.registerContextClassLoader(getClass().getClassLoader()); r.activate(); } /** * Provides a hook for multiplexer integration. This default implementation * is a no-op; subclasses that test mux integration would override * to integrate the given cache with a multiplexer. *

* param cache a cache that has been configured but not yet created. */ protected void configureMultiplexer(Cache cache) throws Exception { // default does nothing } /** * Provides a hook to check that the cache's channel came from the * multiplexer, or not, as expected. This default impl asserts that * the channel did not come from the multiplexer. * * @param cache a cache that has already been started */ protected void validateMultiplexer(Cache cache) { assertFalse("Cache is not using multiplexer", cache.getConfiguration().isUsingMultiplexer()); } protected void startCache(Cache cache) throws Exception { cache.create(); cache.start(); validateMultiplexer(cache); } protected void configureCacheLoader(Configuration c, String cacheID, boolean async) throws Exception { configureCacheLoader(c, "org.jboss.cache.loader.FileCacheLoader", cacheID, async, true); } protected void configureCacheLoader(Configuration c, String cacheloaderClass, String cacheID, boolean async, boolean fetchPersistentState) throws Exception { if (cacheloaderClass != null) { if (cacheloaderClass.equals("org.jboss.cache.loader.JDBCCacheLoader")) { Properties prop = new Properties(); try { prop.load(this.getClass().getClassLoader().getResourceAsStream("cache-jdbc.properties")); } catch (Exception e) { System.out.println("Error loading jdbc properties "); } String props = "cache.jdbc.driver =" + prop.getProperty("cache.jdbc.driver") + "\n" + "cache.jdbc.url=" + prop.getProperty("cache.jdbc.url") + "\n" + "cache.jdbc.user=" + prop.getProperty("cache.jdbc.user") + "\n" + "cache.jdbc.password=" + prop.getProperty("cache.jdbc.password") + "\n" + "cache.jdbc.node.type=" + prop.getProperty("cache.jdbc.node.type") + "\n" + "cache.jdbc.sql-concat=" + prop.getProperty("cache.jdbc.sql-concat"); c.setCacheLoaderConfig(getSingleCacheLoaderConfig("", "org.jboss.cache.loader.JDBCCacheLoader", props, false, true, false)); } else { String tmp_location = getTempLocation(cacheID); // Do cleanup in case it failed before File file = new File(tmp_location); cleanFile(file); file.mkdir(); tmp_location = escapeWindowsPath(tmp_location); String props = "location = " + tmp_location + "\n"; c.setCacheLoaderConfig(getSingleCacheLoaderConfig("", cacheloaderClass, props, async, fetchPersistentState, false)); } } } protected void initialStateTferWithLoaderTest(String cacheLoaderClass1, String cacheLoaderClass2, boolean asyncLoader) throws Exception { CacheSPI cache1 = createCache("cache1", false, false, cacheLoaderClass1, false, true, true); cache1.put(A_B, "name", JOE); cache1.put(A_B, "age", TWENTY); cache1.put(A_C, "name", BOB); cache1.put(A_C, "age", FORTY); CacheSPI cache2 = createCache("cache2", false, false, cacheLoaderClass2, asyncLoader, false, true); cache2.start(); // Pause to give caches time to see each other TestingUtil.blockUntilViewsReceived(new CacheSPI[]{cache1, cache2}, 60000); if (asyncLoader) { TestingUtil.sleepThread((long) 100); } CacheLoader loader = cache2.getCacheLoaderManager().getCacheLoader(); assertEquals("Incorrect loader name for /a/b", JOE, loader.get(A_B).get("name")); assertEquals("Incorrect loader age for /a/b", TWENTY, loader.get(A_B).get("age")); assertEquals("Incorrect loader name for /a/c", BOB, loader.get(A_C).get("name")); assertEquals("Incorrect loader age for /a/c", FORTY, loader.get(A_C).get("age")); assertEquals("Incorrect name for /a/b", JOE, cache2.get(A_B, "name")); assertEquals("Incorrect age for /a/b", TWENTY, cache2.get(A_B, "age")); assertEquals("Incorrect name for /a/c", BOB, cache2.get(A_C, "name")); assertEquals("Incorrect age for /a/c", FORTY, cache2.get(A_C, "age")); } protected String getTempLocation(String cacheID) { String tmp_location = System.getProperty("java.io.tmpdir", "c:\\tmp"); File file = new File(tmp_location); file = new File(file, cacheID); return file.getAbsolutePath(); } protected String escapeWindowsPath(String path) { if ('/' == File.separatorChar) { return path; } char[] chars = path.toCharArray(); StringBuilder sb = new StringBuilder(); for (char aChar : chars) { if (aChar == '\\') { sb.append('\\'); } sb.append(aChar); } return sb.toString(); } @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { caches = new HashMap(); // Save the TCL in case a test changes it orig_TCL = Thread.currentThread().getContextClassLoader(); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { System.out.println("*** in tearDown()"); // Restore the TCL in case a test changed it Thread.currentThread().setContextClassLoader(orig_TCL); for (String cacheID : caches.keySet()) { try { stopCache(caches.get(cacheID)); TestingUtil.sleepThread(1500); File file = new File(getTempLocation(cacheID)); cleanFile(file); } catch (Exception e) { // errors in teardown should not fail test } } } protected void stopCache(Cache cache) { if (cache != null) { try { cache.stop(); cache.destroy(); } catch (Exception e) { System.out.println("Exception stopping cache " + e.getMessage()); e.printStackTrace(System.out); } } } protected void cleanFile(File file) { File[] children = file.listFiles(); if (children != null) { for (File child : children) { cleanFile(child); } } if (file.exists()) { file.delete(); } if (file.exists()) { file.deleteOnExit(); } } protected ClassLoader getClassLoader() throws Exception { String[] includesClasses = {"org.jboss.cache.marshall.Person", "org.jboss.cache.marshall.Address"}; String[] excludesClasses = {}; ClassLoader cl = Thread.currentThread().getContextClassLoader(); return new SelectedClassnameClassLoader(includesClasses, excludesClasses, cl); } protected ClassLoader getNotFoundClassLoader() throws Exception { String[] notFoundClasses = {"org.jboss.cache.marshall.Person", "org.jboss.cache.marshall.Address"}; ClassLoader cl = Thread.currentThread().getContextClassLoader(); return new SelectedClassnameClassLoader(null, null, notFoundClasses, cl); } protected abstract class CacheUser implements Runnable { protected Semaphore semaphore; protected CacheSPI cache; protected String name; protected Exception exception; protected Thread thread; CacheUser() { } CacheUser(Semaphore semaphore, String name, boolean sync, boolean activateRoot) throws Exception { this.cache = createCache(name, sync, true, false); this.semaphore = semaphore; this.name = name; if (activateRoot) { cache.getRegion(Fqn.ROOT, true).activate(); } } public void run() { boolean acquired = false; try { acquired = semaphore.tryAcquire(60, TimeUnit.SECONDS); if (!acquired) { throw new Exception(name + " cannot acquire semaphore"); } //System.out.println(name + " acquired semaphore"); useCache(); } catch (Exception e) { System.out.println(name + ": " + e.getLocalizedMessage()); e.printStackTrace(System.out); // Save it for the test to check exception = e; } finally { if (acquired) { semaphore.release(); } } } abstract void useCache() throws Exception; public Exception getException() { return exception; } public CacheSPI getCacheSPI() { return cache; } public String getName() { return name; } public void start() { thread = new Thread(this, name); thread.start(); } public void cleanup() { if (thread != null && thread.isAlive()) { thread.interrupt(); } } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/statetransfer/ForcedStateTransferTest.java0000644000175000017500000006203411017455042032740 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * Copyright 2005, JBoss Inc., and individual contributors as indicated * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.cache.statetransfer; import org.jboss.cache.CacheException; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.Version; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.notifications.annotation.CacheListener; import org.jboss.cache.notifications.annotation.NodeModified; import org.jboss.cache.notifications.event.NodeEvent; import static org.testng.AssertJUnit.*; import org.testng.annotations.Test; import javax.transaction.Synchronization; import javax.transaction.Transaction; import javax.transaction.TransactionManager; /** * Tests the ability to force a state transfer in the presence of * transactional and non-transactional threads that are hung holding * locks in the cache. * * @author Brian Stansberry * @version $Revision$ */ @Test(groups = {"functional"}, enabled = false) // this has always been disabled since 1.4.x. See JBCACHE-315 public class ForcedStateTransferTest extends StateTransferTestBase { /** * Starts a cache in a separate thread, allowing the main thread * to abort if state transfer is taking too long. */ static class CacheStarter extends Thread { CacheSPI cache; boolean useMarshalling; Exception failure; CacheStarter(CacheSPI cache, boolean useMarshalling) { this.cache = cache; this.useMarshalling = useMarshalling; } public void run() { try { cache.start(); if (useMarshalling) { // If we don't do initial state transfer, there is // no guarantee of start() blocking until the view is received // so we need to do it ourself TestingUtil.blockUntilViewReceived(cache, 2, 60000); cache.getRegion(Fqn.ROOT, true).activate(); } } catch (Exception e) { failure = e; } } } /** * Generic superclass of classes that perform some operation on the * cache that is intended to hang with a lock held on certain nodes. */ static abstract class TaskRunner extends Thread { CacheSPI cache; Fqn fqn; String value; Exception failure; boolean asleep = false; TaskRunner(CacheSPI cache, String rootFqn, String value) { this.cache = cache; this.value = value; this.fqn = Fqn.fromRelativeElements(Fqn.fromString(rootFqn), value); } public void run() { try { // do whatever the task is executeTask(); } catch (Exception e) { if (!isDone()) failure = e; } finally { asleep = false; // hook to allow final processing finalCleanup(); } } abstract void executeTask() throws Exception; abstract boolean isDone(); void finalCleanup() { } boolean isAsleep() { return asleep; } } /** * Hangs with an active or rollback-only transaction holding locks. */ static class TxRunner extends TaskRunner { TransactionManager tm = null; boolean rollback = false; boolean done = true; TxRunner(CacheSPI cache, String rootFqn, String value, boolean rollback) { super(cache, rootFqn, value); this.rollback = rollback; } void executeTask() throws Exception { tm = cache.getTransactionManager(); tm.begin(); cache.put(fqn, "KEY", value); if (rollback) tm.setRollbackOnly(); asleep = true; TestingUtil.sleepThread((long) 25000); done = true; } void finalCleanup() { if (tm != null) { try { tm.commit(); } catch (Exception ignore) { } } } boolean isDone() { return done; } } /** * TreeCacheListener that hangs the thread in nodeModified(). */ @CacheListener static class HangThreadListener { boolean asleep; Fqn toHang; boolean alreadyHung; boolean done; HangThreadListener(Fqn toHang) { this.toHang = toHang; } @NodeModified public void nodeModified(NodeEvent e) { if (!e.isPre()) hangThread(e.getFqn()); } private void hangThread(Fqn fqn) { if (!alreadyHung && toHang.equals(fqn)) { asleep = true; //System.out.println("Hanging thread changing node " + fqn); alreadyHung = true; TestingUtil.sleepThread((long) 30000); done = true; asleep = false; } } } /** * Hangs with a non-transactional thread holding locks. */ static class HangThreadRunner extends TaskRunner { HangThreadListener listener; HangThreadRunner(CacheSPI cache, String rootFqn, String value) { super(cache, rootFqn, value); listener = new HangThreadListener(fqn); cache.addCacheListener(listener); } void executeTask() throws Exception { // Just do a put and the listener will hang the thread cache.put(fqn, "KEY", value); } boolean isAsleep() { return listener.asleep; } boolean isDone() { return listener.done; } } /** * Synchronization that hangs the thread either in * beforeCompletion() or afterCompletion(). */ static class HangThreadSynchronization implements Synchronization { boolean asleep; boolean hangBefore; boolean done; HangThreadSynchronization(boolean hangBefore) { this.hangBefore = hangBefore; } public void beforeCompletion() { if (hangBefore) { hang(); } } public void afterCompletion(int status) { if (!hangBefore) { hang(); } } void hang() { asleep = true; TestingUtil.sleepThread((long) 30000); done = true; } } /** * Hangs with a transactional thread either in the beforeCompletion() * or afterCompletion() phase holding locks. */ static class SynchronizationTxRunner extends TaskRunner { Transaction tx = null; HangThreadSynchronization sync; SynchronizationTxRunner(CacheSPI cache, String rootFqn, String value, boolean hangBefore) { super(cache, rootFqn, value); this.sync = new HangThreadSynchronization(hangBefore); } void executeTask() throws Exception { TransactionManager tm = cache.getTransactionManager(); tm.begin(); tx = tm.getTransaction(); tx.registerSynchronization(sync); cache.put(fqn, "KEY", value); // Committing the tx will hang the thread tm.commit(); } boolean isAsleep() { return sync.asleep; } boolean isDone() { return sync.done; } } /** * Tests the ability to force a state transfer in the presence * of active transactions on the sending cache. * * @throws Exception */ public void testActiveTransaction() throws Exception { String[] values = {"A", "B", "C"}; transactionTest(values, false, "REPEATABLE_READ"); } /** * Tests the ability to force a state transfer in the presence * of a transaction marked rollback-only on the sending cache. * * @throws Exception */ public void testRollbackOnlyTransaction() throws Exception { String[] values = {"A", "B", "C"}; transactionTest(values, true, "REPEATABLE_READ"); } /** * Run a basic test with transactional threads doing puts and then * hanging before committing. * * @param values node names under which puts should be done * @param rollback should the transactions be marked rollback-only * before hanging * @param isolationLevel cache's isolation level * @throws Exception */ private void transactionTest(String[] values, boolean rollback, String isolationLevel) throws Exception { // Create the cache from which state will be requested CacheSPI sender = initializeSender(isolationLevel, false, false); // Start threads that will do operations on the cache and then hang TxRunner[] runners = initializeTransactionRunners(values, sender, "/LOCK", rollback); // Create and start the cache that requests a state transfer CacheSPI receiver = startReceiver(isolationLevel, false, false); // Confirm the receiver got the expected state and the threads are OK checkResults(receiver, runners, false); } /** * Creates and starts a CacheSPI from which another cache will request * state. Also adds value "X" under key "KEY" in node "/OK". This node * should be present in the transferred state in any test. * * @param isolationLevel cache's isolation level * @param replSync is cache REPL_SYNC? * @param useMarshalling is the activateRegion() API to be used? * @return the cache * @throws Exception */ private CacheSPI initializeSender(String isolationLevel, boolean replSync, boolean useMarshalling) throws Exception { CacheSPI sender = createCache("sender", isolationLevel, replSync, useMarshalling, true); if (useMarshalling) sender.getRegion(Fqn.ROOT, true).activate(); sender.put(Fqn.fromString("/OK"), "KEY", "X"); return sender; } /** * Start a set of TaskRunner threads that do a transactional put on the cache * and then go to sleep with the transaction uncommitted. * * @param values the name of the node that should be put under * rootFqn, and the value that shoud be put in its map * @param sender the cache on which the put should be done * @param rootFqn Fqn under which the new node should be inserted -- the * Fqn of the new node will be /rootFqn/value * @param rollback true if the tx should be marked * rollback-only before the thread goes to sleep * @return the TaskRunner threads */ private TxRunner[] initializeTransactionRunners(String[] values, CacheSPI sender, String rootFqn, boolean rollback) { TxRunner[] runners = new TxRunner[values.length]; for (int i = 0; i < values.length; i++) { runners[i] = new TxRunner(sender, rootFqn, values[i], rollback); initializeRunner(runners[i]); } return runners; } /** * Starts the runner and waits up to 1 second until it is asleep, confirming * that it is alive. * * @param runner */ private void initializeRunner(TaskRunner runner) { runner.start(); // Loop until it executes its put and goes to sleep (i.e. hangs) long start = System.currentTimeMillis(); while (!(runner.isAsleep())) { assertTrue(runner.getClass().getName() + " " + runner.value + " is alive", runner.isAlive()); // Avoid hanging test fixture by only waiting 1 sec before failing assertFalse(runner.getClass().getName() + " " + runner.value + " has not timed out", (System.currentTimeMillis() - start) > 1000); } } /** * Checks whether the receiver cache has the expected state and whether * the runners ran cleanly. Also terminates the runners. * * @param receiver the cache that received state * @param runners the task runners * @param allowValues true if the runners' values are expected to * be in the cache state; false otherwise * @throws CacheException */ private void checkResults(CacheSPI receiver, TaskRunner[] runners, boolean allowValues) throws CacheException { // Check that the runners are alive and kill them boolean[] aliveStates = new boolean[runners.length]; for (int i = 0; i < runners.length; i++) { aliveStates[i] = runners[i].isAlive(); if (aliveStates[i]) runners[i].interrupt(); } // Confirm we got the "non-hung" state assertEquals("OK value correct", "X", receiver.get(Fqn.fromString("/OK"), "KEY")); for (int i = 0; i < runners.length; i++) { assertTrue("Runner " + runners[i].value + " was alive", aliveStates[i]); assertNull("Runner " + runners[i].value + " ran cleanly", runners[i].failure); if (allowValues) { assertEquals("Correct value in " + runners[i].fqn, runners[i].value, receiver.get(runners[i].fqn, "KEY")); } else { assertNull("No value in " + runners[i].fqn, receiver.get(runners[i].fqn, "KEY")); } } } /** * Tests the ability to force a state transfer in the presence of * a hung thread holding a lock on the sending cache. * * @throws Exception */ public void testHungThread() throws Exception { // Create the cache from which state will be requested CacheSPI sender = initializeSender("REPEATABLE_READ", false, false); // Start threads that will do operations on the cache and then hang String[] values = {"A", "B", "C"}; HangThreadRunner[] runners = initializeHangThreadRunners(values, sender, "/LOCK"); // Create and start the cache that requests a state transfer CacheSPI receiver = startReceiver("REPEATABLE_READ", false, false); // Confirm the receiver got the expected state and the threads are OK checkResults(receiver, runners, true); } /** * Start a set of TaskRunner threads that do a non-transactional put on the * cache and then go to sleep with the thread hung in a * TreeCacheListener and locks unreleased * * @param values the name of the node that should be put under * rootFqn, and the value that shoud be put in its map * @param sender the cache on which the put should be done * @param rootFqn Fqn under which the new node should be inserted -- the * Fqn of the new node will be /rootFqn/value * @return the TaskRunner threads */ private HangThreadRunner[] initializeHangThreadRunners(String[] values, CacheSPI sender, String rootFqn) { HangThreadRunner[] runners = new HangThreadRunner[values.length]; for (int i = 0; i < values.length; i++) { runners[i] = new HangThreadRunner(sender, rootFqn, values[i]); initializeRunner(runners[i]); } return runners; } /** * Tests the ability to force a state transfer in the presence * of a transaction that is hung in a * Synchronization.beforeCompletion() call. * * @throws Exception */ public void testBeforeCompletionLock() throws Exception { synchronizationTest(true); } /** * Tests the ability to force a state transfer in the presence * of a transaction that is hung in a * Synchronization.beforeCompletion() call. * * @throws Exception */ public void testAfterCompletionLock() throws Exception { synchronizationTest(false); } /** * Tests the ability to force a state transfer in the presence * of a transaction that is hung either in a * Synchronization.beforeCompletion() or Synchronization.afterCompletion() * call. * * @param hangBefore true if the thread should hang in * beforeCompletion(), false * if it should hang in afterCompletion * @throws Exception */ private void synchronizationTest(boolean hangBefore) throws Exception { CacheSPI sender = initializeSender("REPEATABLE_READ", false, false); String[] values = {"A", "B", "C"}; SynchronizationTxRunner[] runners = initializeSynchronizationTxRunners(values, sender, "/LOCK", hangBefore); CacheSPI receiver = startReceiver("REPEATABLE_READ", false, false); checkResults(receiver, runners, !hangBefore); } /** * Start a set of TaskRunner threads that do a transactional put on the * cache and then go to sleep with the thread hung in a * transaction Synchronization call and locks unreleased * * @param values the name of the node that should be put under * rootFqn, and the value that shoud be put in its map * @param sender the cache on which the put should be done * @param rootFqn Fqn under which the new node should be inserted -- the * Fqn of the new node will be /rootFqn/value * @param hangBefore true if the thread should hang in * beforeCompletion(), false * if it should hang in afterCompletion * @return the TaskRunner threads */ private SynchronizationTxRunner[] initializeSynchronizationTxRunners(String[] values, CacheSPI sender, String rootFqn, boolean hangBefore) { SynchronizationTxRunner[] runners = new SynchronizationTxRunner[values.length]; for (int i = 0; i < values.length; i++) { runners[i] = new SynchronizationTxRunner(sender, rootFqn, values[i], hangBefore); initializeRunner(runners[i]); } return runners; } /** * Tests the ability to force a state transfer in the presence * of multiple issues on the sending cache (active transactions, * rollback-only transactions, transactions hung in beforeCompletion() and * afterCompletion() calls, as well as hung threads). * * @throws Exception */ public void testMultipleProblems() throws Exception { multipleProblemTest("REPEATABLE_READ", "/LOCK", false, false); } /** * Tests the ability to force a state transfer in the presence * of an active transaction in the sending cache * and isolation level SERIALIZABLE. * * @throws Exception */ public void testSerializableIsolation() throws Exception { multipleProblemTest("SERIALIZABLE", "/", false, false); } /** * Tests the ability to force a partial state transfer with multiple * "problem" actors holding locks on the sending node. Same test as * {@link #testMultipleProblems()} except the partial state transfer API is * used instead of an initial state transfer. * * @throws Exception */ public void testPartialStateTransfer() throws Exception { multipleProblemTest("REPEATABLE_READ", "/LOCK", false, true); } /** * Tests the ability to force a partial state transfer with multiple * "problem" actors holding locks on the sending node and cache mode * REPL_SYNC. Same test as {@link #testMultipleProblems()} except the * cache is configured for REPL_SYNC. * * @throws Exception */ public void testReplSync() throws Exception { multipleProblemTest("REPEATABLE_READ", "/LOCK", true, false); } /** * Tests the ability to force a partial state transfer with multiple * "problem" actors holding locks on the sending node. * * @throws Exception */ private void multipleProblemTest(String isolationLevel, String rootFqn, boolean replSync, boolean useMarshalling) throws Exception { CacheSPI sender = initializeSender(isolationLevel, replSync, useMarshalling); // Do the "after" nodes first, otherwise if there is a /LOCK parent // node, the rollback of a tx will remove it causing the test to fail // since the child node created by it will be gone as well. // This is really a REPEATABLE_READ bug that this test isn't intended // to catch; will create a separate locking test that shows it String[] val1 = {"A", "B", "C"}; SynchronizationTxRunner[] after = initializeSynchronizationTxRunners(val1, sender, rootFqn, false); String[] val2 = {"D", "E", "F"}; SynchronizationTxRunner[] before = initializeSynchronizationTxRunners(val2, sender, rootFqn, true); String[] val3 = {"G", "H", "I"}; TxRunner[] active = initializeTransactionRunners(val3, sender, rootFqn, false); String[] val4 = {"J", "K", "L"}; TxRunner[] rollback = initializeTransactionRunners(val4, sender, rootFqn, true); String[] val5 = {"M", "N", "O"}; HangThreadRunner[] threads = initializeHangThreadRunners(val5, sender, rootFqn); CacheSPI receiver = startReceiver(isolationLevel, replSync, useMarshalling); checkResults(receiver, active, false); checkResults(receiver, rollback, false); checkResults(receiver, before, false); checkResults(receiver, after, true); checkResults(receiver, threads, true); } protected String getReplicationVersion() { return Version.version; } /** * Starts a cache that requests state from another cache. Confirms * that the receiver cache starts properly. * * @param isolationLevel * @param replSync * @param useMarshalling * @return the receiver cache * @throws Exception */ private CacheSPI startReceiver(String isolationLevel, boolean replSync, boolean useMarshalling) throws Exception { CacheSPI receiver = createCache("receiver", isolationLevel, replSync, useMarshalling, false); // Start the cache in a separate thread so we can kill the // thread if the cache doesn't start properly CacheStarter starter = new CacheStarter(receiver, useMarshalling); starter.start(); starter.join(20000); boolean alive = starter.isAlive(); if (alive) starter.interrupt(); assertFalse("Starter finished", alive); assertNull("No exceptions in starter", starter.failure); return receiver; } /** * Override the superclass version to set an unlimited state transfer timeout * and a 1 sec lock acquisition timeout. */ private CacheSPI createCache(String cacheID, String isolationLevel, boolean replSync, boolean useMarshalling, boolean startCache) throws Exception { CacheSPI result = super.createCache(cacheID, replSync, useMarshalling, false, false, false, true); result.getConfiguration().setStateRetrievalTimeout(0); result.getConfiguration().setLockAcquisitionTimeout(1000); result.getConfiguration().setIsolationLevel(isolationLevel); if (startCache) result.start(); return result; } } ././@LongLink0000000000000000000000000000015100000000000011562 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/statetransfer/StateTransferCompatibilityTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/statetransfer/StateTransferCompatibilityTest.0000644000175000017500000000741410700163526033507 0ustar twernertwernerpackage org.jboss.cache.statetransfer; import org.testng.annotations.Test; @Test(groups = {"functional"}) public class StateTransferCompatibilityTest extends StateTransferTestBase { protected String getReplicationVersion() { return "2.0.0.GA"; } /** * These tests ensure that http://jira.jboss.com/jira/browse/JBCACHE-738 * compatibility between non-delegating cacheloaders is maintained. In the tests * below first cacheloader parameter is the state producer and the second cacheloader * parameter is the state receiver. By having each cacheloader be a state receiver * and a state producer we ensure complete cacheloader compatibility. */ public void testCompatibilityBetweenFileAndJbdmCacheLoaders() throws Exception { initialStateTferWithLoaderTest("org.jboss.cache.loader.FileCacheLoader", "org.jboss.cache.loader.jdbm.JdbmCacheLoader", false); } public void testCompatibilityBetweenFileAndJDBCCacheLoaders() throws Exception { initialStateTferWithLoaderTest("org.jboss.cache.loader.FileCacheLoader", "org.jboss.cache.loader.JDBCCacheLoader", false); } public void testCompatibilityBetweenFileAndBdbjeCacheLoaders() throws Exception { initialStateTferWithLoaderTest("org.jboss.cache.loader.FileCacheLoader", "org.jboss.cache.loader.bdbje.BdbjeCacheLoader", false); } public void testCompatibilityBetweenJbdmAndJDBCCacheLoaders() throws Exception { initialStateTferWithLoaderTest("org.jboss.cache.loader.jdbm.JdbmCacheLoader", "org.jboss.cache.loader.JDBCCacheLoader", false); } public void testCompatibilityBetweenJbdmAndBdbjeCacheLoaders() throws Exception { initialStateTferWithLoaderTest("org.jboss.cache.loader.jdbm.JdbmCacheLoader", "org.jboss.cache.loader.bdbje.BdbjeCacheLoader", false); } public void testCompatibilityBetweenJbdmAndFileCacheLoaders() throws Exception { initialStateTferWithLoaderTest("org.jboss.cache.loader.jdbm.JdbmCacheLoader", "org.jboss.cache.loader.FileCacheLoader", false); } public void testCompatibilityBetweenJDBCAndBdjeCacheLoaders() throws Exception { initialStateTferWithLoaderTest("org.jboss.cache.loader.JDBCCacheLoader", "org.jboss.cache.loader.bdbje.BdbjeCacheLoader", false); } public void testCompatibilityBetweenJDBCAndFileCacheLoaders() throws Exception { initialStateTferWithLoaderTest("org.jboss.cache.loader.JDBCCacheLoader", "org.jboss.cache.loader.FileCacheLoader", false); } public void testCompatibilityBetweenJDBCAndJbdmCacheLoaders() throws Exception { initialStateTferWithLoaderTest("org.jboss.cache.loader.JDBCCacheLoader", "org.jboss.cache.loader.jdbm.JdbmCacheLoader", false); } public void testCompatibilityBetweenBdbjeandJDBCCacheLoaders() throws Exception { initialStateTferWithLoaderTest("org.jboss.cache.loader.bdbje.BdbjeCacheLoader", "org.jboss.cache.loader.JDBCCacheLoader", false); } public void testCompatibilityBetweenBdbjeandFileCacheLoaders() throws Exception { initialStateTferWithLoaderTest("org.jboss.cache.loader.bdbje.BdbjeCacheLoader", "org.jboss.cache.loader.FileCacheLoader", false); } public void testCompatibilityBetweenBdbjeandJbdmCacheLoaders() throws Exception { initialStateTferWithLoaderTest("org.jboss.cache.loader.bdbje.BdbjeCacheLoader", "org.jboss.cache.loader.jdbm.JdbmCacheLoader", false); } protected void initialStateTferWithLoaderTest(boolean asyncLoader) throws Exception { initialStateTferWithLoaderTest("org.jboss.cache.loader.FileCacheLoader", "org.jboss.cache.loader.FileCacheLoader", asyncLoader); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/statetransfer/CorruptedFileCacheLoader.java0000644000175000017500000000170110740711413033003 0ustar twernertwernerpackage org.jboss.cache.statetransfer; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.Fqn; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.loader.FileCacheLoader; import java.io.IOException; import java.io.ObjectOutputStream; public class CorruptedFileCacheLoader extends FileCacheLoader { private Log log = LogFactory.getLog(CorruptedFileCacheLoader.class); private CacheLoaderConfig.IndividualCacheLoaderConfig cfg; @Override public void setConfig(CacheLoaderConfig.IndividualCacheLoaderConfig base) { this.cfg = base; super.setConfig(base); } @Override public CacheLoaderConfig.IndividualCacheLoaderConfig getConfig() { return cfg; } @Override public void loadState(Fqn subtree, ObjectOutputStream os) throws Exception { throw new IOException("see StateTransfer200Test#testCacheLoaderFailure()"); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/statetransfer/FailedStateTransferTest.java0000644000175000017500000001117711017455042032724 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * Copyright 2005, JBoss Inc., and individual contributors as indicated * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.cache.statetransfer; import org.jboss.cache.CacheException; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Version; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.factories.ComponentRegistry; import org.jboss.cache.factories.UnitTestCacheConfigurationFactory; import org.jboss.cache.factories.annotations.NonVolatile; import org.jboss.cache.lock.TimeoutException; import org.jboss.cache.remoting.jgroups.ChannelMessageListener; import static org.testng.AssertJUnit.fail; import org.testng.annotations.Test; import java.io.InputStream; /** * A FailedStateTransferTest. * * @author Brian Stansberry * @version $Revision$ */ @Test(groups = {"functional"}) public class FailedStateTransferTest extends StateTransferTestBase { public void testFailedStateTransfer() throws Exception { CacheSPI cache = (CacheSPI) new DefaultCacheFactory().createCache(UnitTestCacheConfigurationFactory.createConfiguration(CacheMode.REPL_ASYNC), false); cache.getConfiguration().setClusterName("VersionedTestBase"); cache.getConfiguration().setReplVersionString(getReplicationVersion()); // Use a long timeout to facilitate setting debugger breakpoints cache.getConfiguration().setStateRetrievalTimeout(60000); // Put the cache in the map before starting, so if it fails in // start it can still be destroyed later caches.put("secretive", cache); // inject our own message listener and re-wire deps ComponentRegistry cr = TestingUtil.extractComponentRegistry(cache); // cr.unregisterComponent(ChannelMessageListener.class); cr.registerComponent(new SecretiveStateCacheMessageListener(), ChannelMessageListener.class); // cr.updateDependencies(); cache.start(); CacheSPI recipient = (CacheSPI) new DefaultCacheFactory().createCache(UnitTestCacheConfigurationFactory.createConfiguration(CacheMode.REPL_ASYNC), false); recipient.getConfiguration().setClusterName("VersionedTestBase"); recipient.getConfiguration().setReplVersionString(getReplicationVersion()); // Use a long timeout to facilitate setting debugger breakpoints recipient.getConfiguration().setStateRetrievalTimeout(60000); //Put the cache in the map before starting, so if it fails in // start it can still be destroyed later caches.put("secretive2", recipient); // inject our own message listener and re-wire deps cr = TestingUtil.extractComponentRegistry(recipient); cr.registerComponent(new SecretiveStateCacheMessageListener(), ChannelMessageListener.class); try { recipient.start(); fail("start() should throw an exception"); } catch (CacheException good) { // this is what we want } } protected String getReplicationVersion() { return Version.version; } @NonVolatile private static class SecretiveStateCacheMessageListener extends ChannelMessageListener { @Override public void setState(byte[] new_state) { setStateException = new TimeoutException("Planned Timeout"); } @Override public void setState(InputStream istream) { setStateException = new TimeoutException("Planned Timeout"); } @Override public void setState(String state_id, byte[] state) { setStateException = new TimeoutException("Planned Timeout"); } @Override public void setState(String state_id, InputStream istream) { setStateException = new TimeoutException("Planned Timeout"); } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/buddyreplication/0000755000175000017500000000000011376173754026013 5ustar twernertwerner././@LongLink0000000000000000000000000000015000000000000011561 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/buddyreplication/BuddyReplicationRejoinTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/buddyreplication/BuddyReplicationRejoinTest.j0000644000175000017500000001644011017455042033424 0ustar twernertwernerpackage org.jboss.cache.buddyreplication; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.Cache; import org.jboss.cache.CacheStatus; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.config.BuddyReplicationConfig; import org.jboss.cache.config.Configuration; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.util.CachePrinter; import org.testng.annotations.AfterTest; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; /** * Tests fail over scenario when using buddy replication. *

* What we are looking for is the last log output 'RECOVERED DATA', if all counter values * are zero then we have failed to recover the work done on the buddy node. *

* Change the LATE_START_BUDDY_CACHE flag to trigger different startup behavior at the initial * object creation. It does not seem to have an impact on the outcome though. * * @author Fredrik Johansson, Cubeia Ltd */ @Test public class BuddyReplicationRejoinTest extends BuddyReplicationTestsBase { private static Log log = LogFactory.getLog(BuddyReplicationRejoinTest.class); /** * How many object we should insert to the cache. */ private final static int OBJECT_COUNT = 10; private Cache cache1; private Cache cache2; @BeforeTest public void setUp() throws Exception { BuddyReplicationConfig brc = new BuddyReplicationConfig(); brc.setEnabled(true); brc.setBuddyCommunicationTimeout(1000); brc.setAutoDataGravitation(true); brc.setDataGravitationRemoveOnFind(true); brc.setDataGravitationSearchBackupTrees(true); Configuration c = new Configuration(); c.setCacheMode(Configuration.CacheMode.REPL_SYNC); c.setBuddyReplicationConfig(brc); // Cache1 will be used only for recovery. // Cache2 will perform some updates on the objects and then fail. cache1 = new DefaultCacheFactory().createCache(c, false); cache2 = new DefaultCacheFactory().createCache(c.clone(), false); } @AfterTest public void tearDown() { TestingUtil.killCaches(cache1, cache2); } /** * Executes the test scenario which goes like this: *

* 1. Start primary cache (cache1) *

* 2. Add initial data to primary cache1. All counters are now 0. *

* 3. Start secondary cache (cache2) *

* 4. Let the secondary change data in half of the objects. The cache * will simply update a counter value. The values should now be 1 * for those objects. *

* This will trigger a data-gravitation to the secondary cache and * the objects should be removed from the primary cache's real data *

* 5. Fail the secondary cache (cache.stop()). The primary cache should hold * valid values for the objects updated by the secondary cache (values * should be 2). *

* 6. Print out all data in the primary cache. This will cause all objects * gravitate to the primary cache. We should have the the same state as * after #1 in list except that object 0-4 should have counter = 1. *

* 7. Startup a secondary cache again. *

* 8. Run the same update procedure. The secondary cache should gravitate objects * 0-4 and update the counters with +1, making the counters = 2. *

* 9. Fail the secondary cache. *

* 10. Print out all data in the primary cache. This will cause all objects * gravitate to the primary cache. We should have the the same state as * after #1 in list except that object 0-4 should have counter = 2 this time * since the second secondary cache should have update the values again. *

*

* FAIL. *

* The recovered values in the end are 1, not 2. * It seems like the first buddy cache works fine, but he second time around it * fails to remove the local objects on then primary cache on the remote gravitation. *

* The log printout in the end *should* look like: *

* *********** RECOVERED DATA *********** * /0 counter = 2 * /1 counter = 2 * /2 counter = 2 * /3 counter = 2 * /4 counter = 2 * /5 counter = 0 * /6 counter = 0 * /7 counter = 0 * /8 counter = 0 * /9 counter = 0 * ********************************* *

* But we get 1's instead of 2's, which means that the recovery failed. */ @SuppressWarnings("unchecked") public void testGravitationAndFailover() throws CloneNotSupportedException { Configuration cfg = cache2.getConfiguration().clone(); cache1.start(); TestingUtil.sleepThread(100); addInitial(cache1); printCacheDetails("INITIAL STATES"); cache2.start(); printCacheDetails("CACHE2 STARTED"); runBuddyUpdatesAndFail(); checkRecoveredData(cache1, 1); printCacheDetails("DATA GRAVITATED BACK TO CACHE1"); cache2 = new DefaultCacheFactory().createCache(cfg); printCacheDetails("BUDDY BACK"); runBuddyUpdatesAndFail(); checkRecoveredData(cache1, 2); printCacheDetails("DATA GRAVITATED BACK TO CACHE1 (AGAIN)"); } private void runBuddyUpdatesAndFail() { executeBuddy(cache2); printCacheDetails("BUDDY UPDATED"); cache2.stop(); printCacheDetails("BUDDY FAILED"); } /** * Change some data for a select number of objects on the given cache. * * @param cache */ private void executeBuddy(Cache cache) { for (int i = 0; i < OBJECT_COUNT / 2; i++) { Integer integer = cache.get(Fqn.fromString(String.valueOf(i)), "counter"); cache.put(Fqn.fromString(String.valueOf(i)), "counter", integer + 1); } } /** * Add inital state to a cache * * @param cache */ private void addInitial(Cache cache) { for (int i = 0; i < OBJECT_COUNT; i++) { cache.put(Fqn.fromElements(String.valueOf(i)), "counter", 0); } } /** * Print all data to the log * * @param cache */ private void checkRecoveredData(Cache cache, int expectedValue) { log.info("*********** RECOVERED DATA ***********"); for (int i = 0; i < OBJECT_COUNT; i++) { Integer counter = cache.get(Fqn.fromString(String.valueOf(i)), "counter"); log.info("/" + i + " counter = " + counter); assert i < 5 ? counter == expectedValue : counter == 0; } log.info("*********************************"); } private void printCacheDetails(String state) { log.info("*********** " + state + " ***********"); if (cache1.getCacheStatus() == CacheStatus.STARTED) { log.info("--------- CACHE 1 (" + cache1.getLocalAddress() + ") ---------"); log.info(CachePrinter.printCacheDetails(cache1)); } else { log.info("--------- CACHE 1 STOPPED"); } if (cache2.getCacheStatus() == CacheStatus.STARTED) { log.info("--------- CACHE 2 (" + cache2.getLocalAddress() + ") ---------"); log.info(CachePrinter.printCacheDetails(cache2) + "\n\n"); } else { log.info("--------- CACHE 2 STOPPED"); } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/buddyreplication/BuddyPoolBroadcastTest.java0000644000175000017500000001473311017455042033233 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.buddyreplication; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.CacheSPI; import org.jboss.cache.util.TestingUtil; import static org.testng.AssertJUnit.assertEquals; import org.testng.annotations.AfterMethod; import org.testng.annotations.Test; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.CountDownLatch; /** * Tests basic group membership semantics * * @author Manik Surtani (manik@jboss.org) */ @Test(groups = "functional") public class BuddyPoolBroadcastTest extends BuddyReplicationTestsBase { private Log log = LogFactory.getLog(BuddyPoolBroadcastTest.class); private void checkConsistentPoolState(List> caches) { for (int i = 0; i < caches.size(); i++) { Map groupMap = caches.get(i).getBuddyManager().buddyPool; for (int j = 0; j < caches.size(); j++) { if (i != j) { Map groupMap2 = caches.get(j).getBuddyManager().buddyPool; for (CacheSPI cache : caches) { assertEquals("Comparing contents of cache " + (i + 1) + " pool map with cache " + (j + 1), groupMap.get(cache), groupMap2.get(cache)); } } } } } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { long st = System.currentTimeMillis(); super.tearDown(); System.out.println("Teardown: " + (System.currentTimeMillis() - st)); } public void test2CachesWithPoolNames() throws Exception { log.error("Running test2CachesWithPoolNames"); caches = createCaches(2, true); log.error("Created 2 caches"); BuddyManager m = caches.get(0).getBuddyManager(); Map groupMap = m.buddyPool; assertEquals("A", groupMap.get(caches.get(0).getLocalAddress())); assertEquals("B", groupMap.get(caches.get(1).getLocalAddress())); } public void test3CachesWithPoolNames() throws Exception { log.debug("Running test3CachesWithPoolNames"); long st = System.currentTimeMillis(); caches = createCaches(3, true); System.out.println("Setup: " + (System.currentTimeMillis() - st)); st = System.currentTimeMillis(); BuddyManager m = caches.get(0).getBuddyManager(); Map groupMap = m.buddyPool; assertEquals("A", groupMap.get(caches.get(0).getLocalAddress())); assertEquals("B", groupMap.get(caches.get(1).getLocalAddress())); assertEquals("C", groupMap.get(caches.get(2).getLocalAddress())); System.out.println("Test: " + (System.currentTimeMillis() - st)); } public void testBuddyPoolSync() throws Exception { log.debug("Running testBuddyPoolSync"); caches = createCaches(3, true); Map map = caches.get(0).getBuddyManager().buddyPool; // first test the values assertEquals("Failed on cache 1", "A", map.get(caches.get(0).getLocalAddress())); assertEquals("Failed on cache 1", "B", map.get(caches.get(1).getLocalAddress())); assertEquals("Failed on cache 1", "C", map.get(caches.get(2).getLocalAddress())); // now test against each other checkConsistentPoolState(caches); } public void testChangingBuddyPoolMembership() throws Exception { log.debug("Running testChangingBuddyPoolMembership"); caches = createCaches(3, true); Map map = caches.get(0).getBuddyManager().buddyPool; // first test the values assertEquals("Failed on cache 1", "A", map.get(caches.get(0).getLocalAddress())); assertEquals("Failed on cache 1", "B", map.get(caches.get(1).getLocalAddress())); assertEquals("Failed on cache 1", "C", map.get(caches.get(2).getLocalAddress())); // now test against each other checkConsistentPoolState(caches); caches.get(1).stop(); caches.set(1, createCache(1, "Z")); TestingUtil.blockUntilViewsReceived(caches.toArray(new CacheSPI[0]), VIEW_BLOCK_TIMEOUT); TestingUtil.sleepThread(getSleepTimeout()); // first test the values assertEquals("Failed on cache 1", "A", map.get(caches.get(0).getLocalAddress())); assertEquals("Failed on cache 1", "Z", map.get(caches.get(1).getLocalAddress())); assertEquals("Failed on cache 1", "C", map.get(caches.get(2).getLocalAddress())); // now test against each other checkConsistentPoolState(caches); } public void testConcurrency() throws Exception { log.debug("Running testConcurrency"); int numCaches = 4; caches = new ArrayList>(4); CountDownLatch latch = new CountDownLatch(1); CacheStarter[] starters = new CacheStarter[numCaches]; for (int i = 0; i < numCaches; i++) { caches.add(createCache(1, new String(new char[]{(char) ('A' + i)}), false, false)); starters[i] = new CacheStarter("CacheStarter-" + i, latch, caches.get(i)); starters[i].start(); } // now have the lot start simultaneously // TestingUtil.sleepThread(500); latch.countDown(); // allow a generous sleep time TestingUtil.blockUntilViewsReceived(caches.toArray(new CacheSPI[0]), 240000); TestingUtil.sleepThread(1000 * numCaches); // the max timeout we can expect is 2500ms * 10 nodes // and now look at the state of things. Map map = caches.get(0).getBuddyManager().buddyPool; System.out.println(map); for (int i = 0; i < numCaches; i++) { if (caches.get(i) != null) assertEquals("Failed on cache " + i + "(" + caches.get(i).getLocalAddress() + ")", new String(new char[]{(char) ('A' + i)}), map.get(caches.get(i).getLocalAddress())); else System.out.println("Cache " + i + " is null!??"); } checkConsistentPoolState(caches); } public class CacheStarter extends Thread { private CountDownLatch latch; private CacheSPI cache; public CacheStarter(String name, CountDownLatch latch, CacheSPI cache) { super(name); this.latch = latch; this.cache = cache; } public void run() { try { latch.await(); cache.start(); } catch (Exception e) { e.printStackTrace(); } } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/buddyreplication/EmptyRegionTest.java0000644000175000017500000000561411024205072031741 0ustar twernertwernerpackage org.jboss.cache.buddyreplication; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.Region; import org.jboss.cache.notifications.annotation.BuddyGroupChanged; import org.jboss.cache.notifications.annotation.CacheListener; import org.jboss.cache.notifications.event.Event; import org.jboss.cache.util.CachePrinter; import org.jboss.cache.util.TestingUtil; import org.testng.annotations.AfterTest; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; /** * To test http://jira.jboss.org/jira/browse/JBCACHE-1349 * * @author Manik Surtani (manik@jboss.org) */ @Test(groups = "functional") public class EmptyRegionTest extends BuddyReplicationTestsBase { CacheSPI c1, c2; Fqn regionFqn = Fqn.fromString("/a/b/c"); Fqn region2Fqn = Fqn.fromString("/d/e/f"); Region region, region2; CountDownLatch buddyJoinLatch = new CountDownLatch(1); @BeforeTest public void setUp() throws Exception { c1 = createCache(1, null, false, false, false); c1.getConfiguration().setUseRegionBasedMarshalling(true); c1.getConfiguration().setFetchInMemoryState(true); c2 = (CacheSPI) new DefaultCacheFactory().createCache(c1.getConfiguration().clone(), false); c1.start(); region = c1.getRegion(regionFqn, true); region2 = c1.getRegion(region2Fqn, true); region.registerContextClassLoader(getClass().getClassLoader()); region2.registerContextClassLoader(getClass().getClassLoader()); c1.put(region2Fqn, "key", "value"); c2.create(); c2.addCacheListener(new BuddyJoinListener()); } @AfterTest public void tearDown() { TestingUtil.killCaches(c1, c2); } public void testEmptyRegion() throws InterruptedException { // region on c1 is empty - with no root node. assert c1.getNode(regionFqn) == null : "Node should not exist"; assert c1.getRegion(regionFqn, false) != null : "Region should exist"; assert c1.getRegion(regionFqn, false).isActive() : "Region should be active"; // now start c2 c2.start(); // wait for buddy join notifications to complete. buddyJoinLatch.await(60, TimeUnit.SECONDS); // should not throw any exceptions!! System.out.println("Cache1 " + CachePrinter.printCacheDetails(c1)); System.out.println("Cache2 " + CachePrinter.printCacheDetails(c2)); // make sure region2 stuff did get transmitted! assert c2.peek(fqnTransformer.getBackupFqn(c1.getLocalAddress(), region2Fqn), false) != null : "Region2 state should have transferred!"; } @CacheListener public class BuddyJoinListener { @BuddyGroupChanged public void buddyJoined(Event e) { buddyJoinLatch.countDown(); } } } ././@LongLink0000000000000000000000000000015600000000000011567 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/buddyreplication/BuddyAssignmentStateTransferTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/buddyreplication/BuddyAssignmentStateTransfer0000644000175000017500000001751711024205072033532 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.buddyreplication; import org.jboss.cache.Cache; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.notifications.annotation.BuddyGroupChanged; import org.jboss.cache.notifications.annotation.CacheListener; import org.jboss.cache.notifications.event.Event; import static org.testng.AssertJUnit.*; import org.testng.annotations.Test; import java.util.ArrayList; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; /** * Tests how groups are formed and disbanded * * @author Manik Surtani (manik@jboss.org) */ @Test(groups = {"functional", "jgroups"}) public class BuddyAssignmentStateTransferTest extends BuddyReplicationTestsBase { protected int timeout = 10000; // !!! protected int getSleepTimeout() { return timeout; } @CacheListener public static class BuddyJoinedListener { CountDownLatch latch; public BuddyJoinedListener(CountDownLatch latch) { this.latch = latch; } @BuddyGroupChanged public void buddyJoined(Event e) { latch.countDown(); } } private void createCacheWithLatch(CountDownLatch latch) throws Exception { CacheSPI cache2 = createCache(1, "TEST", false, false); cache2.create(); cache2.addCacheListener(new BuddyJoinedListener(latch)); cache2.start(); caches.add(cache2); } private void replaceLatch(Cache cache, CountDownLatch newLatch) { BuddyJoinedListener bjl = null; for (Object listener : cache.getCacheListeners()) { if (listener instanceof BuddyJoinedListener) { bjl = (BuddyJoinedListener) listener; break; } } if (bjl != null) cache.removeCacheListener(bjl); cache.addCacheListener(new BuddyJoinedListener(newLatch)); } public void testNonRegionBasedStateTransfer() throws Exception { caches = new ArrayList>(); caches.add(createCache(1, "TEST", false, true)); Fqn main = Fqn.fromString("/a/b/c"); caches.get(0).put(main, "name", "Joe"); CountDownLatch latch = new CountDownLatch(1); createCacheWithLatch(latch); assert latch.await(getSleepTimeout(), TimeUnit.MILLISECONDS) : "Buddy groups not formed after " + getSleepTimeout() + " millis!"; Fqn test = fqnTransformer.getBackupFqn(caches.get(0).getLocalAddress(), main); assertEquals("State not transferred", "Joe", caches.get(1).get(test, "name")); latch = new CountDownLatch(1); createCacheWithLatch(latch); assert latch.await(getSleepTimeout(), TimeUnit.MILLISECONDS) : "Buddy groups not formed after " + getSleepTimeout() + " millis!"; assertNull("State not transferred", caches.get(2).get(test, "name")); latch = new CountDownLatch(1); replaceLatch(caches.get(2), latch); replaceLatch(caches.get(0), latch); // Make 2 the buddy of 0 caches.get(1).stop(); caches.set(1, null); assert latch.await(getSleepTimeout(), TimeUnit.MILLISECONDS) : "Buddy groups not formed after " + getSleepTimeout() + " millis!"; assertEquals("State transferred", "Joe", caches.get(2).get(test, "name")); } public void testRegionBasedStateTransfer() throws Exception { caches = new ArrayList>(); CountDownLatch latch = new CountDownLatch(4); caches.add(createCache(1, "TEST", false, false)); caches.add(createCache(1, "TEST", false, false)); caches.add(createCache(1, "TEST", false, false)); // JBCACHE-1234 -- add a 4th cache so when we kill caches[1] // caches[0] is not the backup node for caches[2] (although // caches[2] *is* the backup node for caches[0] caches.add(createCache(1, "TEST", false, false)); for (Cache c : caches) { c.getConfiguration().setInactiveOnStartup(true); c.getConfiguration().setUseRegionBasedMarshalling(true); c.create(); c.addCacheListener(new BuddyJoinedListener(latch)); } for (Cache c : caches) c.start(); assert latch.await(getSleepTimeout(), TimeUnit.MILLISECONDS) : "Buddy groups not formed after " + getSleepTimeout() + " millis!"; Fqn fqnA = Fqn.fromString("/a"); Fqn fqnD = Fqn.fromString("/d"); // FIXME We have to use a hack to get JBC to recognize that our regions are for marshalling ClassLoader cl = Fqn.class.getClassLoader(); for (Cache c : caches) { c.getRegion(fqnA, true).registerContextClassLoader(cl); c.getRegion(fqnD, true).registerContextClassLoader(cl); } for (Cache c : caches) c.getRegion(fqnA, true).activate(); caches.get(0).getRegion(fqnD, true).activate(); caches.get(1).getRegion(fqnD, true).activate(); Fqn mainA = Fqn.fromString("/a/b/c"); caches.get(0).put(mainA, "name", "Joe"); Fqn mainD = Fqn.fromString("/d/e/f"); caches.get(0).put(mainD, "name", "Joe"); Fqn testA = fqnTransformer.getBackupFqn(caches.get(0).getLocalAddress(), mainA); assertEquals("/a replicated", "Joe", caches.get(1).get(testA, "name")); assertNull("No backup of /a", caches.get(2).get(testA, "name")); Fqn testD = fqnTransformer.getBackupFqn(caches.get(0).getLocalAddress(), mainD); assertEquals("/d replicated", "Joe", caches.get(1).get(testD, "name")); assertNull("No backup of /d", caches.get(2).get(testD, "name")); // Make 2 the buddy of 0 -- this should cause a push from 0 to 2 latch = new CountDownLatch(1); replaceLatch(caches.get(2), latch); replaceLatch(caches.get(0), latch); caches.get(1).stop(); assert latch.await(getSleepTimeout(), TimeUnit.MILLISECONDS) : "Buddy groups not formed after " + getSleepTimeout() + " millis!"; assertEquals("/a state transferred", "Joe", caches.get(2).get(testA, "name")); assertNull("/d state not transferred", caches.get(2).get(testD, "name")); // JBCACHE-1234 -- Activate region on 2 and 3. This should cause // a pull from 0 by and from 2 by 3. caches.get(2).getRegion(fqnD, true).activate(); caches.get(3).getRegion(fqnD, true).activate(); assertEquals("/d transferred to cache 2", "Joe", caches.get(2).get(testD, "name")); assertNull("/d state not transferred to cache 3", caches.get(3).get(testD, "name")); } public void testPersistentStateTransfer() throws Exception { caches = new ArrayList>(); CountDownLatch latch = new CountDownLatch(2); caches.add(createCacheWithCacheLoader(false, false, false, true, false)); caches.get(0).getConfiguration().setFetchInMemoryState(false); caches.get(0).create(); caches.get(0).addCacheListener(new BuddyJoinedListener(latch)); caches.get(0).start(); Fqn main = Fqn.fromString("/a/b/c"); caches.get(0).put(main, "name", "Joe"); caches.add(createCacheWithCacheLoader(false, false, false, true, false)); caches.get(1).getConfiguration().setFetchInMemoryState(false); caches.get(1).create(); caches.get(1).addCacheListener(new BuddyJoinedListener(latch)); caches.get(1).start(); assert latch.await(getSleepTimeout(), TimeUnit.MILLISECONDS) : "Buddy groups not formed after " + getSleepTimeout() + " millis!"; Fqn test = fqnTransformer.getBackupFqn(caches.get(0).getLocalAddress(), main); assertFalse("/a/b/c shld not be bin memory", caches.get(1).exists(test)); assertNotNull("/a/b/c shld be in CL", caches.get(1).getCacheLoaderManager().getCacheLoader().get(test)); assertEquals("/a/b/c shld in cache loader", "Joe", caches.get(1).get(test, "name")); } }././@LongLink0000000000000000000000000000015000000000000011561 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/buddyreplication/EvictionOfBuddyBackupsTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/buddyreplication/EvictionOfBuddyBackupsTest.j0000644000175000017500000000467711024205072033365 0ustar twernertwernerpackage org.jboss.cache.buddyreplication; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.config.EvictionConfig; import org.jboss.cache.config.EvictionRegionConfig; import org.jboss.cache.eviction.LRUConfiguration; import org.jboss.cache.eviction.NullEvictionPolicy; import org.testng.annotations.AfterTest; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; import java.util.Collections; /** * Tests the eviction of buddy backup regions * * @author Manik Surtani (manik@jboss.org) * @since 2.2.0 */ @Test(groups = "functional") public class EvictionOfBuddyBackupsTest extends BuddyReplicationTestsBase { private CacheSPI cache1, cache2; private Fqn fqn = Fqn.fromString("/a/b/c"); @BeforeTest public void setUp() throws Exception { cache1 = createCache(1, null, true, false); cache1.getConfiguration().setEvictionConfig(getEvictionConfig()); cache1.start(); cache2 = (CacheSPI) new DefaultCacheFactory().createCache(cache1.getConfiguration().clone()); TestingUtil.blockUntilViewsReceived(60000, cache1, cache2); } @AfterTest public void tearDown() { TestingUtil.killCaches(cache1, cache2); } private EvictionConfig getEvictionConfig() { EvictionConfig c = new EvictionConfig(); c.setDefaultEvictionPolicyClass(NullEvictionPolicy.class.getName()); c.setWakeupIntervalSeconds(1); LRUConfiguration epc = new LRUConfiguration(); epc.setMaxAgeSeconds(1); epc.setTimeToLiveSeconds(1); EvictionRegionConfig erc = new EvictionRegionConfig(fqn, epc); c.setEvictionRegionConfigs(Collections.singletonList(erc)); return c; } public void testEvictionOfBackupRegions() throws Exception { cache1.put(fqn, "k", "v"); assert cache1.peek(fqn, false, false) != null : "Node should exist"; assert cache2.peek(fqnTransformer.getBackupFqn(cache1.getLocalAddress(), fqn), false, false) != null : "Node should exist on backup"; // now wait for eviction to kick in - for up to 2 secs TestingUtil.sleepThread(2000); assert cache1.peek(fqn, false, false) == null : "Node should have evicted"; assert cache2.peek(fqnTransformer.getBackupFqn(cache1.getLocalAddress(), fqn), false, false) == null : "Node should have evicted on backup"; } } ././@LongLink0000000000000000000000000000014600000000000011566 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/buddyreplication/BuddyGroupAssignmentTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/buddyreplication/BuddyGroupAssignmentTest.jav0000644000175000017500000002070011024205072033444 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.buddyreplication; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.Cache; import org.jboss.cache.Fqn; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.util.CachePrinter; import org.testng.annotations.Test; /** * Tests how groups are formed and disbanded * * @author Manik Surtani (manik@jboss.org) */ @Test(groups = {"functional", "jgroups"}) public class BuddyGroupAssignmentTest extends BuddyReplicationTestsBase { private Log log = LogFactory.getLog(BuddyGroupAssignmentTest.class); BuddyFqnTransformer fqnTransformer = new BuddyFqnTransformer(); public void testSingleBuddy() throws Exception { log.debug("Running testSingleBuddy"); caches = createCaches(3, false); System.out.println("*** Testing cache 0:"); assertIsBuddy(caches.get(0), caches.get(1), true); System.out.println("*** Testing cache 1:"); assertIsBuddy(caches.get(1), caches.get(2), true); System.out.println("*** Testing cache 2:"); assertIsBuddy(caches.get(2), caches.get(0), true); System.out.println("Cache 0 = " + CachePrinter.printCacheLockingInfo(caches.get(0))); System.out.println("Cache 1 = " + CachePrinter.printCacheLockingInfo(caches.get(1))); System.out.println("Cache 2 = " + CachePrinter.printCacheLockingInfo(caches.get(2))); } public void test2Buddies() throws Exception { log.debug("Running test2Buddies"); caches = createCaches(2, 3, false); TestingUtil.blockUntilViewsReceived(5000, caches.toArray(new Cache[0])); // TestingUtil.sleepThread(2000); System.out.println("*** Testing cache 0"); assertIsBuddy(caches.get(0), caches.get(1), false); assertIsBuddy(caches.get(0), caches.get(2), false); System.out.println("*** Testing cache 1"); assertIsBuddy(caches.get(1), caches.get(2), false); assertIsBuddy(caches.get(1), caches.get(0), false); System.out.println("*** Testing cache 2"); assertIsBuddy(caches.get(2), caches.get(1), false); assertIsBuddy(caches.get(2), caches.get(0), false); } public void testRemovalFromClusterSingleBuddy() throws Exception { log.debug("Running testRemovalFromClusterSingleBuddy"); caches = createCaches(3, false); System.out.println("*** Testing cache 0"); assertIsBuddy(caches.get(0), caches.get(1), true); System.out.println("*** Testing cache 1"); assertIsBuddy(caches.get(1), caches.get(2), true); System.out.println("*** Testing cache 2"); assertIsBuddy(caches.get(2), caches.get(0), true); // now remove a cache from the cluster caches.get(1).stop(); caches.set(1, null); TestingUtil.sleepThread(getSleepTimeout()); // now test new buddy groups System.out.println("*** Testing cache 0"); assertIsBuddy(caches.get(0), caches.get(2), true); System.out.println("*** Testing cache 2"); assertIsBuddy(caches.get(2), caches.get(0), true); System.out.println("*** Completed successfully ***"); assertNoLocks(caches); } public void testRemovalFromCluster2Buddies() throws Exception { log.debug("Running testRemovalFromCluster2Buddies"); caches = createCaches(2, 4, false); assertNoLocks(caches); System.out.println("*** Testing cache 0"); assertIsBuddy(caches.get(0), caches.get(1), false); assertIsBuddy(caches.get(0), caches.get(2), false); System.out.println("*** Testing cache 1"); assertIsBuddy(caches.get(1), caches.get(2), false); assertIsBuddy(caches.get(1), caches.get(3), false); System.out.println("*** Testing cache 2"); assertIsBuddy(caches.get(2), caches.get(3), false); assertIsBuddy(caches.get(2), caches.get(0), false); System.out.println("*** Testing cache 3"); assertIsBuddy(caches.get(3), caches.get(0), false); assertIsBuddy(caches.get(3), caches.get(1), false); // now remove a cache from the cluster caches.get(1).stop(); caches.set(1, null); TestingUtil.sleepThread(getSleepTimeout()); // now test new buddy groups System.out.println("*** Testing cache 0"); assertIsBuddy(caches.get(0), caches.get(2), false); assertIsBuddy(caches.get(0), caches.get(3), false); System.out.println("*** Testing cache 2"); assertIsBuddy(caches.get(2), caches.get(3), false); assertIsBuddy(caches.get(2), caches.get(0), false); System.out.println("*** Testing cache 3"); assertIsBuddy(caches.get(3), caches.get(0), false); assertIsBuddy(caches.get(3), caches.get(2), false); assertNoLocks(caches); } public void testAddingNewCaches() throws Exception { log.debug("Running testAddingNewCaches"); caches = createCaches(2, false); // get some data in there. caches.get(0).put("/cache0", "k", "v"); caches.get(1).put("/cache1", "k", "v"); System.out.println("*** Testing cache 0:"); assertIsBuddy(caches.get(0), caches.get(1), true); System.out.println("*** Testing cache 1:"); assertIsBuddy(caches.get(1), caches.get(0), true); assert caches.get(0).peek(fqnTransformer.getBackupRoot(caches.get(0).getLocalAddress()), false) == null : "Should not have backup region for self"; assert caches.get(0).peek(fqnTransformer.getBackupRoot(caches.get(1).getLocalAddress()), false) != null : "Should have backup region for buddy"; assert caches.get(1).peek(fqnTransformer.getBackupRoot(caches.get(0).getLocalAddress()), false) != null : "Should have backup region for buddy"; assert caches.get(1).peek(fqnTransformer.getBackupRoot(caches.get(1).getLocalAddress()), false) == null : "Should not have backup region for self"; caches.add(createCache(1, null)); TestingUtil.blockUntilViewsReceived(60000, caches); System.out.println("*** Testing cache 0:"); assertIsBuddy(caches.get(0), caches.get(1), true); System.out.println("*** Testing cache 1:"); assertIsBuddy(caches.get(1), caches.get(2), true); System.out.println("*** Testing cache 2:"); assertIsBuddy(caches.get(2), caches.get(0), true); System.out.println("0 Lock info: " + CachePrinter.printCacheLockingInfo(caches.get(0))); System.out.println("1 Lock info: " + CachePrinter.printCacheLockingInfo(caches.get(1))); System.out.println("2 Lock info: " + CachePrinter.printCacheLockingInfo(caches.get(2))); assert caches.get(0).peek(fqnTransformer.getBackupRoot(caches.get(0).getLocalAddress()), false) == null : "Should not have backup region for self"; assert caches.get(0).peek(fqnTransformer.getBackupRoot(caches.get(1).getLocalAddress()), false) == null : "Should have backup region for non-buddy"; assert caches.get(0).peek(fqnTransformer.getBackupRoot(caches.get(2).getLocalAddress()), false) != null : "Should have backup region for buddy"; assert caches.get(1).peek(fqnTransformer.getBackupRoot(caches.get(0).getLocalAddress()), false) != null : "Should have backup region for buddy"; assert caches.get(1).peek(fqnTransformer.getBackupRoot(caches.get(1).getLocalAddress()), false) == null : "Should not have backup region for self"; assert caches.get(1).peek(fqnTransformer.getBackupRoot(caches.get(2).getLocalAddress()), false) == null : "Should not have backup region for non-buddy"; assert caches.get(2).peek(fqnTransformer.getBackupRoot(caches.get(0).getLocalAddress()), false) == null : "Should not have backup region for non-buddy"; assert caches.get(2).peek(fqnTransformer.getBackupRoot(caches.get(1).getLocalAddress()), false) != null : "Should have backup region for buddy"; assert caches.get(2).peek(fqnTransformer.getBackupRoot(caches.get(2).getLocalAddress()), false) == null : "Should not have backup region for self"; // ensure no state transfer has happened!! assert caches.get(2).peek(Fqn.fromString("/cache0"), false) == null : "Unnecessary state should not have been transferred!"; assert caches.get(2).peek(Fqn.fromString("/cache1"), false) == null : "Unnecessary state should not have been transferred!"; // ensure backup state has been transferred. assert caches.get(2).peek(fqnTransformer.getBackupFqn(caches.get(1).getLocalAddress(), Fqn.fromString("/cache1")), false) != null : "Backup state should have transferred!"; } } ././@LongLink0000000000000000000000000000014500000000000011565 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/buddyreplication/BuddyFqnTransformerTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/buddyreplication/BuddyFqnTransformerTest.java0000644000175000017500000000254511026603620033441 0ustar twernertwernerpackage org.jboss.cache.buddyreplication; import org.jboss.cache.Fqn; import org.testng.annotations.Test; /** * @author Manik Surtani (manik@jboss.org) * @since 2.2.0 */ @Test(groups = "unit") public class BuddyFqnTransformerTest { private BuddyFqnTransformer buddyFqnTransformer = new BuddyFqnTransformer(); public void testActualFqn() { Fqn backupFqn = Fqn.fromString("/_BUDDY_BACKUP_/1.2.3.4_5678/a/b/c/d"); assert buddyFqnTransformer.getActualFqn(backupFqn).equals(Fqn.fromString("/a/b/c/d")); backupFqn = Fqn.fromString("/_BUDDY_BACKUP_/1.2.3.4_5678"); Fqn actual = buddyFqnTransformer.getActualFqn(backupFqn); assert actual.equals(Fqn.ROOT); } public void testBackupRootFqn() { Fqn backupFqn = Fqn.fromString("/_BUDDY_BACKUP_/1.2.3.4_5678/a/b/c/d"); assert buddyFqnTransformer.getBackupRootFromFqn(backupFqn).equals(Fqn.fromString("/_BUDDY_BACKUP_/1.2.3.4_5678")) : "Got " + buddyFqnTransformer.getBackupRootFromFqn(backupFqn); } public void testGetActualFqnOnBuddyBackupRoot() { assert Fqn.ROOT == buddyFqnTransformer.getActualFqn(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN); } public void testBackupRootFromFqnOnBuddyBackupRoot() { assert Fqn.ROOT == buddyFqnTransformer.getBackupRootFromFqn(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN); } } ././@LongLink0000000000000000000000000000015200000000000011563 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/buddyreplication/BuddyReplicationFailoverTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/buddyreplication/BuddyReplicationFailoverTest0000644000175000017500000002633311024205072033511 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.buddyreplication; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.util.TestingUtil; import org.jgroups.JChannel; import org.jgroups.protocols.DISCARD; import static org.testng.AssertJUnit.*; import org.testng.annotations.Test; /** * Tests behaviour when data owners fail - essentially this tests data gravitation * * @author Manik Surtani (manik@jboss.org) */ @Test(groups = "functional") public class BuddyReplicationFailoverTest extends BuddyReplicationTestsBase { protected boolean optimisticLocks = false; private String key = "key"; private String value = "value"; BuddyFqnTransformer fqnTransformer = new BuddyFqnTransformer(); public void testDataGravitationKillOwner() throws Exception { testDataGravitation(true); } public void testDataGravitationDontKillOwner() throws Exception { testDataGravitation(false); } private void testDataGravitation(boolean killOwner) throws Exception { caches = createCaches(3, false, true, optimisticLocks); Fqn fqn = Fqn.fromString("/test"); Fqn backupFqn = fqnTransformer.getBackupFqn(caches.get(0).getLocalAddress(), fqn); TestingUtil.dumpCacheContents(caches); caches.get(0).put(fqn, key, value); TestingUtil.dumpCacheContents(caches); assertEquals("Value should exist", value, caches.get(0).get(fqn, key)); TestingUtil.dumpCacheContents(caches); // use exists instead of get() to prevent going up the interceptor stack assertTrue("Should be false", !caches.get(1).exists(fqn)); assertTrue("Should be false", !caches.get(2).exists(fqn)); assertFalse("Should be false", caches.get(0).exists(backupFqn)); assertTrue("Value be true", caches.get(1).exists(backupFqn)); assertFalse("Should be false", caches.get(2).exists(backupFqn)); if (killOwner) { System.out.println("***** About to kill original data owner (" + caches.get(0).getLocalAddress() + "). *****"); // forcefully kill data owner. killChannel(caches.get(0), caches.get(1), 2); System.out.println("Killed. Testing backup roots."); TestingUtil.dumpCacheContents(caches); // assert that the remaining caches have picked new buddies. Cache 1 should have cache 2's backup data. assert caches.get(1).peek(fqnTransformer.getBackupRoot(caches.get(2).getLocalAddress()), false) != null : "Should have new buddy's backup root."; assert caches.get(1).peek(fqnTransformer.getBackupRoot(caches.get(1).getLocalAddress()), false) == null : "Should not have self as a backup root."; assert caches.get(1).peek(fqnTransformer.getBackupRoot(caches.get(0).getLocalAddress()), false) == null : "Should not have dead node as a backup root."; assert caches.get(1).peek(Fqn.fromRelativeElements(fqnTransformer.getDeadBackupRoot(caches.get(0).getLocalAddress()), 1), false) != null : "Should have dead node as a defunct backup root."; assert caches.get(2).peek(fqnTransformer.getBackupRoot(caches.get(2).getLocalAddress()), false) == null : "Should not have self as a backup root."; assert caches.get(2).peek(fqnTransformer.getBackupRoot(caches.get(1).getLocalAddress()), false) != null : "Should have new buddy's backup root."; assert caches.get(2).peek(fqnTransformer.getBackupRoot(caches.get(0).getLocalAddress()), false) == null : "Should not have dead node as a backup root."; assert caches.get(2).peek(Fqn.fromRelativeElements(fqnTransformer.getDeadBackupRoot(caches.get(0).getLocalAddress()), 1), false) == null : "Should not have dead node as a defunct backup root."; } System.out.println("***** Killed original data owner, about to call a get on a different cache instance. *****"); // according to data gravitation, a call to *any* cache should retrieve the data, and move the data to the new cache. assertEquals("Value should have gravitated", value, caches.get(2).get(fqn, key)); delay(); // cleanup commands are async TestingUtil.dumpCacheContents(caches); // now lets test the eviction part of gravitation Fqn newBackupFqn = fqnTransformer.getBackupFqn(caches.get(2).getLocalAddress(), fqn); // use exists instead of get() to prevent going up the interceptor stack if (!killOwner) { assertTrue("Should be false", !caches.get(0).exists(fqn)); } else { assert caches.get(1).peek(fqnTransformer.getBackupRoot(caches.get(2).getLocalAddress()), false) != null : "Should have new buddy's backup root."; assert caches.get(1).peek(fqnTransformer.getBackupRoot(caches.get(1).getLocalAddress()), false) == null : "Should not have self as a backup root."; assert caches.get(1).peek(fqnTransformer.getBackupRoot(caches.get(0).getLocalAddress()), false) == null : "Should not have dead node as a backup root."; assert caches.get(1).peek(Fqn.fromRelativeElements(fqnTransformer.getDeadBackupRoot(caches.get(0).getLocalAddress()), 1), false) == null : "Should not have dead node as a defunct backup root."; assert caches.get(1).peek(fqnTransformer.getDeadBackupRoot(caches.get(0).getLocalAddress()), false) == null : "Should not have dead node as a defunct backup root."; assert caches.get(2).peek(fqnTransformer.getBackupRoot(caches.get(2).getLocalAddress()), false) == null : "Should not have self as a backup root."; assert caches.get(2).peek(fqnTransformer.getBackupRoot(caches.get(1).getLocalAddress()), false) != null : "Should have new buddy's backup root."; assert caches.get(2).peek(fqnTransformer.getBackupRoot(caches.get(0).getLocalAddress()), false) == null : "Should not have dead node as a backup root."; assert caches.get(2).peek(Fqn.fromRelativeElements(fqnTransformer.getDeadBackupRoot(caches.get(0).getLocalAddress()), 1), false) == null : "Should not have dead node as a defunct backup root."; } assertTrue("Should be false", !caches.get(1).exists(fqn)); // the old backup should no longer exist if (!killOwner) assertFalse("Should be null", caches.get(0).exists(backupFqn)); assertFalse("Should be null", caches.get(1).exists(backupFqn)); assertFalse("Should be null", caches.get(2).exists(backupFqn)); // and the backup should now exist in caches.get(2)'s buddy which is caches.get(0) if (killOwner) { assertEquals("Value should exist", value, caches.get(1).get(newBackupFqn, key)); } else { assertEquals("Value should exist", value, caches.get(0).get(newBackupFqn, key)); assertFalse("Should be null", caches.get(1).exists(newBackupFqn)); } assertFalse("Should be null", caches.get(2).exists(newBackupFqn)); } private void killChannel(CacheSPI cacheToKill, CacheSPI anotherCache, int finalExpectedClusterSize) { JChannel channel = (JChannel) cacheToKill.getRPCManager().getChannel(); DISCARD discard = (DISCARD) channel.getProtocolStack().findProtocol(DISCARD.class); if (discard != null) { discard.setDiscardAll(true); TestingUtil.blockUntilViewReceived(anotherCache, finalExpectedClusterSize, 10000, false); // an extra second, for some reason we need this. TestingUtil.sleepThread(1000); } } public void testDataReplicationSuppression() throws Exception { caches = createCaches(3, false, false, optimisticLocks); Fqn fqn = Fqn.fromString("/test"); Fqn backupFqn = fqnTransformer.getBackupFqn(caches.get(0).getLocalAddress(), fqn); caches.get(0).put(fqn, key, value); TestingUtil.dumpCacheContents(caches); assertEquals("value", caches.get(0).get(fqn, key)); assertFalse(caches.get(0).exists(backupFqn)); assertEquals("value", caches.get(1).get(backupFqn, key)); assertFalse(caches.get(1).exists(fqn)); assertFalse(caches.get(2).exists(fqn)); assertFalse(caches.get(2).exists(backupFqn)); assertNoLocks(caches); backupFqn = fqnTransformer.getBackupFqn(caches.get(1).getLocalAddress(), fqn); System.out.println("*** Calling get() on cache[1] with force option"); caches.get(1).getInvocationContext().getOptionOverrides().setForceDataGravitation(true); assertEquals("value", caches.get(1).get(fqn, key)); delay(); // cleanup commands are async TestingUtil.dumpCacheContents(caches); assertFalse(caches.get(1).exists(backupFqn)); assertEquals("value", caches.get(2).get(backupFqn, key)); assertFalse(caches.get(2).exists(fqn)); assertFalse(caches.get(0).exists(fqn)); assertFalse(caches.get(0).exists(backupFqn)); } public void testSubtreeRetrieval() throws Exception { caches = createCaches(3, false, true, optimisticLocks); Fqn fqn = Fqn.fromString("/test"); Fqn fqn2 = Fqn.fromString("/test/subtree"); Fqn backupFqn = fqnTransformer.getBackupFqn(caches.get(0).getLocalAddress(), fqn); Fqn backupFqn2 = fqnTransformer.getBackupFqn(caches.get(0).getLocalAddress(), fqn2); caches.get(0).put(fqn, key, value); caches.get(0).put(fqn2, key, value); // test backup replication to buddy assertEquals(value, caches.get(0).get(fqn, key)); assertEquals(value, caches.get(0).get(fqn2, key)); assertEquals(value, caches.get(1).get(backupFqn, key)); assertEquals(value, caches.get(1).get(backupFqn2, key)); assertTrue(!caches.get(0).exists(backupFqn)); assertTrue(!caches.get(0).exists(backupFqn2)); assertTrue(!caches.get(1).exists(fqn)); assertTrue(!caches.get(1).exists(fqn2)); assertTrue(!caches.get(2).exists(fqn)); assertTrue(!caches.get(2).exists(fqn2)); assertTrue(!caches.get(2).exists(backupFqn)); assertTrue(!caches.get(2).exists(backupFqn2)); assertNoLocks(caches); // gravitate to 2: caches.get(2).getNode(fqn); // expect entire subtree to gravitate. delay(); // cleanup commands are async Fqn newBackupFqn = fqnTransformer.getBackupFqn(caches.get(2).getLocalAddress(), fqn); Fqn newBackupFqn2 = fqnTransformer.getBackupFqn(caches.get(2).getLocalAddress(), fqn2); assertEquals(value, caches.get(2).get(fqn, key)); assertTrue(caches.get(2).exists(fqn2)); assertEquals(value, caches.get(0).get(newBackupFqn, key)); assertTrue(caches.get(0).exists(newBackupFqn2)); assertTrue(!caches.get(2).exists(newBackupFqn)); assertTrue(!caches.get(2).exists(newBackupFqn2)); assertTrue(!caches.get(0).exists(fqn)); assertTrue(!caches.get(0).exists(fqn2)); assertTrue(!caches.get(1).exists(fqn)); assertTrue(!caches.get(1).exists(fqn2)); assertTrue(!caches.get(1).exists(newBackupFqn)); assertTrue(!caches.get(1).exists(newBackupFqn2)); TestingUtil.dumpCacheContents(caches); for (CacheSPI cache : caches) { assertTrue(!cache.exists(backupFqn)); assertTrue(!cache.exists(backupFqn2)); } assertNoLocks(caches); } protected void delay() { TestingUtil.sleepThread(250); } } ././@LongLink0000000000000000000000000000015000000000000011561 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/buddyreplication/NextMemberBuddyLocatorTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/buddyreplication/NextMemberBuddyLocatorTest.j0000644000175000017500000003167210665031725033410 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.buddyreplication; import static org.testng.AssertJUnit.assertEquals; import java.net.Inet6Address; import java.net.InetAddress; import java.net.NetworkInterface; import java.util.Enumeration; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import org.jgroups.Address; import org.jgroups.stack.IpAddress; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * Tests the NextMemberBuddyLocator * * @author Manik Surtani (manik@jboss.org) */ @Test(groups = "functional") public class NextMemberBuddyLocatorTest { private IpAddress dataOwner; private List

buddies_localhost, buddies_same_host_different_nic, buddies_different_hosts; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { buddies_localhost = new LinkedList
(); buddies_same_host_different_nic = new LinkedList
(); buddies_different_hosts = new LinkedList
(); try { dataOwner = new IpAddress(InetAddress.getByName("localhost"), 1000); buddies_localhost.add(new IpAddress(InetAddress.getByName("localhost"), 2000)); buddies_localhost.add(new IpAddress(InetAddress.getByName("localhost"), 3000)); buddies_localhost.add(new IpAddress(InetAddress.getByName("localhost"), 4000)); buddies_localhost.add(new IpAddress(InetAddress.getByName("localhost"), 5000)); // lets get a few more interfaces from the current host Enumeration en = NetworkInterface.getNetworkInterfaces(); while (en.hasMoreElements()) { NetworkInterface i = (NetworkInterface) en.nextElement(); for (Enumeration en2 = i.getInetAddresses(); en2.hasMoreElements();) { InetAddress addr = (InetAddress) en2.nextElement(); if (addr.isLoopbackAddress() || addr instanceof Inet6Address) continue; buddies_same_host_different_nic.add(new IpAddress(addr, 1000)); } } // now lets get some which are definitely on different hosts // don't bother with DNS lookups - just use dummy IP addresses. buddies_different_hosts.add(new IpAddress(InetAddress.getByName("61.62.63.64"), 1000)); buddies_different_hosts.add(new IpAddress(InetAddress.getByName("81.82.83.84"), 1000)); buddies_different_hosts.add(new IpAddress(InetAddress.getByName("101.102.103.104"), 1000)); buddies_different_hosts.add(new IpAddress(InetAddress.getByName("121.122.123.124"), 1000)); } catch (Exception e) { throw e; } } @AfterMethod(alwaysRun = true) public void tearDown() { buddies_localhost = null; buddies_same_host_different_nic = null; buddies_different_hosts = null; dataOwner = null; } private List getBuddies(int numBuddies, boolean ignoreColoc, List
candidates) { return getBuddies(numBuddies, ignoreColoc, candidates, null); } private List getBuddies(int numBuddies, boolean ignoreColoc, List
candidates, Map buddyPool) { NextMemberBuddyLocatorConfig cfg = new NextMemberBuddyLocatorConfig(); cfg.setIgnoreColocatedBuddies(ignoreColoc); cfg.setNumBuddies(numBuddies); NextMemberBuddyLocator nmbl = new NextMemberBuddyLocator(); nmbl.init(cfg); return nmbl.locateBuddies(buddyPool, candidates, dataOwner); } // without colocation public void testSingleBuddyNoColoc() { List
list = new LinkedList
(); list.add(dataOwner); list.add(buddies_localhost.get(0)); list.add(buddies_localhost.get(1)); List results = getBuddies(1, false, list); assertEquals(1, results.size()); assertEquals(buddies_localhost.get(0), results.get(0)); } public void testThreeBuddiesNoColoc() { List
list = new LinkedList
(); list.add(dataOwner); list.add(buddies_localhost.get(0)); list.add(buddies_localhost.get(1)); list.add(buddies_localhost.get(2)); list.add(buddies_localhost.get(3)); List results = getBuddies(3, false, list); assertEquals(3, results.size()); assertEquals(buddies_localhost.get(0), results.get(0)); assertEquals(buddies_localhost.get(1), results.get(1)); assertEquals(buddies_localhost.get(2), results.get(2)); } public void testMoreBuddiesThanAvblNoColoc() { List
list = new LinkedList
(); list.add(dataOwner); list.add(buddies_localhost.get(0)); list.add(buddies_localhost.get(1)); List results = getBuddies(3, false, list); assertEquals(2, results.size()); assertEquals(buddies_localhost.get(0), results.get(0)); assertEquals(buddies_localhost.get(1), results.get(1)); } // with colocation, but all candidates are on the same host public void testSingleBuddyWithColocAllCandidatesColoc() { List
list = new LinkedList
(); list.add(dataOwner); list.add(buddies_localhost.get(0)); list.add(buddies_localhost.get(1)); List results = getBuddies(1, true, list); assertEquals(1, results.size()); assertEquals(buddies_localhost.get(0), results.get(0)); } public void testThreeBuddiesWithColocAllCandidatesColoc() { List
list = new LinkedList
(); list.add(dataOwner); list.add(buddies_localhost.get(0)); list.add(buddies_localhost.get(1)); list.add(buddies_localhost.get(2)); list.add(buddies_localhost.get(3)); List results = getBuddies(3, true, list); assertEquals(3, results.size()); assertEquals(buddies_localhost.get(0), results.get(0)); assertEquals(buddies_localhost.get(1), results.get(1)); assertEquals(buddies_localhost.get(2), results.get(2)); } public void testMoreBuddiesThanAvblWithColocAllCandidatesColoc() { List
list = new LinkedList
(); list.add(dataOwner); list.add(buddies_localhost.get(0)); list.add(buddies_localhost.get(1)); List results = getBuddies(3, true, list); assertEquals(2, results.size()); assertEquals(buddies_localhost.get(0), results.get(0)); assertEquals(buddies_localhost.get(1), results.get(1)); } // with colocation, all candidates are on the same host but with different NICs public void testSingleBuddyWithColocAllCandidatesColocDiffNics() { List
list = new LinkedList
(); list.add(dataOwner); list.add(buddies_localhost.get(0)); list.add(buddies_localhost.get(1)); list.addAll(buddies_same_host_different_nic); List results = getBuddies(1, true, list); assertEquals(1, results.size()); assertEquals(buddies_localhost.get(0), results.get(0)); } public void testThreeBuddiesWithColocAllCandidatesColocDiffNics() { List
list = new LinkedList
(); list.add(dataOwner); list.add(buddies_localhost.get(0)); list.add(buddies_localhost.get(1)); list.add(buddies_localhost.get(2)); list.add(buddies_localhost.get(3)); list.addAll(buddies_same_host_different_nic); List results = getBuddies(3, true, list); assertEquals(3, results.size()); assertEquals(buddies_localhost.get(0), results.get(0)); assertEquals(buddies_localhost.get(1), results.get(1)); assertEquals(buddies_localhost.get(2), results.get(2)); } public void testMoreBuddiesThanAvblWithColocAllCandidatesColocDiffNics() { List
list = new LinkedList
(); list.add(dataOwner); list.add(buddies_localhost.get(0)); list.add(buddies_localhost.get(1)); list.addAll(buddies_same_host_different_nic); List results = getBuddies(3, true, list); assertEquals(buddies_same_host_different_nic.isEmpty() ? 2 : 3, results.size()); assertEquals(buddies_localhost.get(0), results.get(0)); assertEquals(buddies_localhost.get(1), results.get(1)); if (!buddies_same_host_different_nic.isEmpty()) assertEquals(buddies_same_host_different_nic.get(0), results.get(2)); } // now for some non-colocated buddies to pick from public void testSingleBuddyWithColocDiffHosts() { List
list = new LinkedList
(); list.add(dataOwner); list.add(buddies_localhost.get(0)); list.add(buddies_localhost.get(1)); list.addAll(buddies_same_host_different_nic); list.addAll(buddies_different_hosts); List results = getBuddies(1, true, list); assertEquals(1, results.size()); assertEquals(buddies_different_hosts.get(0), results.get(0)); } public void testThreeBuddiesWithColocDiffHosts() { List
list = new LinkedList
(); list.add(dataOwner); list.add(buddies_localhost.get(0)); list.add(buddies_localhost.get(1)); list.add(buddies_localhost.get(2)); list.add(buddies_localhost.get(3)); list.addAll(buddies_same_host_different_nic); list.addAll(buddies_different_hosts); List results = getBuddies(3, true, list); assertEquals(3, results.size()); assertEquals(buddies_different_hosts.get(0), results.get(0)); assertEquals(buddies_different_hosts.get(1), results.get(1)); assertEquals(buddies_different_hosts.get(2), results.get(2)); } public void testMoreBuddiesThanAvblWithColocDiffHosts() { List
list = new LinkedList
(); list.add(dataOwner); list.add(buddies_localhost.get(0)); list.add(buddies_localhost.get(1)); list.add(buddies_different_hosts.get(0)); list.add(buddies_different_hosts.get(1)); List results = getBuddies(3, true, list); assertEquals(3, results.size()); assertEquals(buddies_different_hosts.get(0), results.get(0)); assertEquals(buddies_different_hosts.get(1), results.get(1)); assertEquals(buddies_localhost.get(0), results.get(2)); } // now lets try this with a buddy pool public void testSingleLocalBuddyWithPool() { List
list = new LinkedList
(); list.add(dataOwner); list.add(buddies_localhost.get(0)); list.add(buddies_localhost.get(1)); list.add(buddies_localhost.get(2)); Map pool = new HashMap(); pool.put(dataOwner, "A"); pool.put(buddies_localhost.get(2), "A"); pool.put(buddies_localhost.get(0), "B"); pool.put(buddies_localhost.get(1), "B"); List results = getBuddies(1, true, list, pool); assertEquals(1, results.size()); assertEquals(buddies_localhost.get(2), results.get(0)); } // now lets try this with a buddy pool public void testSingleLocalBuddyWithPoolMixed1() { List
list = new LinkedList
(); list.add(dataOwner); list.add(buddies_localhost.get(0)); list.add(buddies_localhost.get(1)); list.add(buddies_localhost.get(2)); list.add(buddies_different_hosts.get(0)); list.add(buddies_different_hosts.get(1)); Map pool = new HashMap(); pool.put(dataOwner, "A"); pool.put(buddies_localhost.get(2), "A"); pool.put(buddies_localhost.get(0), "B"); pool.put(buddies_localhost.get(1), "B"); pool.put(buddies_different_hosts.get(0), "C"); pool.put(buddies_different_hosts.get(1), "C"); List results = getBuddies(1, true, list, pool); assertEquals(1, results.size()); assertEquals(buddies_localhost.get(2), results.get(0)); } public void testSingleLocalBuddyWithPoolMixed2() { List
list = new LinkedList
(); list.add(dataOwner); list.add(buddies_localhost.get(0)); list.add(buddies_localhost.get(1)); list.add(buddies_localhost.get(2)); list.add(buddies_different_hosts.get(0)); list.add(buddies_different_hosts.get(1)); Map pool = new HashMap(); pool.put(dataOwner, "A"); pool.put(buddies_localhost.get(2), "B"); pool.put(buddies_localhost.get(0), "B"); pool.put(buddies_localhost.get(1), "B"); pool.put(buddies_different_hosts.get(0), "C"); pool.put(buddies_different_hosts.get(1), "C"); List results = getBuddies(1, true, list, pool); assertEquals(1, results.size()); // preference should be for the non-coloc host assertEquals(buddies_different_hosts.get(0), results.get(0)); } public void testWithDataOwnerAtEnd() { List
list = new LinkedList
(); list.addAll(buddies_localhost); list.add(dataOwner); List results = getBuddies(1, true, list); assertEquals(1, results.size()); assertEquals(buddies_localhost.get(0), results.get(0)); } } ././@LongLink0000000000000000000000000000014700000000000011567 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/buddyreplication/BuddyReplicationTestsBase.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/buddyreplication/BuddyReplicationTestsBase.ja0000644000175000017500000003332311024205072033365 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.buddyreplication; import org.jboss.cache.Cache; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.config.BuddyReplicationConfig; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.factories.UnitTestCacheConfigurationFactory; import org.jboss.cache.factories.XmlConfigurationParser; import org.jboss.cache.loader.CacheLoader; import org.jboss.cache.loader.CacheLoaderManager; import org.jboss.cache.loader.DummyInMemoryCacheLoader; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.xml.XmlHelper; import org.jgroups.Address; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.Test; import org.w3c.dom.Element; import javax.transaction.TransactionManager; import java.util.ArrayList; import java.util.List; /** * Base class for BR tests * * @author Manik Surtani (manik@jboss.org) */ @Test(groups = {"functional", "jgroups"}) public abstract class BuddyReplicationTestsBase { protected List> caches; protected BuddyFqnTransformer fqnTransformer = new BuddyFqnTransformer(); @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { System.out.println("***** TEARING DOWN *****"); System.setProperty("org.jboss.cache.shutdown.force", "true"); if (caches != null) { // an optimisation to aid the progress of unit tests, especially in the case of TCP connections. Note that this // is NOT necessary in live systems since each cache would typically be in a separate JVM. for (CacheSPI c : caches) { if (c != null && c.getBuddyManager() != null) c.getBuddyManager().stop(); } for (CacheSPI c : caches) { if (c != null) { TransactionManager tm = c.getTransactionManager(); if (tm != null) { try { if (tm.getTransaction() != null) tm.rollback(); } catch (Exception e) { // error rolling back gtx2EntryMap e.printStackTrace(); } } CacheLoaderManager clm = c.getCacheLoaderManager(); if (clm != null) { CacheLoader cl = c.getCacheLoaderManager().getCacheLoader(); try { if (cl != null) cl.remove(Fqn.ROOT); } catch (Exception e) { // unable to clean cache loader e.printStackTrace(); } } c.stop(); c = null; } } } caches = null; System.gc(); } protected final static int VIEW_BLOCK_TIMEOUT = 5000; protected CacheSPI createCache(int numBuddies, String buddyPoolName) throws Exception { return createCache(numBuddies, buddyPoolName, false, true); } protected CacheSPI createCache(int numBuddies, String buddyPoolName, boolean useDataGravitation) throws Exception { return createCache(numBuddies, buddyPoolName, useDataGravitation, true); } protected CacheSPI createCache(int numBuddies, String buddyPoolName, boolean useDataGravitation, boolean start) throws Exception { return createCache(false, numBuddies, buddyPoolName, useDataGravitation, true, start); } protected CacheSPI createCache(boolean optimisticLocks, int numBuddies, String buddyPoolName, boolean useDataGravitation, boolean start) throws Exception { return createCache(optimisticLocks, numBuddies, buddyPoolName, useDataGravitation, true, start); } protected CacheSPI createCache(int numBuddies, String buddyPoolName, boolean useDataGravitation, boolean removeOnFind, boolean start) throws Exception { return createCache(false, numBuddies, buddyPoolName, useDataGravitation, removeOnFind, start); } protected CacheSPI createCache(boolean optimisticLocks, int numBuddies, String buddyPoolName, boolean useDataGravitation, boolean removeOnFind, boolean start) throws Exception { CacheSPI c = (CacheSPI) new DefaultCacheFactory().createCache(UnitTestCacheConfigurationFactory.createConfiguration(CacheMode.REPL_SYNC, false, false, true), false); c.getConfiguration().setClusterName("BuddyReplicationTest"); // basic config String xmlString = "true\n" + "500000\n" + " org.jboss.cache.buddyreplication.NextMemberBuddyLocator\n" + " " + useDataGravitation + "\n" + " " + removeOnFind + "\n" + " numBuddies = " + numBuddies + "\n"; if (buddyPoolName != null) xmlString += "" + buddyPoolName + ""; xmlString += ""; Element element = XmlHelper.stringToElement(xmlString); BuddyReplicationConfig config = XmlConfigurationParser.parseBuddyReplicationConfig(element); c.getConfiguration().setBuddyReplicationConfig(config); c.getConfiguration().setFetchInMemoryState(true); if (optimisticLocks) { c.getConfiguration().setNodeLockingScheme(Configuration.NodeLockingScheme.OPTIMISTIC); } c.getConfiguration().setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup"); c.getConfiguration().setSyncCommitPhase(true);// helps track down breakages // Call the hook that allows mux integration if that's what the test wants configureMultiplexer(c); if (start) { c.start(); validateMultiplexer(c); } return c; } /** * Provides a hook for multiplexer integration. This default implementation * is a no-op; subclasses that test mux integration would override * to integrate the given cache with a multiplexer. *

* param cache a cache that has been configured but not yet created. */ protected void configureMultiplexer(Cache cache) throws Exception { // default does nothing } /** * Provides a hook to check that the cache's channel came from the * multiplexer, or not, as expected. This default impl asserts that * the channel did not come from the multiplexer. * * @param cache a cache that has already been started */ protected void validateMultiplexer(Cache cache) { assertFalse("Cache is not using multiplexer", cache.getConfiguration().isUsingMultiplexer()); } protected List> createCaches(int numCaches, boolean useBuddyPool) throws Exception { return createCaches(1, numCaches, useBuddyPool, false); } protected List> createCaches(int numCaches, boolean useBuddyPool, boolean useDataGravitation, boolean optimisticLocks) throws Exception { return createCaches(1, numCaches, useBuddyPool, useDataGravitation, optimisticLocks); } protected List> createCaches(int numCaches, boolean useBuddyPool, boolean useDataGravitation) throws Exception { return createCaches(1, numCaches, useBuddyPool, useDataGravitation); } protected List> createCachesWithCacheLoader(int numCaches, boolean useDataGravitation, boolean removeOnFind, boolean passivation) throws Exception { return this.createCachesWithCacheLoader(numCaches, useDataGravitation, removeOnFind, passivation, false); } protected List> createCachesWithCacheLoader(int numCaches, boolean useDataGravitation, boolean removeOnFind, boolean passivation, boolean fetchPersistent) throws Exception { List> caches = new ArrayList>(); for (int i = 0; i < numCaches; i++) { caches.add(createCacheWithCacheLoader(useDataGravitation, removeOnFind, passivation, fetchPersistent, true)); } // allow some time for the caches to start up and discover each other TestingUtil.blockUntilViewsReceived(caches.toArray(new Cache[0]), VIEW_BLOCK_TIMEOUT); TestingUtil.sleepThread(getSleepTimeout()); return caches; } protected CacheSPI createCacheWithCacheLoader(boolean useDataGravitation, boolean removeOnFind, boolean passivation, boolean fetchPersistent, boolean start) throws Exception { CacheSPI cache = createCache(1, null, useDataGravitation, removeOnFind, false); String cloader = "\n" + "" + passivation + "\n" + "\n" + "\n" + "" + DummyInMemoryCacheLoader.class.getName() + "\n" + "false\n" + "false\n" + "" + fetchPersistent + "\n" + "\n" + ""; Element element = XmlHelper.stringToElement(cloader); CacheLoaderConfig cfg = XmlConfigurationParser.parseCacheLoaderConfig(element); cache.getConfiguration().setCacheLoaderConfig(cfg); if (start) { cache.start(); } return cache; } protected List> createCaches(int numBuddies, int numCaches, boolean useBuddyPool) throws Exception { return createCaches(numBuddies, numCaches, useBuddyPool, false); } protected List> createCaches(int numBuddies, int numCaches, boolean useBuddyPool, boolean useDataGravitation) throws Exception { return createCaches(numBuddies, numCaches, useBuddyPool, useDataGravitation, false); } protected List> createCaches(int numBuddies, int numCaches, boolean useBuddyPool, boolean useDataGravitation, boolean optimisticLocks) throws Exception { return createCaches(numBuddies, numCaches, useBuddyPool, useDataGravitation, optimisticLocks, true); } protected List> createCaches(int numBuddies, int numCaches, boolean useBuddyPool, boolean useDataGravitation, boolean optimisticLocks, boolean start) throws Exception { List> caches = new ArrayList>(numCaches); for (int i = 0; i < numCaches; i++) caches.add(createCache(optimisticLocks, numBuddies, useBuddyPool ? Character.toString((char) ('A' + i)) : null, useDataGravitation, start)); if (start) { // allow some time for the caches to start up and discover each other TestingUtil.blockUntilViewsReceived(caches.toArray(new Cache[0]), VIEW_BLOCK_TIMEOUT); TestingUtil.sleepThread(getSleepTimeout()); } return caches; } protected void printBuddyGroup(Cache cache) { BuddyManager bm = ((CacheSPI) cache).getBuddyManager(); BuddyGroup bg = bm.buddyGroup; System.out.println("*** " + bg); System.out.println(" Groups I participate in: " + bm.buddyGroupsIParticipateIn.keySet()); } /** * This is to allow for any state transfers involved (when assigning a buddy) to complete */ protected int getSleepTimeout() { return 1000; } protected void assertIsBuddy(Cache dataOwner, Cache buddy, boolean onlyBuddy) { Address dataOwnerLocalAddress = dataOwner.getLocalAddress(); Address buddyLocalAddress = buddy.getLocalAddress(); System.out.println("*** assert with groups. Testing that " + buddyLocalAddress + " is a buddy for owner " + dataOwnerLocalAddress + " only buddy? " + onlyBuddy); printBuddyGroup(dataOwner); BuddyManager dataOwnerBuddyManager = ((CacheSPI) dataOwner).getBuddyManager(); BuddyManager buddyBuddyManager = ((CacheSPI) buddy).getBuddyManager(); // lets test things on the data owner's side of things if (onlyBuddy) assertEquals("Should only have one buddy", 1, dataOwnerBuddyManager.getBuddyAddresses().size()); assertTrue(buddyLocalAddress + " should be a buddy to " + dataOwnerLocalAddress, dataOwnerBuddyManager.getBuddyAddresses().contains(buddyLocalAddress)); // and now on the buddy end BuddyGroup group = buddyBuddyManager.buddyGroupsIParticipateIn.get(dataOwnerLocalAddress); System.out.println("*** Groups I participate in: " + buddyBuddyManager.buddyGroupsIParticipateIn); System.out.println("*** Buddy's version of dataOwner's group " + group); assertTrue("buddy's list of groups it participates in should contain data owner's group name", buddyBuddyManager.buddyGroupsIParticipateIn.containsKey(dataOwnerLocalAddress)); if (onlyBuddy) assertEquals(1, group.getBuddies().size()); assertTrue(buddyLocalAddress + " should be a buddy to " + group.getGroupName(), group.getBuddies().contains(buddyLocalAddress)); } protected void assertNoLocks(List> caches) { for (Cache cache : caches) { if (cache != null) assertEquals(0, ((CacheSPI) cache).getNumberOfLocksHeld()); } } } ././@LongLink0000000000000000000000000000016300000000000011565 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/buddyreplication/BuddyBackupActivationInactivationTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/buddyreplication/BuddyBackupActivationInactiv0000644000175000017500000001545611024205072033461 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.buddyreplication; import org.jboss.cache.Cache; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.Region; import org.jboss.cache.config.BuddyReplicationConfig; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.factories.UnitTestCacheConfigurationFactory; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.util.CachePrinter; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.HashMap; import java.util.Map; /** * Tests handling of the buddy backup region during region * activation and inactivation * * @author Brian Stansberry */ @Test(groups = "functional") public class BuddyBackupActivationInactivationTest extends BuddyReplicationTestsBase { public static final Fqn A = Fqn.fromString("/a"); public static final Fqn A_B = Fqn.fromString("/a/b"); public static final String JOE = "JOE"; protected Map caches; private ClassLoader orig_TCL; public void testBuddyBackupActivation() throws Exception { CacheSPI cache1 = createCache("cache1", true, true, true); CacheSPI cache2 = createCache("cache2", true, true, true); Fqn A = Fqn.fromString("/a"); TestingUtil.blockUntilViewsReceived(VIEW_BLOCK_TIMEOUT, cache1, cache2); // create the regions on the two caches first Region c1 = cache1.getRegionManager().getRegion(A, Region.Type.MARSHALLING, true); Region c2 = cache2.getRegionManager().getRegion(A, Region.Type.MARSHALLING, true); assertFalse(c1.isActive()); assertFalse(c2.isActive()); c1.activate(); cache1.put(A_B, "name", JOE); // TestingUtil.sleepThread(getSleepTimeout()); System.out.println("Cache dump BEFORE activation"); System.out.println("cache1 " + CachePrinter.printCacheDetails(cache1)); System.out.println("cache2 " + CachePrinter.printCacheDetails(cache2)); c2.activate(); System.out.println("Cache dump AFTER activation"); System.out.println("cache1 " + CachePrinter.printCacheDetails(cache1)); System.out.println("cache2 " + CachePrinter.printCacheDetails(cache2)); Fqn fqn = fqnTransformer.getBackupFqn(cache1.getLocalAddress(), A_B); assertEquals("State transferred with activation", JOE, cache2.get(fqn, "name")); } public void testReplToInactiveRegion() throws Exception { CacheSPI cache1 = createCache("cache1", true, true, true); CacheSPI cache2 = createCache("cache2", true, true, true); TestingUtil.blockUntilViewsReceived(VIEW_BLOCK_TIMEOUT, cache1, cache2); Fqn backupFqn = fqnTransformer.getBackupFqn(cache1.getLocalAddress(), A_B); Fqn A = Fqn.fromString("/a"); Region regionA = cache1.getRegion(A, true); regionA.registerContextClassLoader(getClass().getClassLoader()); regionA.activate(); // Activate the buddy backup subtree in the recipient so any // repl message doesn't get rejected due to that tree being inactive cache2.getRegionManager().activate(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN); cache2.getRegionManager().deactivate(A); cache1.put(A_B, "name", JOE); // TestingUtil.sleepThread(getSleepTimeout()); assertNull("Should be no replication to inactive region", cache2.get(A_B, "name")); assertNull("Should be no replication to inactive backup region", cache2.get(backupFqn, "name")); } public void testBuddyBackupInactivation() throws Exception { CacheSPI cache1 = createCache("cache1", true, true, true); Fqn A = Fqn.fromString("/a"); Region regionA = cache1.getRegion(A, true); regionA.registerContextClassLoader(getClass().getClassLoader()); regionA.activate(); Fqn fqn = Fqn.fromRelativeElements(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN, "test"); fqn = Fqn.fromRelativeFqn(fqn, A_B); cache1.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); cache1.put(fqn, "name", JOE); assertEquals("Put should have been OK", JOE, cache1.get(fqn, "name")); regionA.deactivate(); assertNull("Inactivation should have cleared region", cache1.get(fqn, "name")); } protected CacheSPI createCache(String cacheID, boolean sync, boolean useMarshalling, boolean startCache) throws Exception { if (caches.get(cacheID) != null) { throw new IllegalStateException(cacheID + " already created"); } CacheMode mode = sync ? CacheMode.REPL_SYNC : CacheMode.REPL_ASYNC; Configuration c = UnitTestCacheConfigurationFactory.createConfiguration(mode); CacheSPI cache = (CacheSPI) new DefaultCacheFactory().createCache(c, false); cache.getConfiguration().setClusterName("TestCluster"); if (useMarshalling) { cache.getConfiguration().setUseRegionBasedMarshalling(true); cache.getConfiguration().setInactiveOnStartup(true); } cache.getConfiguration().setBuddyReplicationConfig(getBuddyConfig()); // Put the cache in the map before starting, so if it fails in // start it can still be destroyed later caches.put(cacheID, cache); if (startCache) { cache.create(); cache.start(); } return cache; } @BeforeMethod(alwaysRun = true) protected void setUp() throws Exception { caches = new HashMap(); // Save the TCL in case a test changes it orig_TCL = Thread.currentThread().getContextClassLoader(); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { super.tearDown(); // Restore the TCL in case a test changed it Thread.currentThread().setContextClassLoader(orig_TCL); for (String cacheID : caches.keySet()) { stopCache(caches.get(cacheID)); } } protected void stopCache(Cache cache) { if (cache != null) { try { cache.stop(); cache.destroy(); } catch (Exception e) { System.out.println("Exception stopping cache " + e.getMessage()); e.printStackTrace(System.out); } } } private BuddyReplicationConfig getBuddyConfig() throws Exception { BuddyReplicationConfig brc = new BuddyReplicationConfig(); brc.setEnabled(true); brc.setAutoDataGravitation(false); return brc; } } ././@LongLink0000000000000000000000000000016200000000000011564 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/buddyreplication/BuddyReplicationWithTransactionsTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/buddyreplication/BuddyReplicationWithTransact0000644000175000017500000001255711024205072033520 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.buddyreplication; import org.jboss.cache.Fqn; import org.jboss.cache.util.TestingUtil; import static org.jboss.cache.util.TestingUtil.dumpCacheContents; import static org.testng.AssertJUnit.assertTrue; import org.testng.annotations.Test; import javax.transaction.TransactionManager; /** * @author Manik Surtani (manik@jboss.org) */ @Test(groups = {"functional", "jgroups"}) public class BuddyReplicationWithTransactionsTest extends BuddyReplicationTestsBase { private Fqn fqn = Fqn.fromString("test"); private String key = "key"; private String value = "value"; BuddyFqnTransformer fqnTransformer = new BuddyFqnTransformer(); public void testTransactionsCommit() throws Exception { caches = createCaches(3, false, true, false); caches.get(0).put(fqn, key, value); Fqn oldBackupFqn = Fqn.fromString("/" + BuddyManager.BUDDY_BACKUP_SUBTREE + "/" + fqnTransformer.getGroupNameFromAddress(caches.get(0).getLocalAddress()) + "/test"); Fqn newBackupFqn = Fqn.fromString("/" + BuddyManager.BUDDY_BACKUP_SUBTREE + "/" + fqnTransformer.getGroupNameFromAddress(caches.get(2).getLocalAddress()) + "/test"); dumpCacheContents(caches); TransactionManager txman = caches.get(2).getTransactionManager(); assertTrue(caches.get(0).exists(fqn)); assertTrue(!caches.get(1).exists(fqn)); assertTrue(!caches.get(2).exists(fqn)); assertTrue(!caches.get(0).exists(oldBackupFqn)); assertTrue(caches.get(1).exists(oldBackupFqn)); assertTrue(!caches.get(2).exists(oldBackupFqn)); assertTrue(!caches.get(0).exists(newBackupFqn)); assertTrue(!caches.get(1).exists(newBackupFqn)); assertTrue(!caches.get(2).exists(newBackupFqn)); txman.begin(); dumpCacheContents(caches); // will cause gravitation caches.get(2).get(fqn, key); dumpCacheContents(caches); assertTrue(caches.get(0).exists(fqn)); assertTrue(!caches.get(1).exists(fqn)); assertTrue(caches.get(2).exists(fqn)); assertTrue(!caches.get(0).exists(oldBackupFqn)); assertTrue(caches.get(1).exists(oldBackupFqn)); assertTrue(!caches.get(2).exists(oldBackupFqn)); assertTrue(!caches.get(0).exists(newBackupFqn)); assertTrue(!caches.get(1).exists(newBackupFqn)); assertTrue(!caches.get(2).exists(newBackupFqn)); txman.commit(); cleanupDelay(); // cleanup commands are async dumpCacheContents(caches); assertTrue(!caches.get(0).exists(fqn)); assertTrue(!caches.get(1).exists(fqn)); assertTrue(caches.get(2).exists(fqn)); assertTrue(!caches.get(0).exists(oldBackupFqn)); assertTrue(!caches.get(1).exists(oldBackupFqn)); assertTrue(!caches.get(2).exists(oldBackupFqn)); assertTrue(caches.get(0).exists(newBackupFqn)); assertTrue(!caches.get(1).exists(newBackupFqn)); assertTrue(!caches.get(2).exists(newBackupFqn)); dumpCacheContents(caches); assertNoLocks(caches); } private void cleanupDelay() { TestingUtil.sleepThread(250); } public void testTransactionsRollback() throws Exception { caches = createCaches(3, false, true, false); caches.get(0).put(fqn, key, value); Fqn oldBackupFqn = Fqn.fromString("/" + BuddyManager.BUDDY_BACKUP_SUBTREE + "/" + fqnTransformer.getGroupNameFromAddress(caches.get(0).getLocalAddress()) + "/test"); Fqn newBackupFqn = Fqn.fromString("/" + BuddyManager.BUDDY_BACKUP_SUBTREE + "/" + fqnTransformer.getGroupNameFromAddress(caches.get(2).getLocalAddress()) + "/test"); dumpCacheContents(caches); TransactionManager txman = caches.get(2).getTransactionManager(); assertTrue(caches.get(0).exists(fqn)); assertTrue(!caches.get(1).exists(fqn)); assertTrue(!caches.get(2).exists(fqn)); assertTrue(!caches.get(0).exists(oldBackupFqn)); assertTrue(caches.get(1).exists(oldBackupFqn)); assertTrue(!caches.get(2).exists(oldBackupFqn)); assertTrue(!caches.get(0).exists(newBackupFqn)); assertTrue(!caches.get(1).exists(newBackupFqn)); assertTrue(!caches.get(2).exists(newBackupFqn)); txman.begin(); dumpCacheContents(caches); caches.get(2).get(fqn, key); dumpCacheContents(caches); assertTrue(caches.get(0).exists(fqn)); assertTrue(!caches.get(1).exists(fqn)); assertTrue(caches.get(2).exists(fqn)); assertTrue(!caches.get(0).exists(oldBackupFqn)); assertTrue(caches.get(1).exists(oldBackupFqn)); assertTrue(!caches.get(2).exists(oldBackupFqn)); assertTrue(!caches.get(0).exists(newBackupFqn)); assertTrue(!caches.get(1).exists(newBackupFqn)); assertTrue(!caches.get(2).exists(newBackupFqn)); txman.rollback(); dumpCacheContents(caches); assertTrue(caches.get(0).exists(fqn)); assertTrue(!caches.get(1).exists(fqn)); assertTrue(!caches.get(2).exists(fqn)); assertTrue(!caches.get(0).exists(oldBackupFqn)); assertTrue(caches.get(1).exists(oldBackupFqn)); assertTrue(!caches.get(2).exists(oldBackupFqn)); assertTrue(!caches.get(0).exists(newBackupFqn)); assertTrue(!caches.get(1).exists(newBackupFqn)); assertTrue(!caches.get(2).exists(newBackupFqn)); assertNoLocks(caches); } } ././@LongLink0000000000000000000000000000016700000000000011571 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/buddyreplication/BuddyReplicationWithOptimisticLockingTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/buddyreplication/BuddyReplicationWithOptimist0000644000175000017500000000122111024205072033533 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.buddyreplication; import org.testng.annotations.Test; /** * @author Manik Surtani (manik@jboss.org) */ @Test(groups = {"functional", "jgroups"}) public class BuddyReplicationWithOptimisticLockingTest extends BuddyReplicationFailoverTest { public BuddyReplicationWithOptimisticLockingTest() { optimisticLocks = true; } @Override public void testDataGravitationKillOwner() throws Exception { super.testDataGravitationKillOwner(); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/buddyreplication/BuddyManagerTest.java0000644000175000017500000002123011024246144032036 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.buddyreplication; import org.jboss.cache.Fqn; import org.jboss.cache.commands.ReplicableCommand; import org.jboss.cache.commands.remote.ReplicateCommand; import org.jboss.cache.commands.write.PutKeyValueCommand; import org.jboss.cache.config.BuddyReplicationConfig; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.CommandsFactory; import org.jboss.cache.factories.XmlConfigurationParser; import org.jboss.cache.xml.XmlHelper; import static org.testng.AssertJUnit.*; import org.testng.annotations.Test; import org.w3c.dom.Element; import java.util.ArrayList; import java.util.List; /** * Tests the BuddyManager class * * @author Manik Surtani (manik@jboss.org) */ @Test(groups = "functional") public class BuddyManagerTest { private static final String DUMMY_LOCAL_ADDRESS = "myLocalAddress:12345"; private BuddyFqnTransformer fqnTransformer; /** * Constructs a buddy manager using the default buddy locator but with some specific properties. * * @throws Exception */ public void testConstruction1() throws Exception { String xmlConfig = "true\n" + " numBuddies = 3\n" + " groupOne"; Element element = XmlHelper.stringToElement(xmlConfig); BuddyReplicationConfig config = XmlConfigurationParser.parseBuddyReplicationConfig(element); BuddyManager mgr = new BuddyManager(config); assertTrue(mgr.isEnabled()); assertEquals("groupOne", mgr.getBuddyPoolName()); assertEquals(NextMemberBuddyLocator.class, mgr.buddyLocator.getClass()); NextMemberBuddyLocatorConfig blc = (NextMemberBuddyLocatorConfig) mgr.buddyLocator.getConfig(); assertEquals(3, blc.getNumBuddies()); assertTrue(blc.isIgnoreColocatedBuddies()); } /** * Constructs a buddy manager using a nonexistent buddy locator but with some specific properties. * * @throws Exception */ public void testConstruction2() throws Exception { String xmlConfig = "true\n" + " org.i.dont.exist.PhantomBuddyLocator\n" + " numBuddies = 3\n" + " groupOne"; Element element = XmlHelper.stringToElement(xmlConfig); BuddyReplicationConfig config = XmlConfigurationParser.parseBuddyReplicationConfig(element); BuddyManager mgr = new BuddyManager(config); assertTrue(mgr.isEnabled()); assertEquals("groupOne", mgr.getBuddyPoolName()); assertEquals(NextMemberBuddyLocator.class, mgr.buddyLocator.getClass()); // since the properties are not passed on to the next member buddy locator - they were obviously meant for a different impl. NextMemberBuddyLocatorConfig blc = (NextMemberBuddyLocatorConfig) mgr.buddyLocator.getConfig(); assertEquals(1, blc.getNumBuddies()); assertTrue(blc.isIgnoreColocatedBuddies()); } /** * Constructs a disabled buddy manager * * @throws Exception */ public void testConstruction3() throws Exception { String xmlConfig = "false"; Element element = XmlHelper.stringToElement(xmlConfig); BuddyReplicationConfig config = XmlConfigurationParser.parseBuddyReplicationConfig(element); BuddyManager mgr = new BuddyManager(config); assertTrue(!mgr.isEnabled()); } /** * Constructs a buddy manager using a minimal config set * * @throws Exception */ public void testConstruction4() throws Exception { String xmlConfig = "true"; Element element = XmlHelper.stringToElement(xmlConfig); BuddyReplicationConfig config = XmlConfigurationParser.parseBuddyReplicationConfig(element); BuddyManager mgr = new BuddyManager(config); assertTrue(mgr.isEnabled()); assertNull(mgr.getBuddyPoolName()); assertEquals(NextMemberBuddyLocator.class, mgr.buddyLocator.getClass()); NextMemberBuddyLocatorConfig blc = (NextMemberBuddyLocatorConfig) mgr.buddyLocator.getConfig(); assertEquals(1, blc.getNumBuddies()); assertTrue(blc.isIgnoreColocatedBuddies()); } private BuddyManager createBasicBuddyManager() { BuddyManager bm = null; try { Element element = XmlHelper.stringToElement("true"); BuddyReplicationConfig cfg = XmlConfigurationParser.parseBuddyReplicationConfig(element); bm = new BuddyManager(cfg); bm.injectDependencies(null, null, null, null, null, null, null, null, new BuddyFqnTransformer()); CommandsFactory commandsFactory = new CommandsFactory(); commandsFactory.initialize(null, null, null, null, null, null, null, new Configuration(), null, new BuddyFqnTransformer()); bm.initFqnTransformer(DUMMY_LOCAL_ADDRESS, commandsFactory); } catch (Exception e) { e.printStackTrace(); } return bm; } public void testFqnManipulation() { Fqn fqn1 = Fqn.fromString("/hello/world"); PutKeyValueCommand call1 = new PutKeyValueCommand(null, fqn1, "key", "value"); ReplicateCommand call2 = new ReplicateCommand(call1); BuddyManager bm = createBasicBuddyManager(); ReplicateCommand newReplicatedCall = bm.transformReplicateCommand(call2); PutKeyValueCommand newPutCall = (PutKeyValueCommand) newReplicatedCall.getSingleModification(); // should use object refs to transform the original MethodCall. Fqn expected = Fqn.fromString("/" + BuddyManager.BUDDY_BACKUP_SUBTREE + "/" + DUMMY_LOCAL_ADDRESS + "/hello/world"); assertEquals(expected, newPutCall.getFqn()); } public void testRootFqnManipulation() { Fqn fqn1 = Fqn.ROOT; ReplicableCommand call1 = new PutKeyValueCommand(null, fqn1, "key", "value"); ReplicateCommand call2 = new ReplicateCommand(call1); BuddyManager bm = createBasicBuddyManager(); ReplicateCommand newReplicatedCall = bm.transformReplicateCommand(call2); PutKeyValueCommand newPutCall = (PutKeyValueCommand) newReplicatedCall.getSingleModification(); // should use object refs to transform the original MethodCall. Fqn expected = Fqn.fromString("/" + BuddyManager.BUDDY_BACKUP_SUBTREE + "/" + DUMMY_LOCAL_ADDRESS); assertEquals(expected, newPutCall.getFqn()); } public void testMultiFqnManipulation() { Fqn fqn1 = Fqn.ROOT; Fqn fqn2 = Fqn.fromString("/hello/world"); Fqn fqn3 = Fqn.fromString("/hello/again"); Fqn fqn4 = Fqn.fromString("/buddy/replication"); PutKeyValueCommand call1 = new PutKeyValueCommand(null, fqn1, "key", "value"); PutKeyValueCommand call2 = new PutKeyValueCommand(null, fqn2, "key", "value"); PutKeyValueCommand call3 = new PutKeyValueCommand(null, fqn3, "key", "value"); PutKeyValueCommand call4 = new PutKeyValueCommand(null, fqn4, "key", "value"); List list = new ArrayList(); list.add(call1); list.add(call2); list.add(call3); list.add(call4); ReplicateCommand call5 = new ReplicateCommand(list); BuddyManager bm = createBasicBuddyManager(); ReplicateCommand newReplicatedCall = bm.transformReplicateCommand(call5); List l = newReplicatedCall.getModifications(); // should use object refs to transform the original MethodCall. String expected = "/" + BuddyManager.BUDDY_BACKUP_SUBTREE + "/" + DUMMY_LOCAL_ADDRESS; int i = 0; assertEquals(Fqn.fromString(expected), ((PutKeyValueCommand) l.get(i++)).getFqn()); assertEquals(Fqn.fromString(expected + "/hello/world"), ((PutKeyValueCommand) l.get(i++)).getFqn()); assertEquals(Fqn.fromString(expected + "/hello/again"), ((PutKeyValueCommand) l.get(i++)).getFqn()); assertEquals(Fqn.fromString(expected + "/buddy/replication"), ((PutKeyValueCommand) l.get(i)).getFqn()); } public void testGetActualFqn() { fqnTransformer = new BuddyFqnTransformer(); Fqn x = Fqn.fromString("/x"); Fqn backup = fqnTransformer.getBackupFqn("y", x); assertEquals(x, fqnTransformer.getActualFqn(backup)); } } ././@LongLink0000000000000000000000000000016100000000000011563 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/buddyreplication/BuddyReplicationWithCacheLoaderTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/buddyreplication/BuddyReplicationWithCacheLoa0000644000175000017500000003346611024205072033402 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.buddyreplication; import org.jboss.cache.Cache; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.EvictionConfig; import org.jboss.cache.config.EvictionRegionConfig; import org.jboss.cache.eviction.LRUConfiguration; import org.jboss.cache.loader.CacheLoader; import static org.jboss.cache.util.TestingUtil.dumpCacheContents; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertTrue; import org.testng.annotations.Test; import java.util.ArrayList; import java.util.List; import java.util.Map; /** * Tests use of the data gravitator alongside other cache loaders as well as data gravitator options such as removeOnFind. * * @author Manik Surtani (manik@jboss.org) */ @Test(groups = "functional") public class BuddyReplicationWithCacheLoaderTest extends BuddyReplicationTestsBase { protected Fqn fqn = Fqn.fromString("/test/br/four/level"); protected String key = "key"; protected String value = "value"; protected boolean passivation = false; private CacheLoader[] getLoaders(List> caches) { CacheLoader[] retVal = new CacheLoader[caches.size()]; for (int i = 0; i < retVal.length; i++) { retVal[i] = caches.get(i).getCacheLoaderManager().getCacheLoader(); } return retVal; } public void testWithDataGravitationDefault() throws Exception { dataGravitationDefaultTest(true); } public void testWithDataGravitationDefaultNoAuto() throws Exception { dataGravitationDefaultTest(false); } private void dataGravitationDefaultTest(boolean autoGravitate) throws Exception { // create 3 caches caches = createCachesWithCacheLoader(3, autoGravitate, true, passivation); TestingUtil.sleepThread(1000); CacheLoader[] loaders = getLoaders(caches); // cleanup for (int i = 0; i < 3; i++) loaders[i].remove(Fqn.ROOT); for (int i = 0; i < 3; i++) System.out.println("Loader " + i + ": " + loaders[i].get(fqn)); // put stuff in cache0 caches.get(0).put(fqn, key, value); // make sure there are no locks. assertNoLocks(caches); for (int i = 0; i < 3; i++) System.out.println("Loader " + i + ": " + loaders[i].get(fqn)); dumpCacheContents(caches); // request data from cache2 if (!autoGravitate) caches.get(2).getInvocationContext().getOptionOverrides().setForceDataGravitation(true); // should cause a gravitation event assertEquals(value, caches.get(2).get(fqn, key)); cleanupDelay(); // gravitation cleanup is async!! assertNoLocks(caches); for (int i = 0; i < 3; i++) System.out.println("Loader " + i + ": " + loaders[i].get(fqn)); dumpCacheContents(caches); // test that data does not exist in cache0 assertTrue("should not exist in cache0", !caches.get(0).exists(fqn)); // test that data does not exist in cache1 assertTrue("should not exist in cache1", !caches.get(1).exists(fqn)); // test that data does exist in cache2 assertTrue("should exist in cache2", caches.get(2).exists(fqn)); // test that data does not exist in loader0 assertTrue("should not exist in loader0", !loaders[0].exists(fqn)); // test that data does not exist in loader1 assertTrue("should not exist in loader1", !loaders[1].exists(fqn)); // test that data does exist in loader2 assertTrue("should exist in loader2", passivation ? !loaders[2].exists(fqn) : loaders[2].exists(fqn)); Fqn b1 = fqnTransformer.getBackupFqn(caches.get(0).getLocalAddress(), fqn); Fqn b2 = fqnTransformer.getBackupFqn(caches.get(2).getLocalAddress(), fqn); // test that bkup does exist in cache0 assertTrue("should not exist in cache0", !caches.get(0).exists(b1)); assertTrue("should exist in cache0", caches.get(0).exists(b2)); // test that bkup does not exist in cache1 assertTrue("should not exist in cache1", !caches.get(1).exists(b1)); assertTrue("should not exist in cache1", !caches.get(1).exists(b2)); // test that bkup does not exist in cache2 assertTrue("should not exist in cache2", !caches.get(2).exists(b1)); assertTrue("should not exist in cache2", !caches.get(2).exists(b2)); // test that bkup does exist in loader0 assertTrue("should not exist in loader0", !loaders[0].exists(b1)); assertTrue("should exist in loader0", passivation ? !loaders[0].exists(b2) : loaders[0].exists(b2)); // test that bkup does not exist in loader1 assertTrue("should not exist in loaders1", !loaders[1].exists(b1)); assertTrue("should not exist in loaders1", !loaders[1].exists(b2)); // test that bkup does not exist in loader2 assertTrue("should not exist in loaders2", !loaders[2].exists(b1)); assertTrue("should not exist in loaders2", !loaders[2].exists(b2)); } /** * Tests data gravitation when "removeOnFind=false"; i.e. nodes * from which data is gravitated evict it instead of removing it. * * @throws Exception */ public void testWithDataGravitationEvictOnFind() throws Exception { dataGravitationEvictionTest(true); } /** * Tests data gravitation when auto-gravitation is disabled and * "removeOnFind=false"; i.e. nodes from which data is gravitated * evict it instead of removing it. * * @throws Exception */ public void testWithDataGravitationEvictOnFindNoAuto() throws Exception { dataGravitationEvictionTest(false); } private void dataGravitationEvictionTest(boolean autoGravitate) throws Exception { // create 3 caches caches = createCachesWithCacheLoader(3, autoGravitate, false, passivation); CacheLoader[] loaders = getLoaders(caches); Fqn b1 = fqnTransformer.getBackupFqn(caches.get(0).getLocalAddress(), fqn); Fqn b2 = fqnTransformer.getBackupFqn(caches.get(2).getLocalAddress(), fqn); // put stuff in cache0 caches.get(0).put(fqn, key, value); Map m = loaders[1].get(b1); System.out.println("From loader: " + m); System.out.println("*** About do cause a gravitation event ... "); // request data from cache2 if (!autoGravitate) caches.get(2).getInvocationContext().getOptionOverrides().setForceDataGravitation(true); // should cause a gravitation event assertEquals(value, caches.get(2).get(fqn, key)); cleanupDelay(); // gravitation is async!! // test that data does not exist in cache0 assertTrue("should not exist in cache0", !caches.get(0).exists(fqn)); // test that data does not exist in cache1 assertTrue("should not exist in cache1", !caches.get(1).exists(fqn)); // test that data does exist in cache2 assertTrue("should exist in cache2", caches.get(2).exists(fqn)); // test that data does exist in loader0 assertTrue("should exist in loader0", loaders[0].exists(fqn)); // test that data does not exist in loader1 assertTrue("should not exist in loader1", !loaders[1].exists(fqn)); // test that data does exist in loader2 assertTrue("should exist in loader2", passivation ? !loaders[2].exists(fqn) : loaders[2].exists(fqn)); // test that bkup does exist in cache0 assertTrue("should not exist in cache0", !caches.get(0).exists(b1)); assertTrue("should exist in cache0", caches.get(0).exists(b2)); // test that bkup does not exist in cache1 assertTrue("should not exist in cache1", !caches.get(1).exists(b1)); assertTrue("should not exist in cache1", !caches.get(1).exists(b2)); // test that bkup does not exist in cache2 assertTrue("should not exist in cache2", !caches.get(2).exists(b1)); assertTrue("should not exist in cache2", !caches.get(2).exists(b2)); // test that bkup does exist in loader0 assertTrue("should not exist in loader0", !loaders[0].exists(b1)); assertTrue("should exist in loader0", passivation ? !loaders[0].exists(b2) : loaders[0].exists(b2)); // test that bkup does not exist in loader1 assertTrue("should exist in loaders1", loaders[1].exists(b1)); assertTrue("should not exist in loaders1", !loaders[1].exists(b2)); // test that bkup does not exist in loader2 assertTrue("should not exist in loaders2", !loaders[2].exists(b1)); assertTrue("should not exist in loaders2", !loaders[2].exists(b2)); } /** * Tests whether nodes that have been evicted can successfully be * gravitated. * * @throws Exception */ public void testLocalGravitationOfEvictedNodes() throws Exception { CacheSPI cache1 = createCacheWithCacheLoader(true, true, passivation, true, false); Configuration cfg1 = cache1.getConfiguration(); configureEviction(cfg1); Configuration cfg0 = cfg1.clone(); CacheSPI cache0 = (CacheSPI) new DefaultCacheFactory().createCache(cfg0, false); // Store them for the teardown method if (caches == null) caches = new ArrayList>(); caches.add(cache0); caches.add(cache1); cache0.start(); cache1.start(); TestingUtil.blockUntilViewsReceived(caches.toArray(new Cache[caches.size()]), VIEW_BLOCK_TIMEOUT * caches.size()); TestingUtil.sleepThread(getSleepTimeout()); Fqn foo = Fqn.fromString("/foo"); Fqn backupFoo = fqnTransformer.getBackupFqn(cache0.getLocalAddress(), foo); cache0.put(foo, "key", "value"); assert cache0.exists(foo) : "Data should exist in data owner"; assert cache1.exists(backupFoo) : "Buddy should have data"; // Sleep long enough for eviction to run twice plus a bit TestingUtil.sleepThread(3050); // test that the data we're looking for has been evicted in both the data owner and the buddy. assert !cache0.exists(foo) : "Data should have evicted in data owner"; assert !cache1.exists(backupFoo) : "Buddy should have data evicted"; // now test that this exists in both loaders. assert cache0.getCacheLoaderManager().getCacheLoader().get(foo) != null : "Should exist in data owner's cache loader"; assert cache1.getCacheLoaderManager().getCacheLoader().get(backupFoo) != null : "Should exist in buddy's loader"; // a local gravitation should occur since cache1 has foo in it's backup tree. assertEquals("Passivated value available from buddy", "value", cache1.get(foo, "key")); } /** * Tests whether nodes that have been evicted can successfully be * gravitated. * * @throws Exception */ public void testRemoteGravitationOfEvictedNodes() throws Exception { CacheSPI cache0 = createCacheWithCacheLoader(true, true, passivation, true, false); Configuration cfg0 = cache0.getConfiguration(); configureEviction(cfg0); Configuration cfg1 = cfg0.clone(); CacheSPI cache1 = (CacheSPI) new DefaultCacheFactory().createCache(cfg1, false); Configuration cfg2 = cfg0.clone(); CacheSPI cache2 = (CacheSPI) new DefaultCacheFactory().createCache(cfg2, false); // Store them for the teardown method if (caches == null) caches = new ArrayList>(); caches.add(cache0); caches.add(cache1); caches.add(cache2); cache0.start(); cache1.start(); cache2.start(); TestingUtil.blockUntilViewsReceived(caches.toArray(new Cache[caches.size()]), 600000); TestingUtil.sleepThread(getSleepTimeout()); assert (cache0.getBuddyManager().getBuddyAddresses().contains(cache1.getLocalAddress())) : "Cache1 should be cache0's buddy!"; Fqn foo = Fqn.fromString("/foo"); Fqn backupFoo = fqnTransformer.getBackupFqn(cache0.getLocalAddress(), foo); cache0.put(foo, "key", "value"); // test that the data exists in both the data owner and the buddy assert cache0.exists(foo) : "Data should exist in data owner"; assert cache1.exists(backupFoo) : "Buddy should have data"; // Sleep long enough for eviction to run twice plus a bit TestingUtil.sleepThread(3050); // test that the data we're looking for has been evicted in both the data owner and the buddy. assert !cache0.exists(foo) : "Data should have evicted in data owner"; assert !cache1.exists(backupFoo) : "Buddy should have data evicted"; // now test that this exists in both loaders. assert cache0.getCacheLoaderManager().getCacheLoader().get(foo) != null : "Should exist in data owner's loader"; assert cache1.getCacheLoaderManager().getCacheLoader().get(backupFoo) != null : "Should exist in buddy's loader"; // doing a get on cache2 will guarantee a remote data gravitation. assertEquals("Passivated value available from buddy", "value", cache2.get(foo, "key")); } private void configureEviction(Configuration cfg) { EvictionConfig ec = new EvictionConfig(); ec.setWakeupIntervalSeconds(1); EvictionRegionConfig erc = new EvictionRegionConfig(); erc.setRegionName("/_default_"); LRUConfiguration epc = new LRUConfiguration(); epc.setMaxAgeSeconds(2); epc.setTimeToLiveSeconds(1); epc.setMaxNodes(1); erc.setEvictionPolicyConfig(epc); List ercs = new ArrayList(); ercs.add(erc); ec.setEvictionRegionConfigs(ercs); cfg.setEvictionConfig(ec); } private void cleanupDelay() { TestingUtil.sleepThread(250); } } ././@LongLink0000000000000000000000000000016100000000000011563 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/buddyreplication/BuddyReplicationWithPassivationTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/buddyreplication/BuddyReplicationWithPassivat0000644000175000017500000000077010731261227033535 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.buddyreplication; import org.testng.annotations.Test; /** * @author Manik Surtani (manik@jboss.org) */ @Test(groups = "functional") public class BuddyReplicationWithPassivationTest extends BuddyReplicationWithCacheLoaderTest { public BuddyReplicationWithPassivationTest() { passivation = true; } } ././@LongLink0000000000000000000000000000015100000000000011562 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/buddyreplication/BuddyReplicationContentTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/buddyreplication/BuddyReplicationContentTest.0000644000175000017500000002765211024205072033437 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.buddyreplication; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.notifications.annotation.CacheBlocked; import org.jboss.cache.notifications.annotation.CacheListener; import org.jboss.cache.notifications.annotation.CacheUnblocked; import org.jboss.cache.notifications.event.Event; import org.jboss.cache.util.CachePrinter; import static org.testng.AssertJUnit.*; import org.testng.annotations.Test; import java.util.ArrayList; import java.util.List; /** * Teststhe transfer of content under *normal* operation * * @author Manik Surtani (manik@jboss.org) */ @Test(groups = {"functional"}) public class BuddyReplicationContentTest extends BuddyReplicationTestsBase { private String key = "key"; private String value = "value"; private Log log = LogFactory.getLog(BuddyGroupAssignmentTest.class); BuddyFqnTransformer fqnTransformer = new BuddyFqnTransformer(); private void assertNoStaleLocks(List> caches) { for (CacheSPI cache : caches) assertNoStaleLocks(cache); } private void assertNoStaleLocks(CacheSPI cache) { assertEquals("Number of locks in cache instance " + cache + " should be 0", 0, cache.getNumberOfLocksHeld()); } public void testSimplePut() throws Exception { log.debug("Running testSimplePut"); caches = createCaches(3, false); String fqn = "/test"; String backupFqn = "/" + BuddyManager.BUDDY_BACKUP_SUBTREE + "/" + fqnTransformer.getGroupNameFromAddress(caches.get(0).getLocalAddress()) + fqn; assertNoStaleLocks(caches); // put something in cache 1 caches.get(0).put(fqn, key, value); assertNoStaleLocks(caches); // this should be in neither of the other caches' "main" trees assertEquals(value, caches.get(0).get(fqn, key)); assertNull("Should be null", caches.get(1).get(fqn, key)); assertNull("Should be null", caches.get(2).get(fqn, key)); // check the backup trees assertEquals("Buddy should have data in backup tree", value, caches.get(1).get(backupFqn, key)); assertNull("Should be null", caches.get(2).get(backupFqn, key)); assertNoStaleLocks(caches); System.out.println("Cache 0 = " + CachePrinter.printCacheLockingInfo(caches.get(0))); System.out.println("Cache 1 = " + CachePrinter.printCacheLockingInfo(caches.get(1))); System.out.println("Cache 2 = " + CachePrinter.printCacheLockingInfo(caches.get(2))); } public void testPutAndRemove() throws Exception { log.debug("Running testPutAndRemove"); caches = createCaches(3, false); String fqn = "/test"; String backupFqn = "/" + BuddyManager.BUDDY_BACKUP_SUBTREE + "/" + fqnTransformer.getGroupNameFromAddress(caches.get(0).getLocalAddress()) + fqn; assertNoStaleLocks(caches); // put something in cache 1 caches.get(0).put(fqn, key, value); assertNoStaleLocks(caches); // this should be in neither of the other caches' "main" trees assertEquals(value, caches.get(0).get(fqn, key)); assertNull("Should be null", caches.get(1).get(fqn, key)); assertNull("Should be null", caches.get(2).get(fqn, key)); // check the backup trees assertEquals("Buddy should have data in backup tree", value, caches.get(1).get(backupFqn, key)); assertNull("Should be null", caches.get(2).get(backupFqn, key)); assertNoStaleLocks(caches); // now remove caches.get(0).removeNode(fqn); assertNoStaleLocks(caches); assertNull("Should be null", caches.get(0).get(fqn, key)); assertNull("Should be null", caches.get(1).get(fqn, key)); assertNull("Should be null", caches.get(2).get(fqn, key)); // check the backup trees assertNull("Should be null", caches.get(0).get(backupFqn, key)); assertNull("Should be null", caches.get(1).get(backupFqn, key)); assertNull("Should be null", caches.get(2).get(backupFqn, key)); assertNoStaleLocks(caches); } public void testPutAndRemove2() throws Exception { log.debug("Running testPutAndRemove2"); caches = createCaches(2, 4, false); String fqn = "/test"; String backupFqn = "/" + BuddyManager.BUDDY_BACKUP_SUBTREE + "/" + fqnTransformer.getGroupNameFromAddress(caches.get(0).getLocalAddress()) + fqn; // put something in cache 1 assertNoStaleLocks(caches); caches.get(0).put(fqn, key, value); assertNoStaleLocks(caches); // this should be in neither of the other caches' "main" trees assertEquals(value, caches.get(0).get(fqn, key)); assertNull("Should be null", caches.get(1).get(fqn, key)); assertNull("Should be null", caches.get(2).get(fqn, key)); assertNull("Should be null", caches.get(3).get(fqn, key)); // check the backup trees assertEquals("Buddy should have data in backup tree", value, caches.get(1).get(backupFqn, key)); assertEquals("Buddy should have data in backup tree", value, caches.get(2).get(backupFqn, key)); assertNull("Should be null", caches.get(3).get(backupFqn, key)); assertNoStaleLocks(caches); // now remove caches.get(0).removeNode(fqn); assertNoStaleLocks(caches); assertNull("Should be null", caches.get(0).get(fqn, key)); assertNull("Should be null", caches.get(1).get(fqn, key)); assertNull("Should be null", caches.get(2).get(fqn, key)); assertNull("Should be null", caches.get(3).get(fqn, key)); // check the backup trees assertNull("Should be null", caches.get(0).get(backupFqn, key)); assertNull("Should be null", caches.get(1).get(backupFqn, key)); assertNull("Should be null", caches.get(2).get(backupFqn, key)); assertNull("Should be null", caches.get(3).get(backupFqn, key)); assertNoStaleLocks(caches); } public void testBuddyJoin() throws Exception { log.debug("Running testBuddyJoin"); caches = createCaches(2, false); CacheSPI cache2 = null; try { Fqn fqn = Fqn.fromString("/test"); Fqn backupFqn = fqnTransformer.getBackupFqn(caches.get(1).getLocalAddress(), fqn); assertNoStaleLocks(caches); // put something in cache 1 caches.get(1).put(fqn, key, value); assertNoStaleLocks(caches); // this should be in neither of the other caches' "main" trees assertEquals(value, caches.get(1).get(fqn, key)); assertFalse("Should be false", caches.get(0).exists(fqn)); // check the backup trees assertEquals("Buddy should have data in backup tree", value, caches.get(0).get(backupFqn, key)); assertNoStaleLocks(caches); // now add a new cache to the cluster cache2 = createCache(1, null); // allow this cache a few msecs to join TestingUtil.blockUntilViewsReceived(3000, caches.get(0), caches.get(1), cache2); TestingUtil.sleepThread(2000); // allow buddy group reorg List> dump = new ArrayList>(caches); dump.add(cache2); TestingUtil.dumpCacheContents(dump); // now caches.get(1)'s buddy should be cache2, not cache[0] assertIsBuddy(caches.get(1), cache2, true); // this should still be the same assertIsBuddy(caches.get(0), caches.get(1), true); // and cache2's buddy should be cache[0] assertIsBuddy(cache2, caches.get(0), true); // so now the backup data we saw on cache[0] should have been removed. assertFalse("This backup data should have been removed", caches.get(0).exists(backupFqn)); // while cache2 should now posess this backup (due to a state transfer) assertEquals("Backup state should have been transferred to this new cache instance", value, cache2.get(backupFqn, key)); caches.get(1).removeNode(fqn); assertNoStaleLocks(caches); assertFalse("Should be null", caches.get(0).exists(fqn)); assertFalse("Should be null", caches.get(1).exists(fqn)); assertFalse("Should be null", cache2.exists(fqn)); // check the backup trees assertFalse("Should be null", caches.get(0).exists(backupFqn)); assertFalse("Should be null", caches.get(1).exists(backupFqn)); assertFalse("Should be null", cache2.exists(backupFqn)); assertNoStaleLocks(caches); } finally { if (cache2 != null) cache2.stop(); } } //@Test (invocationCount = 25) public void testCompleteStateSurvival() throws Exception { log.debug("Running testCompleteStateSurvival"); caches = null; caches = createCaches(3, false, true); CacheBlockListener blockListener = new CacheBlockListener(); caches.get(0).addCacheListener(blockListener); caches.get(1).addCacheListener(blockListener); caches.get(2).addCacheListener(blockListener); caches.get(0).put("/0", "key", "value"); caches.get(1).put("/1", "key", "value"); caches.get(2).put("/2", "key", "value"); // TestingUtil.sleepThread(getSleepTimeout()); //TestingUtil.sleepThread(caches.get(0).getConfiguration().getStateRetrievalTimeout() * 3); blockListener.blockUntilAllCachesAreUnblocked(caches.get(0).getConfiguration().getStateRetrievalTimeout() * 3); log.info("stopping 2"); caches.get(2).stop(); blockListener.blockUntilAllCachesAreUnblocked(caches.get(0).getConfiguration().getStateRetrievalTimeout() * 5); assertEquals("value", caches.get(0).get("/2", "key")); blockListener.blockUntilAllCachesAreUnblocked(caches.get(0).getConfiguration().getStateRetrievalTimeout() * 5); caches.get(1).stop(); // cache[0] is all thats left!! assertEquals("value", caches.get(0).get("/0", "key")); try { assertEquals("value", caches.get(0).get("/1", "key")); } catch (RuntimeException e) { // may barf the first time around since we are unable to contact our buddy and store this data. assertEquals(IllegalArgumentException.class, e.getCause().getClass()); } // now try the assertion again since the local gravitation would have worked. System.out.println("Cache contents " + CachePrinter.printCacheDetails(caches.get(0))); assertEquals("value", caches.get(0).get("/1", "key")); assertEquals("value", caches.get(0).get("/2", "key")); } @CacheListener public static class CacheBlockListener { private int blocks = 0; @CacheBlocked public void processBlock(Event e) { if (e.isPre()) { System.out.println(">>>>>>>> Got BLOCK on cache " + e.getCache().getLocalAddress()); synchronized (this) { blocks++; notifyAll(); } } } @CacheUnblocked public void processUnblock(Event e) { if (e.isPre()) { System.out.println(">>>>>>>> Got UNBLOCK on cache " + e.getCache().getLocalAddress()); synchronized (this) { blocks--; notifyAll(); } } } public void blockUntilAllCachesAreUnblocked(long maxWait) throws InterruptedException { synchronized (this) { if (blocks > 1) { wait(maxWait); } // try returning anyway? blocks = 0; /* if (blocks > 1) throw new RuntimeException("Timed out waiting for unblocks. Number of blocks = " + blocks); if (blocks == 1) blocks = 0; */ } } } } ././@LongLink0000000000000000000000000000015000000000000011561 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/buddyreplication/BuddyReplicationConfigTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/buddyreplication/BuddyReplicationConfigTest.j0000644000175000017500000001160111005354216033373 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.buddyreplication; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.config.BuddyReplicationConfig; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.XmlConfigurationParser; import org.jboss.cache.interceptors.DataGravitatorInterceptor; import org.jboss.cache.interceptors.base.CommandInterceptor; import org.jboss.cache.xml.XmlHelper; import static org.testng.AssertJUnit.*; import org.testng.annotations.AfterMethod; import org.testng.annotations.Test; import org.w3c.dom.Element; /** * Tests basic configuration options by passing stuff into the CacheImpl. * * @author Manik Surtani (manik@jboss.org) */ @Test(groups = {"functional", "jgroups"}) public class BuddyReplicationConfigTest { private CacheSPI cache; @AfterMethod(alwaysRun = true) public void tearDown() { cache.stop(); cache = null; } public void testNullConfig() throws Exception { cache = (CacheSPI) new DefaultCacheFactory().createCache(false); cache.getConfiguration().setBuddyReplicationConfig(null); assertNull(cache.getBuddyManager()); } public void testDisabledConfig() throws Exception { String xmlConfig = "false"; Element element = XmlHelper.stringToElement(xmlConfig); BuddyReplicationConfig config = XmlConfigurationParser.parseBuddyReplicationConfig(element); cache = (CacheSPI) new DefaultCacheFactory().createCache(false); cache.getConfiguration().setBuddyReplicationConfig(config); assertNull(cache.getBuddyManager()); } public void testBasicConfig() throws Exception { String xmlConfig = "true"; Element element = XmlHelper.stringToElement(xmlConfig); BuddyReplicationConfig config = XmlConfigurationParser.parseBuddyReplicationConfig(element); cache = (CacheSPI) new DefaultCacheFactory().createCache(false); cache.getConfiguration().setCacheMode(Configuration.CacheMode.REPL_SYNC); cache.getConfiguration().setBuddyReplicationConfig(config); cache.create(); cache.start(); assertNotNull(cache.getBuddyManager()); BuddyManager mgr = cache.getBuddyManager(); assertTrue(mgr.isEnabled()); assertNull(mgr.getBuddyPoolName()); assertEquals(NextMemberBuddyLocator.class, mgr.buddyLocator.getClass()); NextMemberBuddyLocatorConfig blc = (NextMemberBuddyLocatorConfig) mgr.buddyLocator.getConfig(); assertEquals(1, blc.getNumBuddies()); assertTrue(blc.isIgnoreColocatedBuddies()); } public void testXmlConfig() throws Exception { cache = (CacheSPI) new DefaultCacheFactory().createCache(new XmlConfigurationParser().parseFile("META-INF/buddy-replication-cache-service.xml"), false); cache.create(); cache.start(); BuddyManager bm = cache.getBuddyManager(); assertNotNull(bm); assertTrue(bm.isEnabled()); assertTrue(bm.buddyLocator instanceof NextMemberBuddyLocator); NextMemberBuddyLocator bl = (NextMemberBuddyLocator) bm.buddyLocator; NextMemberBuddyLocatorConfig blc = (NextMemberBuddyLocatorConfig) bl.getConfig(); assertTrue(blc.isIgnoreColocatedBuddies()); assertEquals(1, blc.getNumBuddies()); assertEquals("myBuddyPoolReplicationGroup", bm.getConfig().getBuddyPoolName()); assertEquals(2000, bm.getConfig().getBuddyCommunicationTimeout()); // test Data Gravitator boolean hasDG = false; for (CommandInterceptor interceptor : cache.getInterceptorChain()) { hasDG = hasDG || (interceptor instanceof DataGravitatorInterceptor); } System.out.println(cache.getInterceptorChain()); assertTrue("Should have a data gravitator!!", hasDG); } public void testLocalModeConfig() throws Exception { String xmlConfig = "true"; Element element = XmlHelper.stringToElement(xmlConfig); BuddyReplicationConfig config = XmlConfigurationParser.parseBuddyReplicationConfig(element); cache = (CacheSPI) new DefaultCacheFactory().createCache(false); cache.getConfiguration().setBuddyReplicationConfig(config); cache.create(); cache.start(); assert cache.getBuddyManager() == null; cache.getInvocationContext().getOptionOverrides().setForceDataGravitation(true); cache.getNode(Fqn.fromString("/nonexistent")); // should not barf! } }jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/buddyreplication/RemoveRootBuddyTest.java0000644000175000017500000000215110777521744032606 0ustar twernertwernerpackage org.jboss.cache.buddyreplication; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.testng.annotations.Test; import java.util.ArrayList; /** * Test added to replicate a found issue. When fixing it consider moving test to some other class. * * @author Mircea.Markus@jboss.com * @since 2.1 */ @Test(groups = {"functional"}) public class RemoveRootBuddyTest extends BuddyReplicationTestsBase { public void testRemoveRootNode() throws Exception { CacheSPI cache1 = createCache(false, 1, "myBuddyPoolReplicationGroup", false, true, true); CacheSPI cache2 = createCache(false, 1, "myBuddyPoolReplicationGroup", false, true, true); caches = new ArrayList>(2); caches.add(cache1); caches.add(cache2); int opCount = 10; for (int i = 0; i < opCount; i++) { String key = String.valueOf(opCount); String value = String.valueOf(opCount); Fqn f = Fqn.fromElements("test", key); cache1.put(f, key, value); } cache1.removeNode(Fqn.ROOT); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/buddyreplication/GravitationCleanupTest.java0000644000175000017500000002002511024205072033267 0ustar twernertwernerpackage org.jboss.cache.buddyreplication; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.util.CachePrinter; import org.testng.annotations.Test; /** * @author Manik Surtani (manik@jboss.org) * @since 2.1.0 */ @Test(groups = "functional") public class GravitationCleanupTest extends BuddyReplicationTestsBase { Fqn fqn = Fqn.fromString("/a/b/c"); Object key = "key", value = "value"; BuddyFqnTransformer fqnTransformer = new BuddyFqnTransformer(); public void testStaleRegionOnDataOwnerPessimistic() throws Exception { testDataOwner(false); } public void testStaleRegionOnDataOwnerOptimistic() throws Exception { testDataOwner(true); } public void testStaleRegionOnBuddyPessimistic() throws Exception { testBuddy(false); } public void testStaleRegionOnBuddyOptimistic() throws Exception { testBuddy(true); } private void testDataOwner(boolean optimistic) throws Exception { caches = createCaches(1, 2, false, true, optimistic); // add some stuff on the primary CacheSPI dataOwner = caches.get(0); CacheSPI buddy = caches.get(1); dataOwner.put(fqn, key, value); System.out.println("dataOwner: " + CachePrinter.printCacheLockingInfo(dataOwner)); System.out.println("buddy: " + CachePrinter.printCacheLockingInfo(buddy)); assert dataOwner.peek(fqn, false) != null : "Should have data"; assert dataOwner.peek(Fqn.fromRelativeElements(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN, fqnTransformer.getGroupNameFromAddress(buddy.getLocalAddress())), false) != null : "Should have backup node for buddy"; assert dataOwner.peek(Fqn.fromRelativeElements(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN, fqnTransformer.getGroupNameFromAddress(dataOwner.getLocalAddress())), false) == null : "Should NOT have backup node for self!"; assert buddy.peek(fqn, false) == null : "Should not have data"; assert buddy.peek(Fqn.fromRelativeElements(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN, fqnTransformer.getGroupNameFromAddress(buddy.getLocalAddress())), false) == null : "Should NOT have backup node for self!"; assert buddy.peek(Fqn.fromRelativeElements(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN, fqnTransformer.getGroupNameFromAddress(dataOwner.getLocalAddress())), false) != null : "Should have backup node for buddy"; assert buddy.peek(fqnTransformer.getBackupFqn(dataOwner.getLocalAddress(), fqn), false) != null : "Should have backup data"; // now do a gravitate call. assert buddy.get(fqn, key).equals(value) : "Data should have gravitated!"; // gravitation cleanup calls are async. cleanupDelay(); System.out.println("dataOwner: " + CachePrinter.printCacheLockingInfo(dataOwner)); System.out.println("buddy: " + CachePrinter.printCacheLockingInfo(buddy)); assert buddy.peek(fqn, false) != null : "Should have data"; assert buddy.peek(Fqn.fromRelativeElements(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN, fqnTransformer.getGroupNameFromAddress(dataOwner.getLocalAddress())), false) != null : "Should have backup node for buddy"; assert buddy.peek(Fqn.fromRelativeElements(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN, fqnTransformer.getGroupNameFromAddress(buddy.getLocalAddress())), false) == null : "Should NOT have backup node for self!"; assert dataOwner.peek(fqn, false) == null : "Should not have data"; assert dataOwner.peek(Fqn.fromRelativeElements(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN, fqnTransformer.getGroupNameFromAddress(dataOwner.getLocalAddress())), false) == null : "Should NOT have backup node for self!"; assert dataOwner.peek(Fqn.fromRelativeElements(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN, fqnTransformer.getGroupNameFromAddress(buddy.getLocalAddress())), false) != null : "Should have backup node for buddy"; assert dataOwner.peek(fqnTransformer.getBackupFqn(buddy.getLocalAddress(), fqn), false) != null : "Should have backup data"; } private void testBuddy(boolean optimistic) throws Exception { caches = createCaches(1, 3, false, true, optimistic); // add some stuff on the primary CacheSPI dataOwner = caches.get(0); CacheSPI buddy = caches.get(1); CacheSPI thirdInstance = caches.get(2); assertIsBuddy(dataOwner, buddy, true); assertIsBuddy(buddy, thirdInstance, true); assertIsBuddy(thirdInstance, dataOwner, true); dataOwner.put(fqn, key, value); System.out.println("dataOwner: " + CachePrinter.printCacheLockingInfo(dataOwner)); System.out.println("buddy: " + CachePrinter.printCacheLockingInfo(buddy)); System.out.println("thirdInstance: " + CachePrinter.printCacheLockingInfo(thirdInstance)); assert dataOwner.peek(fqn, false) != null : "Should have data"; assert dataOwner.peek(Fqn.fromRelativeElements(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN, fqnTransformer.getGroupNameFromAddress(thirdInstance.getLocalAddress())), false) != null : "Should have backup node for buddy"; assert dataOwner.peek(Fqn.fromRelativeElements(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN, fqnTransformer.getGroupNameFromAddress(dataOwner.getLocalAddress())), false) == null : "Should NOT have backup node for self!"; assert dataOwner.peek(Fqn.fromRelativeElements(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN, fqnTransformer.getGroupNameFromAddress(buddy.getLocalAddress())), false) == null : "Should NOT have backup node for 2nd instance!"; assert buddy.peek(fqn, false) == null : "Should not have data"; assert buddy.peek(Fqn.fromRelativeElements(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN, fqnTransformer.getGroupNameFromAddress(buddy.getLocalAddress())), false) == null : "Should NOT have backup node for self!"; assert buddy.peek(Fqn.fromRelativeElements(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN, fqnTransformer.getGroupNameFromAddress(dataOwner.getLocalAddress())), false) != null : "Should have backup node for buddy"; assert buddy.peek(fqnTransformer.getBackupFqn(dataOwner.getLocalAddress(), fqn), false) != null : "Should have backup data"; // now do a gravitate call. assert thirdInstance.get(fqn, key).equals(value) : "Data should have gravitated!"; // gravitation cleanup calls are async. cleanupDelay(); System.out.println("dataOwner: " + CachePrinter.printCacheLockingInfo(dataOwner)); System.out.println("buddy: " + CachePrinter.printCacheLockingInfo(buddy)); System.out.println("thirdInstance: " + CachePrinter.printCacheLockingInfo(thirdInstance)); assert thirdInstance.peek(fqn, false) != null : "Should have data"; assert thirdInstance.peek(Fqn.fromRelativeElements(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN, fqnTransformer.getGroupNameFromAddress(buddy.getLocalAddress())), false) != null : "Should have backup node for buddy"; assert thirdInstance.peek(Fqn.fromRelativeElements(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN, fqnTransformer.getGroupNameFromAddress(thirdInstance.getLocalAddress())), false) == null : "Should NOT have backup node for self!"; assert dataOwner.peek(fqn, false) == null : "Should not have data"; assert dataOwner.peek(Fqn.fromRelativeElements(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN, fqnTransformer.getGroupNameFromAddress(dataOwner.getLocalAddress())), false) == null : "Should NOT have backup node for self!"; assert dataOwner.peek(Fqn.fromRelativeElements(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN, fqnTransformer.getGroupNameFromAddress(thirdInstance.getLocalAddress())), false) != null : "Should have backup node for buddy"; assert dataOwner.peek(fqnTransformer.getBackupFqn(thirdInstance.getLocalAddress(), fqn), false) != null : "Should have backup data"; assert buddy.peek(fqn, false) == null : "Should not have data"; assert buddy.peek(fqn.getParent(), false) == null : "Should not have any part of the data"; assert buddy.peek(fqnTransformer.getBackupFqn(dataOwner.getLocalAddress(), fqn), false) == null : "Should NOT have backup data"; } private void cleanupDelay() { TestingUtil.sleepThread(250); } } ././@LongLink0000000000000000000000000000014700000000000011567 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/buddyreplication/DisabledStateTransferTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/buddyreplication/DisabledStateTransferTest.ja0000644000175000017500000000514111024205072033360 0ustar twernertwernerpackage org.jboss.cache.buddyreplication; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.util.CachePrinter; import org.testng.annotations.Test; /** * This is to test JBCACHE-1229 * * @author Manik Surtani (manik@jboss.org) * @since 2.1.0 */ @Test(groups = "functional") public class DisabledStateTransferTest extends BuddyReplicationTestsBase { public void testCachesWithoutStateTransfer() throws Exception { caches = createCaches(1, 3, false, false, false, false); int cacheNumber = 0; for (CacheSPI c : caches) { c.getConfiguration().setFetchInMemoryState(false); c.start(); c.put("/" + cacheNumber++, "k", "v"); } TestingUtil.blockUntilViewsReceived(caches.toArray(new CacheSPI[0]), 60000); for (CacheSPI c : caches) System.out.println("Cache (local address " + c.getLocalAddress() + ") contents: " + CachePrinter.printCacheLockingInfo(c)); for (int i = 0; i < 3; i++) { int backupIndex = i == 2 ? 0 : i + 1; assert caches.get(i).exists("/" + i) : "Data should exist on owner (cache #" + i + ")"; Fqn backup = fqnTransformer.getBackupFqn(caches.get(i).getLocalAddress(), Fqn.fromString("/" + i)); assert caches.get(backupIndex).exists(backup.getParent()) : "Backup region should have been created on buddy (cache #" + backupIndex + ")"; boolean backupStatePropagated = caches.get(backupIndex).exists(backup); boolean backupOlderThanOwner = backupIndex < i; assert (!backupStatePropagated && !backupOlderThanOwner) || (backupStatePropagated && backupOlderThanOwner) : "Backup state should NOT have been transferred to buddy (cache #" + backupIndex + ")"; } // now NEW state should transfer just fine. cacheNumber = 0; for (CacheSPI c : caches) { c.put("/" + (cacheNumber++) + "_NEW", "k", "v"); } for (int i = 0; i < 3; i++) { int backupIndex = i == 2 ? 0 : i + 1; assert caches.get(i).exists("/" + i + "_NEW") : "Data should exist on owner (cache #" + i + ")"; Fqn backup = fqnTransformer.getBackupFqn(caches.get(i).getLocalAddress(), Fqn.fromString("/" + i + "_NEW")); assert caches.get(backupIndex).exists(backup.getParent()) : "Backup region should have been created on buddy (cache #" + backupIndex + ")"; assert caches.get(backupIndex).exists(backup) : "Backup state should NOT have been transferred to buddy (cache #" + backupIndex + ")"; } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/profiling/0000755000175000017500000000000011376173742024440 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/profiling/ProfileTest.java0000644000175000017500000003107611070703545027541 0ustar twernertwernerpackage org.jboss.cache.profiling; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.Fqn; import org.jboss.cache.config.BuddyReplicationConfig; import org.jboss.cache.config.Configuration; import org.jboss.cache.lock.IsolationLevel; import org.jboss.cache.transaction.DummyTransactionManagerLookup; import org.jboss.cache.util.TestingUtil; import org.testng.annotations.Test; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Random; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; /** * Test to use with a profiler to profile replication. To be used in conjunction with ProfileSlaveTest. *

* Typical usage pattern: *

* 1. Start a single test method in ProfileSlaveTest. This will block until you kill it. * 2. Start the corresponding test in this class, with the same name, in a different JVM, and attached to a profiler. * 3. Profile away! *

* * @author Manik Surtani (manik@jboss.org) * @since 2.1.0 */ @Test(groups = "profiling") public class ProfileTest extends AbstractProfileTest { /* Test configuration options */ protected static final long NUM_OPERATIONS = 1000000; // DURATION is replaced with a fixed number of operations instead. protected static final int NUM_THREADS = 25; protected static final int MAX_RANDOM_SLEEP_MILLIS = 1; protected static final int MAX_DEPTH = 3; protected static final int MAX_OVERALL_NODES = 50; protected static final int WARMUP_LOOPS = 20000; protected static final boolean USE_SLEEP = false; // throttle generation a bit protected static final Fqn BELAS_FQN = Fqn.fromString("/a/b/c"); protected static final String BELAS_KEY = "bela"; private List fqns = new ArrayList(MAX_OVERALL_NODES); private Random r = new Random(); Log log = LogFactory.getLog(ProfileTest.class); @Test(enabled = true) public void testLocalModePess() throws Exception { cache.getConfiguration().setCacheMode(Configuration.CacheMode.LOCAL); cache.getConfiguration().setNodeLockingScheme(Configuration.NodeLockingScheme.PESSIMISTIC); cache.getConfiguration().setIsolationLevel(IsolationLevel.REPEATABLE_READ); runCompleteTest(); } @Test(enabled = false) public void testLocalModeOpt() throws Exception { cache.getConfiguration().setCacheMode(Configuration.CacheMode.LOCAL); cache.getConfiguration().setNodeLockingScheme(Configuration.NodeLockingScheme.OPTIMISTIC); runCompleteTest(); } @Test(enabled = false) public void testReplSync() throws Exception { cache.getConfiguration().setNodeLockingScheme(Configuration.NodeLockingScheme.PESSIMISTIC); runCompleteTest(); } @Test(enabled = true) public void testReplAsync() throws Exception { cache.getConfiguration().setNodeLockingScheme(Configuration.NodeLockingScheme.PESSIMISTIC); cache.getConfiguration().setCacheMode(Configuration.CacheMode.REPL_ASYNC); cache.getConfiguration().setClusterConfig(getJGroupsConfig()); runCompleteTest(); } @Test(enabled = false) public void testReplSyncOptimistic() throws Exception { cache.getConfiguration().setNodeLockingScheme(Configuration.NodeLockingScheme.OPTIMISTIC); cache.getConfiguration().setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName()); runCompleteTest(); } @Test(enabled = false) public void testReplAsyncOptimistic() throws Exception { cache.getConfiguration().setCacheMode(Configuration.CacheMode.REPL_ASYNC); cache.getConfiguration().setNodeLockingScheme(Configuration.NodeLockingScheme.OPTIMISTIC); cache.getConfiguration().setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName()); runCompleteTest(); } @Test(enabled = false) public void testReplSyncBR() throws Exception { BuddyReplicationConfig brc = new BuddyReplicationConfig(); brc.setEnabled(true); cache.getConfiguration().setBuddyReplicationConfig(brc); testReplSync(); } @Test(enabled = false) public void testReplAsyncBR() throws Exception { BuddyReplicationConfig brc = new BuddyReplicationConfig(); brc.setEnabled(true); cache.getConfiguration().setBuddyReplicationConfig(brc); cache.getConfiguration().setNodeLockingScheme(Configuration.NodeLockingScheme.PESSIMISTIC); cache.getConfiguration().setIsolationLevel(IsolationLevel.READ_COMMITTED); // cache.getConfiguration().setIsolationLevel(IsolationLevel.REPEATABLE_READ); testReplAsync(); } @Test(enabled = false) public void testReplSyncOptBR() throws Exception { BuddyReplicationConfig brc = new BuddyReplicationConfig(); brc.setEnabled(true); cache.getConfiguration().setBuddyReplicationConfig(brc); testReplSyncOptimistic(); } @Test(enabled = false) public void testReplAsyncOptBR() throws Exception { BuddyReplicationConfig brc = new BuddyReplicationConfig(); brc.setEnabled(true); cache.getConfiguration().setBuddyReplicationConfig(brc); testReplAsyncOptimistic(); } private void runCompleteTest() throws Exception { init(); startup(); warmup(); doTest(); // wait for user exit System.in.read(); } /** * Thr following test phases can be profiled individually using triggers in JProfiler. */ protected void init() { long startTime = System.currentTimeMillis(); log.warn("Starting init() phase"); fqns.clear(); for (int i = 0; i < MAX_OVERALL_NODES; i++) { Fqn fqn = createRandomFqn(r); while (fqns.contains(fqn)) fqn = createRandomFqn(r); if (i % 10 == 0) { log.warn("Generated " + i + " fqns"); } fqns.add(fqn); } System.gc(); long duration = System.currentTimeMillis() - startTime; log.warn("Finished init() phase. " + printDuration(duration)); } private Fqn createRandomFqn(Random r) { String s = "/"; int depth = r.nextInt(MAX_DEPTH); for (int i = 0; i < depth; i++) { s += r.nextInt(Integer.MAX_VALUE) + "/"; } return Fqn.fromString(s); } protected void startup() { long startTime = System.currentTimeMillis(); log.warn("Starting cache"); cache.start(); long duration = System.currentTimeMillis() - startTime; log.warn("Started cache. " + printDuration(duration)); } private void warmup() throws InterruptedException { long startTime = System.currentTimeMillis(); ExecutorService exec = Executors.newFixedThreadPool(NUM_THREADS); log.warn("Starting warmup"); // creates all the Fqns since this can be expensive and we don't really want to measure this (for now) for (final Fqn f : fqns) { exec.execute(new Runnable() { public void run() { // this will create the necessary nodes. cache.put(f, Collections.emptyMap()); } }); } // loop through WARMUP_LOOPS gets and puts for JVM optimisation for (int i = 0; i < WARMUP_LOOPS; i++) { exec.execute(new Runnable() { public void run() { Fqn f = fqns.get(r.nextInt(MAX_OVERALL_NODES)); cache.get(f, ""); cache.put(f, "k", "v"); cache.remove(f, "k"); } }); } exec.shutdown(); exec.awaitTermination(360, TimeUnit.SECONDS); long duration = System.currentTimeMillis() - startTime; log.warn("Finished warmup. " + printDuration(duration)); //cache.removeNode(Fqn.ROOT); cache.stop(); startup(); } private void doTest() throws Exception { ExecutorService exec = Executors.newFixedThreadPool(NUM_THREADS); log.warn("Starting test"); int i; long print = NUM_OPERATIONS / 10; AtomicLong durationPuts = new AtomicLong(); AtomicLong durationGets = new AtomicLong(); AtomicLong durationRemoves = new AtomicLong(); long stElapsed = System.nanoTime(); for (i = 0; i < NUM_OPERATIONS; i++) { MyRunnable r = null; switch (i % 3) { case 0: r = new Putter(i, durationPuts); break; case 1: r = new Getter(i, durationGets); break; case 2: r = new Remover(i, durationRemoves); break; } if (i % print == 0) log.warn("processing iteration " + i); exec.execute(r); // if (USE_SLEEP) TestingUtil.sleepRandom(MAX_RANDOM_SLEEP_MILLIS); if (USE_SLEEP) TestingUtil.sleepThread(MAX_RANDOM_SLEEP_MILLIS); } log.warn("Finished generating runnables; awaiting executor completion"); // wait for executors to complete! exec.shutdown(); while (!exec.awaitTermination(((long) i), TimeUnit.SECONDS)) {Thread.sleep(1);} // wait up to 1 sec for each call? long elapsedTimeNanos = System.nanoTime() - stElapsed; log.warn("Finished test. " + printDuration((long) toMillis(elapsedTimeNanos))); log.warn("Throughput: " + ((double) NUM_OPERATIONS * 1000 / toMillis(elapsedTimeNanos)) + " operations per second (roughly equal numbers of PUT, GET and REMOVE)"); log.warn("Average GET time: " + printAvg(durationGets.get())); log.warn("Average PUT time: " + printAvg(durationPuts.get())); log.warn("Average REMOVE time: " + printAvg(durationRemoves.get())); } private String printAvg(long totalNanos) { double nOps = (double) (NUM_OPERATIONS / 3); double avg = ((double) totalNanos) / nOps; double avgMicros = avg / 1000; return avgMicros + " µs"; } private double toMillis(long nanos) { return ((double) nanos / (double) 1000000); } enum Mode { PUT, GET, REMOVE } private abstract class MyRunnable implements Runnable { int id; Mode mode; AtomicLong duration; public void run() { String k = getRandomString(); Fqn f = fqns.get(r.nextInt(MAX_OVERALL_NODES)); long d = 0, st = 0; switch (mode) { case PUT: st = System.nanoTime(); cache.put(f, k, getRandomString()); d = System.nanoTime() - st; break; case GET: st = System.nanoTime(); cache.get(f, k); d = System.nanoTime() - st; break; case REMOVE: st = System.nanoTime(); cache.remove(f, k); d = System.nanoTime() - st; break; } duration.getAndAdd(d); } } private class Putter extends MyRunnable { private Putter(int id, AtomicLong duration) { this.id = id; this.duration = duration; mode = Mode.PUT; } } private class Getter extends MyRunnable { private Getter(int id, AtomicLong duration) { this.id = id; this.duration = duration; mode = Mode.GET; } } private class Remover extends MyRunnable { private Remover(int id, AtomicLong duration) { this.id = id; this.duration = duration; mode = Mode.REMOVE; } } private String getRandomString() { StringBuilder sb = new StringBuilder(); int len = r.nextInt(10); for (int i = 0; i < len; i++) { sb.append((char) (63 + r.nextInt(26))); } return sb.toString(); } String printDuration(long duration) { if (duration > 2000) { double dSecs = ((double) duration / (double) 1000); return "Duration: " + dSecs + " seconds"; } else { return "Duration: " + duration + " millis"; } } @Test(enabled = false) public void testStateTransfer() throws Exception { throw new Exception("Implement me"); } @Test(enabled = false) public void testStartup() throws Exception { throw new Exception("Implement me"); } @Test(enabled = false) public void testCacheLoading() throws Exception { throw new Exception("Implement me"); } @Test(enabled = false) public void testPassivation() throws Exception { throw new Exception("Implement me"); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/profiling/MockAsyncReplTest.java0000644000175000017500000000607711066146100030647 0ustar twernertwernerpackage org.jboss.cache.profiling; import org.jboss.cache.RPCManager; import org.jboss.cache.commands.ReplicableCommand; import org.jboss.cache.factories.ComponentRegistry; import org.jboss.cache.marshall.CommandAwareRpcDispatcher; import org.jboss.cache.util.TestingUtil; import org.jboss.cache.util.reflect.ReflectionUtil; import org.jgroups.Address; import org.jgroups.blocks.RpcDispatcher; import org.jgroups.blocks.RspFilter; import org.jgroups.util.RspList; import org.testng.annotations.Test; import java.io.NotSerializableException; import java.util.Vector; import java.util.concurrent.atomic.AtomicInteger; /** * // TODO: MANIK: Document this * * @author Manik Surtani (manik@jboss.org) * @since 3.0 */ public class MockAsyncReplTest extends ProfileTest { @Override @Test(enabled = true) public void testReplAsync() throws Exception { // same as superclass, except that we use a mock RpcDispatcher that does nothing. Measure throughput to test speed of JBC stack. super.testReplAsync(); } @Override protected void startup() { long startTime = System.currentTimeMillis(); log.warn("Starting cache"); cache.start(); // now remove the existing RpcDispatcher and replace with one that is a noop. ComponentRegistry cr = TestingUtil.extractComponentRegistry(cache); RPCManager rpcManager = cr.getComponent(RPCManager.class); RpcDispatcher d = (RpcDispatcher) TestingUtil.extractField(rpcManager, "rpcDispatcher"); d.stop(); RpcDispatcher replacement = new NoopDispatcher(); replacement.setRequestMarshaller(d.getRequestMarshaller()); replacement.setResponseMarshaller(d.getResponseMarshaller()); ReflectionUtil.setValue(rpcManager, "rpcDispatcher", replacement); long duration = System.currentTimeMillis() - startTime; log.warn("Started cache. " + printDuration(duration)); } public static class NoopDispatcher extends CommandAwareRpcDispatcher { AtomicInteger ai = new AtomicInteger(); Marshaller m; // Marshaller2 m2; @Override public RspList invokeRemoteCommands(Vector

dests, ReplicableCommand command, int mode, long timeout, boolean anycasting, boolean oob, RspFilter filter) throws NotSerializableException { // make sure we do the marshalling though if (m == null)// && m2 == null) { m = getRequestMarshaller(); /* if (m instanceof Marshaller2) { m2 = (Marshaller2) m; m = null; }*/ } try { //if (m2 == null) m.objectToByteBuffer(command); //else m2.objectToBuffer(command); } catch (Exception e) { e.printStackTrace(); throw new NotSerializableException(e.getMessage()); } int i = ai.incrementAndGet(); if (i % 1000 == 0) log.warn("Dispatching operation #" + i); // no-op return null; } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/profiling/ProfileSlaveTest.java0000644000175000017500000000776411063726637030554 0ustar twernertwernerpackage org.jboss.cache.profiling; import org.jboss.cache.config.BuddyReplicationConfig; import org.jboss.cache.config.Configuration; import org.jboss.cache.lock.IsolationLevel; import org.jboss.cache.transaction.DummyTransactionManagerLookup; import org.testng.annotations.Test; /** * Slave to go with ProfileTest. Should be done in a different VM. Can be profiled as well to profile receiving * messages. * * @author Manik Surtani (manik@jboss.org) * @since 2.1.0 */ @Test(groups = "profiling") public class ProfileSlaveTest extends AbstractProfileTest { private void waitForTest() throws Exception { System.out.println("Slave listening for remote connections. Hit Enter when done."); System.in.read(); } @Test(enabled = false) public void testReplSync() throws Exception { cache.getConfiguration().setNodeLockingScheme(Configuration.NodeLockingScheme.PESSIMISTIC); cache.getConfiguration().setExposeManagementStatistics(true); cache.start(); waitForTest(); } @Test(enabled = true) public void testReplAsync() throws Exception { cache.getConfiguration().setNodeLockingScheme(Configuration.NodeLockingScheme.PESSIMISTIC); cache.getConfiguration().setCacheMode(Configuration.CacheMode.REPL_ASYNC); cache.getConfiguration().setClusterConfig(getJGroupsConfig()); cache.start(); waitForTest(); } @Test(enabled = false) public void testReplSyncOptimistic() throws Exception { cache.getConfiguration().setNodeLockingScheme(Configuration.NodeLockingScheme.OPTIMISTIC); cache.getConfiguration().setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName()); cache.start(); waitForTest(); } @Test(enabled = false) public void testReplAsyncOptimistic() throws Exception { cache.getConfiguration().setCacheMode(Configuration.CacheMode.REPL_ASYNC); cache.getConfiguration().setNodeLockingScheme(Configuration.NodeLockingScheme.OPTIMISTIC); cache.getConfiguration().setTransactionManagerLookupClass(DummyTransactionManagerLookup.class.getName()); cache.start(); waitForTest(); } @Test(enabled = false) public void testReplSyncBR() throws Exception { BuddyReplicationConfig brc = new BuddyReplicationConfig(); brc.setEnabled(true); cache.getConfiguration().setBuddyReplicationConfig(brc); testReplSync(); } @Test(enabled = false) public void testReplAsyncBR() throws Exception { BuddyReplicationConfig brc = new BuddyReplicationConfig(); brc.setEnabled(true); cache.getConfiguration().setBuddyReplicationConfig(brc); cache.getConfiguration().setNodeLockingScheme(Configuration.NodeLockingScheme.PESSIMISTIC); cache.getConfiguration().setIsolationLevel(IsolationLevel.READ_COMMITTED); // cache.getConfiguration().setIsolationLevel(IsolationLevel.REPEATABLE_READ); testReplAsync(); } @Test(enabled = false) public void testReplSyncOptBR() throws Exception { BuddyReplicationConfig brc = new BuddyReplicationConfig(); brc.setEnabled(true); cache.getConfiguration().setBuddyReplicationConfig(brc); testReplSyncOptimistic(); } @Test(enabled = false) public void testReplAsyncOptBR() throws Exception { BuddyReplicationConfig brc = new BuddyReplicationConfig(); brc.setEnabled(true); cache.getConfiguration().setBuddyReplicationConfig(brc); testReplAsyncOptimistic(); } @Test(enabled = false) public void testStateTransfer() throws Exception { throw new Exception("Implement me"); } @Test(enabled = false) public void testStartup() throws Exception { throw new Exception("Implement me"); } @Test(enabled = false) public void testCacheLoading() throws Exception { throw new Exception("Implement me"); } @Test(enabled = false) public void testPassivation() throws Exception { throw new Exception("Implement me"); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/profiling/ConstructionTest.java0000644000175000017500000000172611011102223030607 0ustar twernertwernerpackage org.jboss.cache.profiling; import org.jboss.cache.Cache; import org.jboss.cache.DefaultCacheFactory; import org.testng.annotations.Test; /** * Profile LOCAL mode operation * * @author Manik Surtani (manik@jboss.org) * @since 2.2.0 */ @Test(groups = "profiling") public class ConstructionTest { Cache cache; private static final int WARMUP = 1000; private static final int LOOPS = 5000; public void testConstruction() throws InterruptedException { for (int i = 0; i < WARMUP; i++) new DefaultCacheFactory().createCache(); System.out.println("Finished warmup."); System.gc(); Thread.sleep(1000); System.out.println("Starting test"); doConstructionTest(); } public void doConstructionTest() { for (int i = 0; i < LOOPS; i++) { new DefaultCacheFactory().createCache(); if (i % 100 == 0) System.out.println("In loop num " + i); } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/profiling/AbstractProfileTest.java0000644000175000017500000001604611063726637031236 0ustar twernertwernerpackage org.jboss.cache.profiling; import org.jboss.cache.Cache; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.UnitTestCacheConfigurationFactory; import org.jboss.cache.xml.XmlHelper; import org.testng.annotations.AfterTest; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; import org.w3c.dom.Element; @Test(groups = "profiling") public abstract class AbstractProfileTest { protected Cache cache; @BeforeTest public void setUp() { Configuration cfg = UnitTestCacheConfigurationFactory.createConfiguration(Configuration.CacheMode.REPL_SYNC); cache = new DefaultCacheFactory().createCache(cfg, false); } @AfterTest public void tearDown() { cache.stop(); } public abstract void testReplSync() throws Exception; public abstract void testReplAsync() throws Exception; public abstract void testReplSyncOptimistic() throws Exception; public abstract void testReplAsyncOptimistic() throws Exception; public abstract void testReplSyncBR() throws Exception; public abstract void testReplAsyncBR() throws Exception; public abstract void testReplSyncOptBR() throws Exception; public abstract void testReplAsyncOptBR() throws Exception; public abstract void testStateTransfer() throws Exception; public abstract void testStartup() throws Exception; public abstract void testCacheLoading() throws Exception; public abstract void testPassivation() throws Exception; public Element getJGroupsConfig() throws Exception { String udp = " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " "; String tcp = " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " "; return XmlHelper.stringToElement(udp); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/commands/0000755000175000017500000000000011376173755024254 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/commands/RollbackOnNoOpTest.java0000644000175000017500000000402211032145203030547 0ustar twernertwernerpackage org.jboss.cache.commands; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.UnitTestCacheConfigurationFactory; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.TransactionManager; /** * @author Mircea.Markus@jboss.com * @since 2.2 */ @Test(groups = "functional") public class RollbackOnNoOpTest { private CacheSPI cache; private TransactionManager txMgr; @BeforeMethod(alwaysRun = true) public void setUp() { Configuration cacheConfig = UnitTestCacheConfigurationFactory.createConfiguration(Configuration.CacheMode.LOCAL, false); cache = (CacheSPI) new DefaultCacheFactory().createCache(cacheConfig, false); cache.start(); txMgr = cache.getTransactionManager(); } @AfterMethod(alwaysRun = true) public void tearDown() { cache.stop(); cache.destroy(); } public void testRollbackOnRemoveNodeDoesNotFail() throws Exception { txMgr.begin(); cache.remove("/blah/blah", "non-exist"); txMgr.rollback(); } public void testRollbackOnClearData() throws Exception { txMgr.begin(); cache.clearData("/blah/blah"); txMgr.rollback(); } public void testCreateNodeCommand() throws Exception { cache.put("/blah/blah", "key", "value"); txMgr.begin(); cache.clearData("/blah/blah"); txMgr.rollback(); assert cache.get("/blah/blah", "key") != null; } public void testRemoveKeyCommand() throws Exception { txMgr.begin(); cache.remove("/blah/blah", "key"); txMgr.rollback(); } public void testRemoveNodeCommand() throws Exception { cache.put("/blah/blah", "key", "value"); txMgr.begin(); cache.removeNode("/blah"); txMgr.rollback(); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/commands/read/0000755000175000017500000000000011376173755025167 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/commands/read/GetKeysCommandTest.java0000644000175000017500000000225511032145203031520 0ustar twernertwernerpackage org.jboss.cache.commands.read; import static org.easymock.EasyMock.expect; import static org.easymock.EasyMock.replay; import org.jboss.cache.mock.NodeSpiMock; import org.testng.annotations.Test; import java.util.Set; /** * Tester class for {@link GetKeysCommand}. * * @author Mircea.Markus@jboss.com * @since 2.2 */ @Test(groups = "unit") public class GetKeysCommandTest extends AbstractDataCommandTest { GetKeysCommand command; protected void moreSetup() { command = new GetKeysCommand(testFqn); command.initialize(container); } public void testForNonexistentNode() { expect(container.peek(testFqn)).andReturn(null); replay(container); assert null == command.perform(null); } public void testForExistingNode() { NodeSpiMock node = new NodeSpiMock(testFqn); node.putDirect("k1", "v1"); node.putDirect("k2", "v2"); expect(container.peek(testFqn)).andReturn(node); replay(container); Set result = (Set) command.perform(null); assert 2 == result.size(); assert result.contains("k1"); assert result.contains("k2"); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/commands/read/ExistsCommandTest.java0000644000175000017500000000155711023757460031446 0ustar twernertwernerpackage org.jboss.cache.commands.read; import static org.easymock.EasyMock.*; import org.testng.annotations.Test; /** * Tester class for {@link org.jboss.cache.commands.read.ExistsCommand} * @author Mircea.Markus@jboss.com * @since 2.2 */ @Test(groups="unit") public class ExistsCommandTest extends AbstractDataCommandTest { private ExistsCommand command; protected void moreSetup() { command = new ExistsCommand(testFqn); command.initialize(container); } public void testPerform() { expect(container.exists(testFqn)).andReturn(Boolean.FALSE); replay(container); assert Boolean.FALSE == command.perform(null); reset(container); expect(container.exists(testFqn)).andReturn(Boolean.TRUE); replay(container); assert Boolean.TRUE == command.perform(null); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/commands/read/GravitateDataCommandTest.java0000644000175000017500000001366311032145203032672 0ustar twernertwernerpackage org.jboss.cache.commands.read; import static org.easymock.EasyMock.createStrictControl; import static org.easymock.EasyMock.expect; import org.easymock.IMocksControl; import org.jboss.cache.CacheSPI; import org.jboss.cache.DataContainer; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.NodeSPI; import org.jboss.cache.buddyreplication.BuddyFqnTransformer; import org.jboss.cache.buddyreplication.BuddyManager; import org.jboss.cache.buddyreplication.GravitateResult; import org.jboss.cache.mock.MockNodesFixture; import org.jboss.cache.mock.NodeSpiMock; import org.jgroups.stack.IpAddress; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.ArrayList; import java.util.Collections; /** * Tester class for {@link org.jboss.cache.commands.read.GravitateDataCommand} * * @author Mircea.Markus@jboss.com * @since 2.2 */ @Test(groups = "unit") public class GravitateDataCommandTest { GravitateDataCommand command; DataContainer containerMock; CacheSPI spiMock; IMocksControl control; Fqn fqn = Fqn.fromString("/dummy"); InvocationContext ctx; BuddyFqnTransformer fqnTransformer = new BuddyFqnTransformer(); @BeforeMethod public void setUp() { control = createStrictControl(); containerMock = control.createMock(DataContainer.class); spiMock = control.createMock(CacheSPI.class); command = new GravitateDataCommand(fqn, true, new IpAddress()); command.initialize(containerMock, spiMock, new BuddyFqnTransformer()); ctx = new InvocationContext(); } public void testNonexistentNode() { command.setSearchSubtrees(false); expect(spiMock.getNode(fqn)).andReturn(null); control.replay(); assert GravitateResult.noDataFound().equals(command.perform(ctx)); } public void testExistentNodeInTheCache() { MockNodesFixture nodes = new MockNodesFixture(); command.setSearchSubtrees(false); expect(spiMock.getNode(fqn)).andReturn(nodes.adfNode); ArrayList arrayList = new ArrayList(); expect(containerMock.buildNodeData(Collections.EMPTY_LIST, nodes.adfNode)).andReturn(arrayList); control.replay(); GravitateResult result = (GravitateResult) command.perform(ctx); control.verify(); assert result != null; assert result.getNodeData() == arrayList; } public void testNodeDoesExistsInBackupAndNoDead() { MockNodesFixture nodes = new MockNodesFixture(); expect(spiMock.getNode(fqn)).andReturn(null); expect(containerMock.peek(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN)).andReturn(nodes.abNode); Fqn firstSearch = Fqn.fromString(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN + "/c/dummy"); expect(spiMock.getNode(firstSearch)).andReturn(nodes.abcNode); ArrayList listReference = new ArrayList(); expect(containerMock.buildNodeData(Collections.EMPTY_LIST, nodes.abcNode)).andReturn(listReference); control.replay(); GravitateResult result = (GravitateResult) command.perform(ctx); assert result.getNodeData() == listReference; control.verify(); } public void testNodeDoesNotExistsInBackupAndNoDead() { MockNodesFixture nodes = new MockNodesFixture(); expect(spiMock.getNode(fqn)).andReturn(null); expect(containerMock.peek(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN)).andReturn(nodes.adfNode); control.checkOrder(false); Fqn firstSearch = Fqn.fromString(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN + "/g/dummy"); expect(spiMock.getNode(firstSearch)).andReturn(null); Fqn secondSearch = Fqn.fromString(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN + "/h/dummy"); expect(spiMock.getNode(secondSearch)).andReturn(null); control.checkOrder(true); control.replay(); assert GravitateResult.noDataFound().equals(command.perform(ctx)); control.verify(); } public void testDeadBeackup() throws Exception { NodeSpiMock root = new NodeSpiMock(Fqn.ROOT); //add first dead child IpAddress firstAddress = new IpAddress("127.0.0.1", 1234); NodeSpiMock firstDeadNode = (NodeSpiMock) root.addChildDirect(fqnTransformer.getDeadBackupRoot(firstAddress)); firstDeadNode.addChildDirect(Fqn.fromElements(0)); firstDeadNode.addChildDirect(Fqn.fromElements(1)); firstDeadNode.addChildDirect(Fqn.fromElements(2)); //add second dead child IpAddress secondAddress = new IpAddress("127.0.0.1", 4321); NodeSpiMock secondDeadNode = (NodeSpiMock) root.addChildDirect(fqnTransformer.getDeadBackupRoot(secondAddress)); secondDeadNode.addChildDirect(Fqn.fromElements(0)); expect(spiMock.getNode(fqn)).andReturn(null); NodeSPI buddyBackupRoot = (NodeSPI) root.getChild(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN); expect(containerMock.peek(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN)).andReturn(buddyBackupRoot); control.checkOrder(false); expect(spiMock.getChildrenNames(firstDeadNode.getFqn())).andReturn(firstDeadNode.getChildrenNames()); Object fqnElement = fqn.getLastElement();//this is only composed from one element expect(spiMock.peek(Fqn.fromRelativeElements(firstDeadNode.getFqn(), 0, fqnElement), false)).andReturn(null); expect(spiMock.peek(Fqn.fromRelativeElements(firstDeadNode.getFqn(), 1, fqnElement), false)).andReturn(null); expect(spiMock.peek(Fqn.fromRelativeElements(firstDeadNode.getFqn(), 2, fqnElement), false)).andReturn(null); expect(spiMock.getChildrenNames(secondDeadNode.getFqn())).andReturn(secondDeadNode.getChildrenNames()); expect(spiMock.peek(Fqn.fromRelativeElements(secondDeadNode.getFqn(), 0, fqnElement), false)).andReturn(null); control.checkOrder(true); control.replay(); assert GravitateResult.noDataFound().equals(command.perform(ctx)); control.verify(); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/commands/read/AbstractDataCommandTest.java0000644000175000017500000000170311023757460032515 0ustar twernertwernerpackage org.jboss.cache.commands.read; import static org.easymock.EasyMock.createMock; import org.jboss.cache.DataContainer; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * Class that has convinience fixture for all tests that operated on {@link AbstractDataCommand} * * @author Mircea.Markus@jboss.com * @since 2.2 */ @Test(groups = "unit") public abstract class AbstractDataCommandTest { protected Fqn testFqn = Fqn.fromString("/testfqn"); protected DataContainer container; protected InvocationContext ctx; @BeforeMethod final public void setUp() { container = createMock(DataContainer.class); ctx = new InvocationContext(); moreSetup(); } /** * called by setUp after initializing the testFqn and containeMock. */ protected abstract void moreSetup(); } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/commands/read/GetKeyValueCommandTest.java0000644000175000017500000000505311032145203032331 0ustar twernertwernerpackage org.jboss.cache.commands.read; import static org.easymock.EasyMock.createStrictControl; import static org.easymock.EasyMock.expect; import org.easymock.IMocksControl; import org.jboss.cache.DataContainer; import org.jboss.cache.Fqn; import org.jboss.cache.mock.NodeSpiMock; import org.jboss.cache.notifications.Notifier; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * Tester class for {@link GetKeyValueCommand}. * * @author Mircea.Markus@jboss.com * @since 2.2 */ @Test(groups = "unit") public class GetKeyValueCommandTest { private IMocksControl control; Notifier notifierMock; DataContainer containerMock; GetKeyValueCommand command; Fqn fqn = Fqn.fromString("/dummy"); String key = "key"; @BeforeMethod protected void setUup() { control = createStrictControl(); containerMock = control.createMock(DataContainer.class); notifierMock = control.createMock(Notifier.class); command = new GetKeyValueCommand(fqn, key, false); command.initialize(containerMock, notifierMock); } public void testNonexistentNodeNoNotifications() { expect(containerMock.peek(fqn)).andReturn(null); control.replay(); assert null == command.perform(null); } public void testExistentNodeNoNotifications() { NodeSpiMock node = new NodeSpiMock(fqn); String value = "vvv"; node.put(key, value); expect(containerMock.peek(fqn)).andReturn(node); control.replay(); assert value.equals(command.perform(null)); } /** * Notification should only be triggered if the node exists. */ public void testNonexistentNodeWithNotifications() { command.sendNodeEvent = true; expect(containerMock.peek(fqn)).andReturn(null); control.replay(); assert null == command.perform(null); } public void testExistentNodeWithNotifications() { command.sendNodeEvent = true; NodeSpiMock node = new NodeSpiMock(fqn); String value = "vvv"; node.put(key, value); //not ordred because the peek hapens before notification - that is to make sure that no notification // is sent for an nonexistent node. control.checkOrder(false); notifierMock.notifyNodeVisited(fqn, true, null); expect(containerMock.peek(fqn)).andReturn(node); notifierMock.notifyNodeVisited(fqn, false, null); control.replay(); assert value.equals(command.perform(null)); control.verify(); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/commands/read/GetDataMapCommandTest.java0000644000175000017500000000262111032145203032111 0ustar twernertwernerpackage org.jboss.cache.commands.read; import static org.easymock.EasyMock.expect; import static org.easymock.EasyMock.replay; import org.jboss.cache.mock.NodeSpiMock; import org.testng.annotations.Test; import java.util.Map; /** * Tester class for {@link GetDataMapCommand}. * * @author Mircea.Markus@jboss.com * @since 2.2 */ @Test(groups = "unit") public class GetDataMapCommandTest extends AbstractDataCommandTest { GetDataMapCommand command; protected void moreSetup() { command = new GetDataMapCommand(testFqn); command.initialize(container); } public void testForNonexistentNode() { expect(container.peek(testFqn)).andReturn(null); replay(container); assert null == command.perform(null); } public void testForExistingNode() { NodeSpiMock node = new NodeSpiMock(testFqn); node.putDirect("k1", "v1"); node.putDirect("k2", "v2"); expect(container.peek(testFqn)).andReturn(node); replay(container); Map result = (Map) command.perform(null); assert 2 == result.entrySet().size(); assert result.get("k1").equals("v1"); assert result.get("k2").equals("v2"); try { result.put("k3", "v3"); assert false : "map should be immutable"; } catch (RuntimeException ex) { //expected } } } ././@LongLink0000000000000000000000000000014600000000000011566 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/commands/read/GetChildrenNamesCommandTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/commands/read/GetChildrenNamesCommandTest.jav0000644000175000017500000000363111032145203033157 0ustar twernertwernerpackage org.jboss.cache.commands.read; import static org.easymock.EasyMock.expect; import static org.easymock.EasyMock.replay; import org.jboss.cache.mock.MockNodesFixture; import org.jboss.cache.mock.NodeSpiMock; import org.testng.annotations.Test; import java.util.Set; /** * Tester class for {@link org.jboss.cache.commands.read.GetChildrenNamesCommand}. * * @author Mircea.Markus@jboss.com * @since 2.2 */ @Test(groups = "unit") public class GetChildrenNamesCommandTest extends AbstractDataCommandTest { private GetChildrenNamesCommand command; private MockNodesFixture nodes; protected void moreSetup() { nodes = new MockNodesFixture(); command = new GetChildrenNamesCommand(testFqn); command.initialize(container); } public void testPerformNoChildren() { NodeSpiMock node = new NodeSpiMock(testFqn); expect(container.peek(testFqn)).andReturn(node); replay(container); Set result = (Set) command.perform(null); assert result.isEmpty() : "empty result expected"; } public void testPerformInexistingNode() { expect(container.peek(testFqn)).andReturn(null); replay(container); Set result = (Set) command.perform(null); assert result == null : "empty result expected"; } public void testNodeWithChildren() { expect(container.peek(testFqn)).andReturn(nodes.adfNode); replay(container); Set result = (Set) command.perform(null); assert result.size() == 2; assert result.contains("h"); assert result.contains("g"); } public void testNodeInvalidChildren() { nodes.adfgNode.markAsDeleted(true); expect(container.peek(testFqn)).andReturn(nodes.adfNode); replay(container); Set result = (Set) command.perform(null); assert result.size() == 1; assert result.contains("h"); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/commands/write/0000755000175000017500000000000011376173755025406 5ustar twernertwerner././@LongLink0000000000000000000000000000015300000000000011564 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/commands/write/OptimisticInvalidateCommandTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/commands/write/OptimisticInvalidateCommandTes0000644000175000017500000001106311032145203033402 0ustar twernertwernerpackage org.jboss.cache.commands.write; import static org.easymock.EasyMock.createStrictControl; import static org.easymock.EasyMock.expect; import org.easymock.IMocksControl; import org.jboss.cache.CacheSPI; import org.jboss.cache.DataContainer; import org.jboss.cache.commands.read.AbstractDataCommandTest; import org.jboss.cache.mock.MockNodesFixture; import org.jboss.cache.notifications.Notifier; import org.jboss.cache.optimistic.DataVersion; import org.jboss.cache.optimistic.DefaultDataVersion; import org.testng.annotations.Test; import javax.transaction.Transaction; import javax.transaction.TransactionManager; import java.util.Collections; /** * tester class for {@link OptimisticInvalidateCommand}. * * @author Mircea.Markus@jboss.com * @since 2.2 */ @Test(groups = "unit") public class OptimisticInvalidateCommandTest extends AbstractDataCommandTest { DataVersion dataVersion; OptimisticInvalidateCommand command; IMocksControl control; Notifier notifier; TransactionManager tmMock; MockNodesFixture nodes; CacheSPI spiMock; protected void moreSetup() { control = createStrictControl(); notifier = control.createMock(Notifier.class); container = control.createMock(DataContainer.class); tmMock = control.createMock(TransactionManager.class); spiMock = control.createMock(CacheSPI.class); nodes = new MockNodesFixture(); command = new OptimisticInvalidateCommand(testFqn); dataVersion = new DefaultDataVersion(10); command.setDataVersion(dataVersion); command.initialize(spiMock, container, notifier); command.initialize(tmMock); } public void testWithExistingNode() { nodes.adfNode.put("key", "value"); nodes.adfNode.setDataLoaded(true); expect(spiMock.getNode(testFqn)).andReturn(nodes.adfNode); expect(container.peekVersioned(testFqn, dataVersion)).andReturn(nodes.adfNode); notifier.notifyNodeEvicted(testFqn, true, ctx); notifier.notifyNodeEvicted(testFqn, false, ctx); expect(container.peek(testFqn, false, true)).andReturn(nodes.adfNode); control.replay(); assert null == command.perform(ctx); assert nodes.adfNode.getData().isEmpty(); assert !nodes.adfNode.isDataLoaded(); assert !nodes.adfNode.isValid(); assert nodes.adfNode.getVersion().equals(dataVersion); control.verify(); } public void testWithExistingNodeInvalidVersion() { nodes.adfNode.put("key", "value"); nodes.adfNode.setDataLoaded(true); expect(spiMock.getNode(testFqn)).andReturn(nodes.adfNode); expect(container.peekVersioned(testFqn, dataVersion)).andThrow(new RuntimeException()); control.replay(); try { command.perform(ctx); assert false : "exception expected"; } catch (Exception e) { //expected as there is a version mismatch } assert !nodes.adfNode.getData().isEmpty(); assert nodes.adfNode.isDataLoaded(); assert nodes.adfNode.isValid(); assert !dataVersion.equals(nodes.adfNode.getVersion()); control.verify(); } public void testExistingTumbstone() { nodes.adfNode.setValid(false, true); expect(spiMock.getNode(testFqn)).andReturn(null); expect(container.peek(testFqn, false, true)).andReturn(nodes.adfNode); expect(container.peekVersioned(testFqn, dataVersion)).andReturn(nodes.adfNode); notifier.notifyNodeEvicted(testFqn, true, ctx); notifier.notifyNodeEvicted(testFqn, false, ctx); expect(container.peek(testFqn, false, true)).andReturn(nodes.adfNode); control.replay(); assert null == command.perform(ctx); assert nodes.adfNode.getData().isEmpty(); assert !nodes.adfNode.isDataLoaded(); assert !nodes.adfNode.isValid(); assert nodes.adfNode.getVersion().equals(dataVersion); control.verify(); } public void testCreateTumbstone() throws Exception { Transaction tx = control.createMock(Transaction.class); expect(tmMock.suspend()).andReturn(tx); spiMock.put(testFqn, Collections.emptyMap()); tmMock.resume(tx); control.replay(); command.createTombstone(ctx); control.verify(); } public void testCreateTumbstoneNoTx() throws Exception { expect(tmMock.suspend()).andReturn(null); spiMock.put(testFqn, Collections.EMPTY_MAP); control.replay(); command.createTombstone(ctx); control.verify(); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/commands/write/PutKeyValueCommandTest.java0000644000175000017500000000674311032145203032610 0ustar twernertwernerpackage org.jboss.cache.commands.write; import static org.easymock.EasyMock.expect; import org.jboss.cache.CacheException; import org.jboss.cache.notifications.event.NodeModifiedEvent; import org.testng.annotations.Test; import java.util.HashMap; import java.util.Map; /** * tester class for {@link PutKeyValueCommand}. * * @author Mircea.Markus@jboss.com * @since 2.2 */ @Test(groups = "unit") public class PutKeyValueCommandTest extends AbstractVersionedDataCommandTest { PutKeyValueCommand command; public AbstractVersionedDataCommand moreSetUp() { command = new PutForExternalReadCommand(globalTransaction, fqn, "k", "v"); return command; } public void testInexistentNode() { expect(container.peekStrict(globalTransaction, fqn, false)).andThrow(new CacheException()); control.replay(); try { command.perform(ctx); assert false : "exception should have been thrown as data does not exists."; } catch (Exception e) { //expected } control.verify(); } public void testAddNewData() { nodes.adfNode.put("existingKey", "existingValue"); expect(container.peekStrict(globalTransaction, fqn, false)).andReturn(nodes.adfNode); expect(notifier.shouldNotifyOnNodeModified()).andReturn(true); notifier.notifyNodeModified(fqn, true, NodeModifiedEvent.ModificationType.PUT_DATA, nodes.adfNode.getDataDirect(), ctx); Map expected = new HashMap(); expected.put("k", "v"); expect(notifier.shouldNotifyOnNodeModified()).andReturn(true); notifier.notifyNodeModified(fqn, false, NodeModifiedEvent.ModificationType.PUT_DATA, expected, ctx); control.replay(); assert null == command.perform(ctx) : "no pre existing value"; assert nodes.adfNode.getData().size() == 2; assert "v".equals(nodes.adfNode.getData().get("k")); assert "existingValue".equals(nodes.adfNode.getData().get("existingKey")); control.verify(); control.reset(); expect(container.peek(fqn, false, false)).andReturn(nodes.adfNode); control.replay(); command.rollback(); assert nodes.adfNode.getData().size() == 1; assert "existingValue".equals(nodes.adfNode.getData().get("existingKey")); control.verify(); } public void testOverWriteData() { nodes.adfNode.put("k", "oldValue"); expect(container.peekStrict(globalTransaction, fqn, false)).andReturn(nodes.adfNode); expect(notifier.shouldNotifyOnNodeModified()).andReturn(true); notifier.notifyNodeModified(fqn, true, NodeModifiedEvent.ModificationType.PUT_DATA, nodes.adfNode.getDataDirect(), ctx); Map expected = new HashMap(); expected.put("k", "v"); expect(notifier.shouldNotifyOnNodeModified()).andReturn(true); notifier.notifyNodeModified(fqn, false, NodeModifiedEvent.ModificationType.PUT_DATA, expected, ctx); control.replay(); assert "oldValue".equals(command.perform(ctx)) : "no pre existing value"; assert nodes.adfNode.getData().size() == 1; assert "v".equals(nodes.adfNode.getData().get("k")); control.verify(); control.reset(); expect(container.peek(fqn, false, false)).andReturn(nodes.adfNode); control.replay(); command.rollback(); assert nodes.adfNode.getData().size() == 1; assert "oldValue".equals(nodes.adfNode.getData().get("k")); control.verify(); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/commands/write/InvalidateCommandTest.java0000644000175000017500000000562011032145203032443 0ustar twernertwernerpackage org.jboss.cache.commands.write; import static org.easymock.EasyMock.createStrictControl; import static org.easymock.EasyMock.expect; import org.easymock.IMocksControl; import org.jboss.cache.CacheSPI; import org.jboss.cache.DataContainer; import org.jboss.cache.Fqn; import org.jboss.cache.commands.read.AbstractDataCommandTest; import org.jboss.cache.mock.MockNodesFixture; import org.jboss.cache.notifications.Notifier; import org.testng.annotations.Test; import javax.transaction.TransactionManager; /** * Tester class for {@link org.jboss.cache.commands.write.InvalidateCommand} * * @author Mircea.Markus@jboss.com * @since 2.2 */ @Test(groups = "unit") public class InvalidateCommandTest extends AbstractDataCommandTest { InvalidateCommand command; Notifier notifier; IMocksControl control; MockNodesFixture nodes; TransactionManager tmMock; CacheSPI spiMock; protected void moreSetup() { control = createStrictControl(); notifier = control.createMock(Notifier.class); container = control.createMock(DataContainer.class); tmMock = control.createMock(TransactionManager.class); spiMock = control.createMock(CacheSPI.class); command = new InvalidateCommand(testFqn); command.initialize(spiMock, container, notifier); nodes = new MockNodesFixture(); } public void testNullNode() { expect(spiMock.getNode(testFqn)).andReturn(null); control.replay(); assert null == command.perform(ctx); control.verify(); } public void testExistingNode() { expect(spiMock.getNode(testFqn)).andReturn(nodes.adfNode); notifier.notifyNodeEvicted(testFqn, true, ctx); expect(container.evict(testFqn)).andReturn(Boolean.TRUE); notifier.notifyNodeEvicted(testFqn, false, ctx); control.replay(); assert null == command.perform(ctx); assert !nodes.adfNode.isValid() : "node should had been invalidated"; assert !nodes.adfgNode.isValid() : "child should had been invalidated"; assert !nodes.adfhNode.isValid() : "child should had been invalidated"; control.verify(); } public void testRootNodeInvalidation() { command.setFqn(Fqn.ROOT); nodes.adfgNode.put("key", "value"); expect(spiMock.getNode(Fqn.ROOT)).andReturn(nodes.root); notifier.notifyNodeEvicted(Fqn.ROOT, true, ctx); expect(container.evict(Fqn.ROOT)).andReturn(Boolean.TRUE); notifier.notifyNodeEvicted(Fqn.ROOT, false, ctx); control.replay(); assert null == command.perform(ctx); assert nodes.root.isValid() : "root should NOT had been invalidated"; assert !nodes.adfgNode.isValid() : "node should had been invalidated"; control.verify(); } public void testInvalidateResidentNode() { nodes.adfNode.setResident(true); testExistingNode(); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/commands/write/MoveCommandTest.java0000644000175000017500000000432011032145203031265 0ustar twernertwernerpackage org.jboss.cache.commands.write; import static org.easymock.EasyMock.createStrictControl; import static org.easymock.EasyMock.expect; import org.easymock.IMocksControl; import org.jboss.cache.DataContainer; import org.jboss.cache.Fqn; import org.jboss.cache.NodeNotExistsException; import org.jboss.cache.commands.read.AbstractDataCommandTest; import org.jboss.cache.mock.MockNodesFixture; import org.jboss.cache.notifications.Notifier; import org.testng.annotations.Test; /** * Tester class for {@link org.jboss.cache.commands.write.MoveCommand}. * * @author Mircea.Markus@jboss.com * @since 2.2 */ @Test(groups = "unit") public class MoveCommandTest extends AbstractDataCommandTest { MoveCommand command; Notifier notifier; IMocksControl control; MockNodesFixture nodes; Fqn source = Fqn.fromString("/source"); Fqn destination = Fqn.fromString("/destination"); protected void moreSetup() { control = createStrictControl(); notifier = control.createMock(Notifier.class); container = control.createMock(DataContainer.class); command = new MoveCommand(source, destination); command.initialize(notifier, container); nodes = new MockNodesFixture(); } public void testFailsOnMissingSource() { control.checkOrder(false); expect(container.peek(source, false, false)).andReturn(null); expect(container.peek(destination, false, false)).andReturn(nodes.adfgNode); control.replay(); try { command.perform(ctx); assert false : "should have thrown an exception as the source is null"; } catch (NodeNotExistsException e) { //expected } } public void testFailsOnMissingDestination() { control.checkOrder(false); expect(container.peek(source, false, false)).andReturn(nodes.adfgNode); expect(container.peek(destination, false, false)).andReturn(null); control.replay(); try { command.perform(ctx); assert false : "should have thrown an exception as the source is null"; } catch (NodeNotExistsException e) { //expected } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/commands/write/RemoveNodeCommandTest.java0000644000175000017500000000531511032145203032427 0ustar twernertwernerpackage org.jboss.cache.commands.write; import static org.easymock.EasyMock.expect; import org.jboss.cache.transaction.GlobalTransaction; import org.testng.annotations.Test; /** * tester for {@link RemoveNodeCommand}. * * @author Mircea.Markus@jboss.com * @since 2.2 */ @Test(groups = "unit") public class RemoveNodeCommandTest extends AbstractVersionedDataCommandTest { RemoveNodeCommand command; public AbstractVersionedDataCommand moreSetUp() { command = new RemoveNodeCommand(globalTransaction, fqn); command.setDataVersion(dataVersion); return command; } public void testNonExistentNode() { expect(container.peekVersioned(fqn, dataVersion, true)).andReturn(null); control.replay(); assert Boolean.FALSE == command.perform(ctx) : "nonexistent node was not remove; false expected"; } public void testRemovalNoNotificationsValidNode() { //aditional setup command.setSkipSendingNodeEvents(true); //no notification nodes.adfNode.put("akey", "avalue"); ctx.setGlobalTransaction(new GlobalTransaction()); //check perform expect(container.peekVersioned(fqn, dataVersion, true)).andReturn(nodes.adfNode); control.replay(); assert Boolean.TRUE == command.perform(ctx); assert nodes.adfgNode.isDeleted(); assert nodes.adfhNode.isDeleted(); assert command.originalData != null; control.verify(); //check rollback control.reset(); nodes.adNode.removeChild("f"); expect(container.peek(nodes.ad)).andReturn(nodes.adNode); control.replay(); command.rollback(); assert nodes.adNode.hasChild("f"); } public void testRemovalNoNotificationsInvalidNode() { command.setSkipSendingNodeEvents(true); //no notification nodes.adfNode.setValid(false, false); //invalid node expect(container.peekVersioned(fqn, dataVersion, true)).andReturn(nodes.adfNode); control.replay(); assert Boolean.FALSE == command.perform(ctx); assert nodes.adfgNode.isDeleted(); assert nodes.adfhNode.isDeleted(); control.verify(); } public void testRemovalWithNotificationsInvalidNode() { nodes.adfNode.setValid(false, false); //invalid node expect(container.peekVersioned(fqn, dataVersion, true)).andReturn(nodes.adfNode); notifier.notifyNodeRemoved(fqn, true, nodes.adfNode.getDataDirect(), ctx); notifier.notifyNodeRemoved(fqn, false, null, ctx); control.replay(); assert Boolean.FALSE == command.perform(ctx); assert nodes.adfgNode.isDeleted(); assert nodes.adfhNode.isDeleted(); control.verify(); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/commands/write/ClearDataCommandTest.java0000644000175000017500000000415211032145203032202 0ustar twernertwernerpackage org.jboss.cache.commands.write; import static org.easymock.EasyMock.expect; import org.jboss.cache.notifications.event.NodeModifiedEvent; import org.testng.annotations.Test; import java.util.Collections; /** * tester class for {@link ClearDataCommand}. * * @author Mircea.Markus@jboss.com * @since 2.2 */ @Test(groups = "unit") public class ClearDataCommandTest extends AbstractVersionedDataCommandTest { ClearDataCommand command; public AbstractVersionedDataCommand moreSetUp() { command = new ClearDataCommand(globalTransaction, fqn); command.setDataVersion(dataVersion); return command; } public void testNonexistentNode() { expect(container.peekVersioned(fqn, dataVersion)).andReturn(null); control.replay(); assert null == command.perform(ctx); control.verify(); } public void testExistentData() { nodes.adfgNode.put("key", "value"); expect(container.peekVersioned(fqn, dataVersion)).andReturn(nodes.adfgNode); notifier.notifyNodeModified(fqn, true, NodeModifiedEvent.ModificationType.REMOVE_DATA, nodes.adfgNode.getDataDirect(), ctx); notifier.notifyNodeModified(fqn, false, NodeModifiedEvent.ModificationType.REMOVE_DATA, Collections.EMPTY_MAP, ctx); control.replay(); assert null == command.perform(ctx); assert nodes.adfgNode.getData().isEmpty(); control.verify(); //now do a rollback control.reset(); expect(container.peek(fqn, false, true)).andReturn(nodes.aNode); control.replay(); command.rollback(); assert nodes.aNode.dataSize() == 1; assert nodes.aNode.getData().get("key").equals("value"); } /** * If clearing data on an inexistent node, the rollback should not fail */ public void testNoOpRollback() { expect(container.peek(fqn, false, true)).andReturn(null); control.replay(); try { command.rollback(); } catch (Exception e) { assert false : "should not fail but expect this scenarion"; } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/commands/write/RemoveKeyCommandTest.java0000644000175000017500000000715511032145203032276 0ustar twernertwernerpackage org.jboss.cache.commands.write; import static org.easymock.EasyMock.expect; import org.jboss.cache.notifications.event.NodeModifiedEvent; import org.testng.annotations.Test; import java.util.HashMap; import java.util.Map; /** * tester class for {@link RemoveKeyCommand}. * * @author Mircea.Markus@jboss.com * @since 2.2 */ @Test(groups = "unit") public class RemoveKeyCommandTest extends AbstractVersionedDataCommandTest { RemoveKeyCommand command; private String key; public AbstractVersionedDataCommand moreSetUp() { key = "key"; command = new RemoveKeyCommand(globalTransaction, fqn, key); return command; } public void testNonexistentNode() { expect(container.peek(fqn, false, false)).andReturn(null); control.replay(); assert null == command.perform(ctx); control.verify(); } public void testRemoveNonexistentPair() { Map expected = new HashMap(); expected.put("newKey", "newValue"); nodes.adfgNode.putAll(expected); expect(container.peek(fqn, false, false)).andReturn(nodes.adfgNode); expect(notifier.shouldNotifyOnNodeModified()).andReturn(true); notifier.notifyNodeModified(fqn, true, NodeModifiedEvent.ModificationType.REMOVE_DATA, expected, ctx); expected = new HashMap(); expected.put(key, null); expect(notifier.shouldNotifyOnNodeModified()).andReturn(true); notifier.notifyNodeModified(fqn, false, NodeModifiedEvent.ModificationType.REMOVE_DATA, expected, ctx); control.replay(); assert null == command.perform(ctx); assert nodes.adfgNode.getData().size() == 1; assert "newValue".equals(nodes.adfgNode.getData().get("newKey")); control.verify(); control.reset(); expect(container.peek(fqn, false, true)).andReturn(nodes.adfgNode); control.replay(); command.rollback(); assert nodes.adfgNode.getData().size() == 1; assert "newValue".equals(nodes.adfgNode.getData().get("newKey")); control.verify(); } public void testRemoveExistentPair() { Map expected = new HashMap(); expected.put(key, "newValue"); nodes.adfgNode.putAll(expected); expect(container.peek(fqn, false, false)).andReturn(nodes.adfgNode); expect(notifier.shouldNotifyOnNodeModified()).andReturn(true); notifier.notifyNodeModified(fqn, true, NodeModifiedEvent.ModificationType.REMOVE_DATA, expected, ctx); expect(notifier.shouldNotifyOnNodeModified()).andReturn(true); notifier.notifyNodeModified(fqn, false, NodeModifiedEvent.ModificationType.REMOVE_DATA, expected, ctx); control.replay(); assert "newValue" == command.perform(ctx); assert nodes.adfgNode.getData().get(key) == null; control.verify(); control.reset(); expect(container.peek(fqn, false, true)).andReturn(nodes.adfgNode); control.replay(); command.rollback(); assert nodes.adfgNode.getData().size() == 1; assert "newValue".equals(nodes.adfgNode.getData().get(key)); control.verify(); } /** * On an no-op scenario the user will try to remove a key on an unexisting node. * When rollback is being called, the node might not exist in the cache and we should know how to handle that. */ public void testRollbackOnNoOp() { expect(container.peek(fqn, false, true)).andReturn(null); control.replay(); try { command.rollback(); } catch (Exception ex) { assert false : "No exception should be thrown here."; } } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/commands/write/PutDataMapCommandTest.java0000644000175000017500000000473011032145203032364 0ustar twernertwernerpackage org.jboss.cache.commands.write; import static org.easymock.EasyMock.createStrictControl; import static org.easymock.EasyMock.expect; import org.easymock.IMocksControl; import org.jboss.cache.DataContainer; import org.jboss.cache.Fqn; import org.jboss.cache.mock.NodeSpiMock; import org.jboss.cache.notifications.Notifier; import org.jboss.cache.notifications.event.NodeModifiedEvent; import org.jboss.cache.transaction.GlobalTransaction; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.util.HashMap; import java.util.Map; /** * Tester class for {@link PutDataMapCommand} * * @author Mircea.Markus@jboss.com * @since 2.2 */ @Test(groups = "unit") public class PutDataMapCommandTest { Fqn testFqn = Fqn.fromString("/testfqn"); PutDataMapCommand command; GlobalTransaction gtx; Notifier notifier; DataContainer container; Map dataMap; IMocksControl control; NodeSpiMock node; @BeforeMethod protected void setUp() { gtx = new GlobalTransaction(); dataMap = new HashMap(); command = new PutDataMapCommand(gtx, testFqn, dataMap); control = createStrictControl(); notifier = control.createMock(Notifier.class); container = control.createMock(DataContainer.class); command.initialize(notifier, container); node = new NodeSpiMock(testFqn); node.put("k", "v"); } public void testAddDataNoErase() { expect(container.peekStrict(gtx, testFqn, false)).andReturn(node); dataMap.put("k2", "v2"); Map expected = new HashMap(dataMap); expected.putAll(node.getDataDirect()); expect(notifier.shouldNotifyOnNodeModified()).andReturn(true); notifier.notifyNodeModified(testFqn, true, NodeModifiedEvent.ModificationType.PUT_MAP, node.getData(), null); expect(notifier.shouldNotifyOnNodeModified()).andReturn(true); notifier.notifyNodeModified(testFqn, false, NodeModifiedEvent.ModificationType.PUT_MAP, expected, null); control.replay(); assert null == command.perform(null) : "null result is always expected"; assert command.getOldData().size() == 1; assert command.getOldData().get("k").equals("v"); control.verify(); } public void testRollbackNonexistentNode() { expect(container.peek(testFqn, false, true)).andReturn(null); control.replay(); command.rollback(); control.verify(); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/commands/write/CreateNodeCommandTest.java0000644000175000017500000000447211032145203032400 0ustar twernertwernerpackage org.jboss.cache.commands.write; import static org.easymock.EasyMock.*; import org.jboss.cache.commands.read.AbstractDataCommandTest; import org.jboss.cache.mock.MockNodesFixture; import org.testng.annotations.Test; import java.util.ArrayList; /** * Tester class for {@link CreateNodeCommand} * * @author Mircea.Markus@jboss.com * @since 2.2 */ @Test(groups = "unit") public class CreateNodeCommandTest extends AbstractDataCommandTest { CreateNodeCommand command; private Object[] result; private ArrayList createdNodes; protected void moreSetup() { command = new CreateNodeCommand(testFqn); command.initialize(container); createdNodes = new ArrayList(); result = new Object[2]; result[0] = this.createdNodes; } public void testPerformNoNodesCreated() { expect(container.createNodes(testFqn)).andReturn(result); replay(container); assert null == command.perform(ctx); assert command.getNewlyCreated().isEmpty(); } public void testPerformWithCreatedNodes() { MockNodesFixture nodes = new MockNodesFixture(); createdNodes.add(nodes.aNode); createdNodes.add(nodes.abNode); createdNodes.add(nodes.abcNode); result[1] = nodes.abcNode; expect(container.createNodes(testFqn)).andReturn(result); replay(container); assert nodes.abcNode == command.perform(ctx); assert command.getNewlyCreated().size() == 3; assert command.getNewlyCreated().contains(nodes.a); assert command.getNewlyCreated().contains(nodes.ab); assert command.getNewlyCreated().contains(nodes.abc); } public void testRollback() { MockNodesFixture nodes = new MockNodesFixture(); createdNodes.add(nodes.aNode); createdNodes.add(nodes.abNode); createdNodes.add(nodes.abcNode); expect(container.createNodes(testFqn)).andReturn(result); expect(container.removeFromDataStructure(nodes.a, true)).andReturn(Boolean.TRUE); expect(container.removeFromDataStructure(nodes.ab, true)).andReturn(Boolean.TRUE); expect(container.removeFromDataStructure(nodes.abc, true)).andReturn(Boolean.TRUE); replay(container); command.perform(ctx); command.rollback(); verify(container); } } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/commands/write/EvictCommandTest.java0000644000175000017500000000521211031204434031433 0ustar twernertwernerpackage org.jboss.cache.commands.write; import static org.easymock.EasyMock.createStrictControl; import static org.easymock.EasyMock.expect; import org.easymock.IMocksControl; import org.jboss.cache.DataContainer; import org.jboss.cache.Fqn; import org.jboss.cache.commands.read.AbstractDataCommandTest; import org.jboss.cache.mock.MockNodesFixture; import org.jboss.cache.notifications.Notifier; import org.testng.annotations.Test; import java.util.ArrayList; import java.util.List; /** * tester class for {@link EvictCommand} * * @author Mircea.Markus@jboss.com * @since 2.2 */ @Test(groups = "unit") public class EvictCommandTest extends AbstractDataCommandTest { EvictCommand command; Notifier notifier; IMocksControl control; MockNodesFixture nodes; protected void moreSetup() { control = createStrictControl(); notifier = control.createMock(Notifier.class); container = control.createMock(DataContainer.class); command = new EvictCommand(testFqn); command.initialize(notifier, container); nodes = new MockNodesFixture(); } public void testResidentNodesEviction() { nodes.abNode.setResident(true); expect(container.peek(testFqn, false, true)).andReturn(nodes.abNode); control.replay(); assert Boolean.TRUE == command.perform(ctx); control.verify(); } public void testSimpleEviction() { expect(container.peek(testFqn, false, true)).andReturn(nodes.abNode); notifier.notifyNodeEvicted(testFqn, true, ctx); expect(container.evict(testFqn)).andReturn(true); notifier.notifyNodeEvicted(testFqn, false, ctx); control.replay(); assert Boolean.TRUE == command.perform(ctx); control.verify(); } public void testRecursiveEviction() { List nodesToEvict = new ArrayList(); nodesToEvict.add(nodes.a); nodesToEvict.add(nodes.ab); command.setRecursive(true); expect(container.peek(testFqn, false, true)).andReturn(nodes.aNode); expect(container.getNodesForEviction(testFqn, true)).andReturn(nodesToEvict); control.checkOrder(false); //evict a notifier.notifyNodeEvicted(nodes.a, true, ctx); expect(container.evict(nodes.a)).andReturn(true); notifier.notifyNodeEvicted(nodes.a, false, ctx); //evict b notifier.notifyNodeEvicted(nodes.ab, true, ctx); expect(container.evict(nodes.ab)).andReturn(true); notifier.notifyNodeEvicted(nodes.ab, false, ctx); control.replay(); assert Boolean.TRUE == command.perform(ctx); control.verify(); } } ././@LongLink0000000000000000000000000000015400000000000011565 Lustar rootrootjbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/commands/write/AbstractVersionedDataCommandTest.javajbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/commands/write/AbstractVersionedDataCommandTe0000644000175000017500000000312311014756770033325 0ustar twernertwernerpackage org.jboss.cache.commands.write; import static org.easymock.EasyMock.createStrictControl; import org.easymock.IMocksControl; import org.jboss.cache.DataContainer; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.mock.MockNodesFixture; import org.jboss.cache.notifications.Notifier; import org.jboss.cache.optimistic.DataVersion; import org.jboss.cache.optimistic.DefaultDataVersion; import org.jboss.cache.transaction.GlobalTransaction; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** * Base class for testing {@link AbstractVersionedDataCommand}. * * @author Mircea.Markus@jboss.com * @since 2.2 */ @Test(groups = "unit") public abstract class AbstractVersionedDataCommandTest { Notifier notifier; DataContainer container; IMocksControl control; MockNodesFixture nodes; DataVersion dataVersion; GlobalTransaction globalTransaction; Fqn fqn = Fqn.fromString("/test/fqn"); InvocationContext ctx; @BeforeMethod public final void setUp() { control = createStrictControl(); container = control.createMock(DataContainer.class); notifier = control.createMock(Notifier.class); nodes = new MockNodesFixture(); globalTransaction = new GlobalTransaction(); dataVersion = new DefaultDataVersion(10); ctx = new InvocationContext(); AbstractVersionedDataCommand command = moreSetUp(); command.initialize(notifier, container); } public abstract AbstractVersionedDataCommand moreSetUp(); } jbosscache-core-2.2.2.GA/src/test/java/org/jboss/cache/commands/StructuralNodesOnRollbackTest.java0000644000175000017500000000350711032145203033044 0ustar twernertwernerpackage org.jboss.cache.commands; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.UnitTestCacheConfigurationFactory; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import javax.transaction.TransactionManager; import java.util.HashMap; /** * Test to check that structural nodes are being removed on rollback: http://jira.jboss.com/jira/browse/JBCACHE-1365. * * @author Mircea.Markus@jboss.com * @since 2.2 */ @Test(groups = "functional") public class StructuralNodesOnRollbackTest { private CacheSPI cache; private TransactionManager txMgr; @BeforeMethod(alwaysRun = true) public void setUp() { Configuration cacheConfig = UnitTestCacheConfigurationFactory.createConfiguration(Configuration.CacheMode.LOCAL, false); cache = (CacheSPI) new DefaultCacheFactory().createCache(cacheConfig, false); cache.start(); txMgr = cache.getTransactionManager(); } @AfterMethod(alwaysRun = true) public void tearDown() { cache.stop(); cache.destroy(); } public void testPutDataMap() throws Exception { HashMap map = new HashMap(); map.put("k", "v"); assert !cache.exists("/a/b"); txMgr.begin(); cache.put("/a/b/c", map); assert cache.exists("/a/b"); txMgr.rollback(); assert !cache.exists("/a/b"); } public void testPutKeyValueCommand() throws Exception { assert !cache.exists("/a/b"); txMgr.begin(); cache.put("/a/b/c", "key", "value"); assert cache.exists("/a/b"); txMgr.rollback(); assert !cache.exists("/a/b"); } } jbosscache-core-2.2.2.GA/src/test/resources/0000755000175000017500000000000011376173772020571 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/test/resources/log4j.xml0000644000175000017500000000431711070703545022323 0ustar twernertwerner jbosscache-core-2.2.2.GA/src/test/resources/cache-jdbc.properties0000755000175000017500000000443610757176162024662 0ustar twernertwerner## # Standard JBC table properties # The table name can also be prepended with schema name for the given table. # Even though there is an Sql92 standard syntax for this: . #schema has different meanings accross various DBMS: Oracle - user name; PointBase - database name # Microsoft SQL Server & DB2 - schema name corresponds to the catalog owner cache.jdbc.table.name=jbosscache cache.jdbc.table.create=true cache.jdbc.table.drop=false cache.jdbc.table.primarykey=jbosscache_pk cache.jdbc.fqn.column=fqn cache.jdbc.fqn.type=varchar(255) cache.jdbc.node.column=node cache.jdbc.node.type=blob cache.jdbc.parent.column=parent # Specify your DBMS's string concatenation function syntax in the following manner: concat(1 , 2) -> '12'. # This syntax should work an most popular DBMS like oracle, db2, mssql, mysql, PostgreSQL. Derby - on which #the tests are run does not support 'concat', but '1 || 2' . If no value is sepcified then concat(1 , 2) is used by default. cache.jdbc.sql-concat=1 || 2 # JBoss Cache Table properties for Hypersonic, just overrides #cache.jdbc.node.type=OBJECT ## # DataSource #cache.jdbc.datasource=DefaultDS ## # JDBC driver specific properties # Hypersonic #cache.jdbc.node.type=OBJECT ## MySql #cache.jdbc.driver=com.mysql.jdbc.Driver #cache.jdbc.url=jdbc:mysql://localhost:3306/jbossdb #cache.jdbc.user=root #cache.jdbc.password=admin ## Oracle #cache.jdbc.driver=oracle.jdbc.OracleDriver #cache.jdbc.url=jdbc:oracle:thin:@192.168.0.100:1521:JBOSSDB #cache.jdbc.user=jboss #cache.jdbc.password=sa ## MS Sql Server #cache.jdbc.driver=com.microsoft.jdbc.sqlserver.SQLServerDriver #cache.jdbc.url=jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=jbossdb;SelectMethod=cursor #cache.jdbc.user=sa #cache.jdbc.password= #cache.jdbc.node.type=image ## Pointbase #cache.jdbc.driver=com.pointbase.jdbc.jdbcUniversalDriver #cache.jdbc.url=jdbc:pointbase:server://localhost:9092/jboss,new #cache.jdbc.user=PBPUBLIC #cache.jdbc.password=PBPUBLIC ## PostgreSQL #cache.jdbc.driver = org.postgresql.Driver #cache.jdbc.url=jdbc:postgresql://192.168.0.100:5432/jbossdb #cache.jdbc.user=postgres #cache.jdbc.password=admin ## Derby cache.jdbc.driver = org.apache.derby.jdbc.EmbeddedDriver cache.jdbc.url=jdbc:derby:jbossdb;create=true cache.jdbc.user=user1 cache.jdbc.password=user1jbosscache-core-2.2.2.GA/src/test/resources/META-INF/0000755000175000017500000000000011376173771021730 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/test/resources/META-INF/jbc2-configs.xml0000644000175000017500000012702010710030410024667 0ustar twernertwernerþÿ<?xml version="1.0" encoding="UTF-16"?> <cache-configs> <!-- Various JBoss Cache configurations, suitable for different caching uses (e.g. entities vs. queries). In all cases, TransactionManager configuration not required. Hibernate will plug in its own transaction manager integration. --> <!-- A config appropriate for entity/collection caching. --> <cache-config name="optimistic-entity"> <!-- Node locking scheme --> <attribute name="NodeLockingScheme">OPTIMISTIC</attribute> <!-- Valid modes are LOCAL REPL_ASYNC REPL_SYNC INVALIDATION_ASYNC INVALIDATION_SYNC INVALIDATION_SYNC is highly recommended as the mode for use with entity and collection caches. --> <attribute name="CacheMode">INVALIDATION_SYNC</attribute> <!-- With OPTIMISTIC locking we need to use synchronous commits. --> <attribute name="SyncCommitPhase">true</attribute> <attribute name="SyncRollbackPhase">true</attribute> <!-- Name of cluster. Needs to be the same for all members, in order to find each other --> <attribute name="ClusterName">optimistic-entity</attribute> <!-- Use a UDP (multicast) based stack. No JGroups flow control (FC) because all communication will require a synchronous response. --> <attribute name="MultiplexerStack">udp-sync</attribute> <!-- Whether or not to fetch state on joining a cluster. --> <attribute name="FetchInMemoryState">false</attribute> <!-- The max amount of time (in milliseconds) we wait until the state (ie. the contents of the cache) are retrieved from existing members at startup. Ignored if FetchInMemoryState=false. --> <attribute name="StateRetrievalTimeout">20000</attribute> <!-- Number of milliseconds to wait until all responses for a synchronous call have been received. --> <attribute name="SyncReplTimeout">20000</attribute> <!-- Max number of milliseconds to wait for a lock acquisition --> <attribute name="LockAcquisitionTimeout">15000</attribute> <!-- Indicate whether to use marshalling or not. Set this to true if you are running under a scoped class loader, e.g., inside an application server. Default is "false". --> <attribute name="UseRegionBasedMarshalling">true</attribute> <!-- Must match the value of "useRegionBasedMarshalling" --> <attribute name="InactiveOnStartup">true</attribute> <!-- Specific eviction policy configurations. This is LRU --> <attribute name="EvictionPolicyConfig"> <config> <attribute name="wakeUpIntervalSeconds">5</attribute> <!-- Name of the DEFAULT eviction policy class. --> <attribute name="policyClass">org.jboss.cache.eviction.LRUPolicy</attribute> <!-- Cache wide default --> <region name="/_default_"> <attribute name="maxNodes">5000</attribute> <attribute name="timeToLiveSeconds">1000</attribute> </region> <!-- Don't ever evict modification timestamps --> <region name="/TS"> <attribute name="maxNodes">0</attribute> <attribute name="timeToLiveSeconds">0</attribute> </region> </config> </attribute> </cache-config> <!-- A config appropriate for entity/collection caching that uses pessimistic locking --> <cache-config name="pessimistic-entity"> <!-- Node locking scheme --> <attribute name="NodeLockingScheme">PESSIMISTIC</attribute> <!-- Isolation level : SERIALIZABLE REPEATABLE_READ (default) READ_COMMITTED READ_UNCOMMITTED NONE --> <attribute name="IsolationLevel">REPEATABLE_READ</attribute> <!-- Valid modes are LOCAL REPL_ASYNC REPL_SYNC INVALIDATION_ASYNC INVALIDATION_SYNC INVALIDATION_SYNC is highly recommended as the mode for use with entity and collection caches. --> <attribute name="CacheMode">INVALIDATION_SYNC</attribute> <!-- Name of cluster. Needs to be the same for all members, in order to find each other --> <attribute name="ClusterName">pessimistic-entity</attribute> <!-- Use a UDP (multicast) based stack. No JGroups flow control (FC) because all communication will require a synchronous response. --> <attribute name="MultiplexerStack">udp-sync</attribute> <!-- Whether or not to fetch state on joining a cluster. --> <attribute name="FetchInMemoryState">false</attribute> <!-- The max amount of time (in milliseconds) we wait until the state (ie. the contents of the cache) are retrieved from existing members at startup. Ignored if FetchInMemoryState=false. --> <attribute name="StateRetrievalTimeout">20000</attribute> <!-- Number of milliseconds to wait until all responses for a synchronous call have been received. --> <attribute name="SyncReplTimeout">20000</attribute> <!-- Max number of milliseconds to wait for a lock acquisition --> <attribute name="LockAcquisitionTimeout">15000</attribute> <!-- Indicate whether to use marshalling or not. Set this to true if you are running under a scoped class loader, e.g., inside an application server. Default is "false". --> <attribute name="UseRegionBasedMarshalling">true</attribute> <!-- Must match the value of "useRegionBasedMarshalling" --> <attribute name="InactiveOnStartup">true</attribute> <!-- Specific eviction policy configurations. This is LRU --> <attribute name="EvictionPolicyConfig"> <config> <attribute name="wakeUpIntervalSeconds">5</attribute> <!-- Name of the DEFAULT eviction policy class. --> <attribute name="policyClass">org.jboss.cache.eviction.LRUPolicy</attribute> <!-- Cache wide default --> <region name="/_default_"> <attribute name="maxNodes">5000</attribute> <attribute name="timeToLiveSeconds">1000</attribute> </region> <!-- Don't ever evict modification timestamps --> <region name="/TS"> <attribute name="maxNodes">0</attribute> <attribute name="timeToLiveSeconds">0</attribute> </region> </config> </attribute> </cache-config> <!-- A config appropriate for query caching. Does not replicate queries. DO NOT STORE TIMESTAMPS IN THIS CACHE. --> <cache-config name="local-query"> <!-- Node locking scheme --> <attribute name="NodeLockingScheme">OPTIMISTIC</attribute> <attribute name="CacheMode">LOCAL</attribute> <!-- Max number of milliseconds to wait for a lock acquisition --> <attribute name="LockAcquisitionTimeout">15000</attribute> <!-- Specific eviction policy configurations. This is LRU --> <attribute name="EvictionPolicyConfig"> <config> <attribute name="wakeUpIntervalSeconds">5</attribute> <!-- Name of the DEFAULT eviction policy class. --> <attribute name="policyClass">org.jboss.cache.eviction.LRUPolicy</attribute> <!-- Cache wide default --> <region name="/_default_"> <attribute name="maxNodes">5000</attribute> <attribute name="timeToLiveSeconds">1000</attribute> </region> <!-- Don't ever evict modification timestamps --> <region name="/TS"> <attribute name="maxNodes">0</attribute> <attribute name="timeToLiveSeconds">0</attribute> </region> </config> </attribute> </cache-config> <!-- A query cache that replicates queries. Replication is asynchronous. DO NOT STORE TIMESTAMPS IN THIS CACHE. --> <cache-config name="replicated-query"> <!-- Node locking scheme --> <attribute name="NodeLockingScheme">OPTIMISTIC</attribute> <!-- Isolation level : SERIALIZABLE REPEATABLE_READ (default) READ_COMMITTED READ_UNCOMMITTED NONE --> <attribute name="IsolationLevel">REPEATABLE_READ</attribute> <!-- Valid modes are LOCAL REPL_ASYNC REPL_SYNC INVALIDATION_ASYNC INVALIDATION_SYNC INVALIDATION_SYNC is highly recommended as the mode for use with entity and collection caches. --> <attribute name="CacheMode">REPL_ASYNC</attribute> <!-- Name of cluster. Needs to be the same for all members, in order to find each other --> <attribute name="ClusterName">replicated-query</attribute> <!-- Use a UDP (multicast) based stack --> <attribute name="MultiplexerStack">udp</attribute> <!-- A timestamps cache must fetch state from other cluster nodes. --> <attribute name="FetchInMemoryState">false</attribute> <!-- The max amount of time (in milliseconds) we wait until the state (ie. the contents of the cache) are retrieved from existing members at startup. --> <attribute name="StateRetrievalTimeout">20000</attribute> <!-- Number of milliseconds to wait until all responses for a synchronous call have been received. --> <attribute name="SyncReplTimeout">20000</attribute> <!-- Max number of milliseconds to wait for a lock acquisition --> <attribute name="LockAcquisitionTimeout">15000</attribute> <!-- Indicate whether to use marshalling or not. A timestamps cache only replicates java system types, so marshalling is not needed. --> <attribute name="UseRegionBasedMarshalling">false</attribute> <!-- Must match the value of "useRegionBasedMarshalling" --> <attribute name="InactiveOnStartup">false</attribute> <!-- Specific eviction policy configurations. This is LRU --> <attribute name="EvictionPolicyConfig"> <config> <attribute name="wakeUpIntervalSeconds">5</attribute> <!-- Name of the DEFAULT eviction policy class. --> <attribute name="policyClass">org.jboss.cache.eviction.LRUPolicy</attribute> <!-- Cache wide default --> <region name="/_default_"> <attribute name="maxNodes">5000</attribute> <attribute name="timeToLiveSeconds">1000</attribute> </region> <!-- Don't ever evict modification timestamps --> <region name="/TS"> <attribute name="maxNodes">0</attribute> <attribute name="timeToLiveSeconds">0</attribute> </region> </config> </attribute> </cache-config> <!-- Optimized for timestamp caching. A clustered timestamp cache is required if query caching is used, even if the query cache itself is configured with CacheMode=LOCAL. --> <cache-config name="timestamps-cache"> <!-- Node locking scheme --> <attribute name="NodeLockingScheme">PESSIMISTIC</attribute> <!-- Isolation level : SERIALIZABLE REPEATABLE_READ (default) READ_COMMITTED READ_UNCOMMITTED NONE --> <attribute name="IsolationLevel">REPEATABLE_READ</attribute> <!-- Cannot be INVALIDATION. Hibernate will ensure timestamp replication is always ASYNC, so might as well configure the cache that way. --> <attribute name="CacheMode">REPL_ASYNC</attribute> <!-- Name of cluster. Needs to be the same for all members, in order to find each other --> <attribute name="ClusterName">timestamps-cache</attribute> <!-- Use a UDP (multicast) based stack --> <attribute name="MultiplexerStack">udp</attribute> <!-- Used for timestamps, so must fetch state. --> <attribute name="FetchInMemoryState">true</attribute> <!-- The max amount of time (in milliseconds) we wait until the state (ie. the contents of the cache) are retrieved from existing members at startup. Ignored if FetchInMemoryState=false. --> <attribute name="StateRetrievalTimeout">20000</attribute> <!-- Number of milliseconds to wait until all responses for a synchronous call have been received. --> <attribute name="SyncReplTimeout">20000</attribute> <!-- Max number of milliseconds to wait for a lock acquisition --> <attribute name="LockAcquisitionTimeout">15000</attribute> <!-- Indicate whether to use marshalling or not. Set this to true if you are running under a scoped class loader, e.g., inside an application server. Default is "false". --> <attribute name="UseRegionBasedMarshalling">true</attribute> <!-- Must match the value of "useRegionBasedMarshalling" --> <attribute name="InactiveOnStartup">true</attribute> <!-- Specific eviction policy configurations. This is LRU --> <attribute name="EvictionPolicyConfig"> <config> <attribute name="wakeUpIntervalSeconds">5</attribute> <!-- Name of the DEFAULT eviction policy class. --> <attribute name="policyClass">org.jboss.cache.eviction.LRUPolicy</attribute> <!-- Cache wide default --> <region name="/_default_"> <attribute name="maxNodes">5000</attribute> <attribute name="timeToLiveSeconds">1000</attribute> </region> <!-- Don't ever evict modification timestamps --> <region name="/TS"> <attribute name="maxNodes">0</attribute> <attribute name="timeToLiveSeconds">0</attribute> </region> </config> </attribute> </cache-config> <!-- A config appropriate for a cache that's shared for entity, collection, query and timestamp caching. Not an advised configuration, since it requires cache mode REPL_SYNC, which is the least efficient mode. Also requires a full state transfer at startup, which can be expensive. Uses optimistic locking. --> <cache-config name="optimistic-shared"> <!-- Node locking scheme: OPTIMISTIC PESSIMISTIC (default) --> <attribute name="NodeLockingScheme">OPTIMISTIC</attribute> <!-- Must use REPL since used for timestamp caching. --> <attribute name="CacheMode">REPL_SYNC</attribute> <!-- With OPTIMISTIC locking we need to use synchronous commits. --> <attribute name="SyncCommitPhase">true</attribute> <attribute name="SyncRollbackPhase">true</attribute> <!-- Name of cluster. Needs to be the same for all members, in order to find each other --> <attribute name="ClusterName">optimistic-shared</attribute> <!-- Use a UDP (multicast) based stack. Need JGroups flow control (FC) because timestamp communication will not require a synchronous response. --> <attribute name="MultiplexerStack">udp</attribute> <!-- Used for timestamps, so must fetch state. --> <attribute name="FetchInMemoryState">true</attribute> <!-- The max amount of time (in milliseconds) we wait until the state (ie. the contents of the cache) are retrieved from existing members at startup. Ignored if FetchInMemoryState=false. --> <attribute name="StateRetrievalTimeout">20000</attribute> <!-- Number of milliseconds to wait until all responses for a synchronous call have been received. --> <attribute name="SyncReplTimeout">20000</attribute> <!-- Max number of milliseconds to wait for a lock acquisition --> <attribute name="LockAcquisitionTimeout">15000</attribute> <!-- Indicate whether to use marshalling or not. Set this to true if you are running under a scoped class loader, e.g., inside an application server. Default is "false". --> <attribute name="UseRegionBasedMarshalling">true</attribute> <!-- Must match the value of "useRegionBasedMarshalling" --> <attribute name="InactiveOnStartup">true</attribute> <!-- Specific eviction policy configurations. This is LRU --> <attribute name="EvictionPolicyConfig"> <config> <attribute name="wakeUpIntervalSeconds">5</attribute> <!-- Name of the DEFAULT eviction policy class. --> <attribute name="policyClass">org.jboss.cache.eviction.LRUPolicy</attribute> <!-- Cache wide default --> <region name="/_default_"> <attribute name="maxNodes">5000</attribute> <attribute name="timeToLiveSeconds">1000</attribute> </region> <!-- Don't ever evict modification timestamps --> <region name="/TS"> <attribute name="maxNodes">0</attribute> <attribute name="timeToLiveSeconds">0</attribute> </region> </config> </attribute> </cache-config> <!-- A config appropriate for a cache that's shared for entity, collection, query and timestamp caching. Not an advised configuration, since it requires cache mode REPL_SYNC, which is the least efficient mode. Also requires a full state transfer at startup, which can be expensive. Uses pessmistic locking. --> <cache-config name="pessimistic-shared"> <!-- TransactionManager configuration not required for Hibernate! Hibernate will plug in its own transaction manager integration. --> <!-- Node locking scheme: OPTIMISTIC PESSIMISTIC (default) --> <attribute name="NodeLockingScheme">PESSIMISTIC</attribute> <!-- Isolation level : SERIALIZABLE REPEATABLE_READ (default) READ_COMMITTED READ_UNCOMMITTED NONE --> <attribute name="IsolationLevel">REPEATABLE_READ</attribute> <!-- Valid modes are LOCAL REPL_ASYNC REPL_SYNC INVALIDATION_ASYNC INVALIDATION_SYNC INVALIDATION_SYNC is highly recommended as the mode for use with entity and collection caches. --> <attribute name="CacheMode">REPL_SYNC</attribute> <!-- Name of cluster. Needs to be the same for all members, in order to find each other --> <attribute name="ClusterName">pessimistic-shared</attribute> <!-- Use a UDP (multicast) based stack. Need JGroups flow control (FC) because timestamp communication will not require a synchronous response. --> <attribute name="MultiplexerStack">udp</attribute> <!-- Used for timestamps, so must fetch state. --> <attribute name="FetchInMemoryState">true</attribute> <!-- The max amount of time (in milliseconds) we wait until the state (ie. the contents of the cache) are retrieved from existing members at startup. --> <attribute name="StateRetrievalTimeout">20000</attribute> <!-- Number of milliseconds to wait until all responses for a synchronous call have been received. --> <attribute name="SyncReplTimeout">20000</attribute> <!-- Max number of milliseconds to wait for a lock acquisition --> <attribute name="LockAcquisitionTimeout">15000</attribute> <!-- Indicate whether to use marshalling or not. Set this to true if you are running under a scoped class loader, e.g., inside an application server. Default is "false". --> <attribute name="UseRegionBasedMarshalling">true</attribute> <!-- Must match the value of "useRegionBasedMarshalling" --> <attribute name="InactiveOnStartup">true</attribute> <!-- Specific eviction policy configurations. This is LRU --> <attribute name="EvictionPolicyConfig"> <config> <attribute name="wakeUpIntervalSeconds">5</attribute> <!-- Name of the DEFAULT eviction policy class. --> <attribute name="policyClass">org.jboss.cache.eviction.LRUPolicy</attribute> <!-- Cache wide default --> <region name="/_default_"> <attribute name="maxNodes">5000</attribute> <attribute name="timeToLiveSeconds">1000</attribute> </region> <!-- Don't ever evict modification timestamps --> <region name="/TS"> <attribute name="maxNodes">0</attribute> <attribute name="timeToLiveSeconds">0</attribute> </region> </config> </attribute> </cache-config> </cache-configs>jbosscache-core-2.2.2.GA/src/test/resources/META-INF/unit-test-cache-service.xml0000644000175000017500000004546210754610570027110 0ustar twernertwerner jboss:service=Naming jboss:service=TransactionManager org.jboss.cache.transaction.GenericTransactionManagerLookup REPEATABLE_READ REPL_SYNC false 0 0 JBossCache-Cluster true 15000 15000 10000 2 org.jboss.cache.eviction.LRUPolicy 5000 1000 5000 1000 5 4 true / false org.jboss.cache.loader.FileCacheLoader false true false false jbosscache-core-2.2.2.GA/src/test/resources/META-INF/conf-test/0000755000175000017500000000000011376173771023632 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/test/resources/META-INF/conf-test/clonable-config-service.xml0000644000175000017500000002533210747470665031044 0ustar twernertwerner org.jboss.cache.transaction.GenericTransactionManagerLookup OPTIMISTIC SERIALIZABLE INVALIDATION_SYNC CloneCluster udp false 3 2 1 true org.jboss.cache.buddyreplication.NextMemberBuddyLocator numBuddies = 11 ignoreColocatedBuddies = true cloneGroup 7 false true true 45 4 org.jboss.cache.eviction.LRUPolicy 5000 1000 15000 5000 10000 5000 4000 false / true org.jboss.cache.loader.FileCacheLoader location=/tmp/FileCacheLoader false true false false pushStateWhenCoordinator=true pushStateWhenCoordinatorTimeout=5000 org.jboss.cache.loader.bdbje.BdbjeCacheLoader location=/tmp/BdbjeCacheLoader false false false false pushStateWhenCoordinator=true pushStateWhenCoordinatorTimeout=5000 org.jboss.cache.loader.jdbm.JdbmCacheLoader location=/tmp/JdbmCacheLoader false false false false pushStateWhenCoordinator=true pushStateWhenCoordinatorTimeout=5000 org.jboss.cache.loader.JDBCCacheLoader cache.jdbc.driver=com.foo.jdbc.Driver cache.jdbc.url=foo://driver cache.jdbc.user=sa cache.jdbc.password=secret false false false false pushStateWhenCoordinator=true pushStateWhenCoordinatorTimeout=5000 org.jboss.cache.loader.TcpDelegatingCacheLoader host=127.0.0.1\nport=12121 false false false false pushStateWhenCoordinator=true pushStateWhenCoordinatorTimeout=5000 org.jboss.cache.loader.ClusteredCacheLoader timeout=500 false false false false pushStateWhenCoordinator=true pushStateWhenCoordinatorTimeout=5000 jbosscache-core-2.2.2.GA/src/test/resources/META-INF/conf-test/local-passivation-service.xml0000644000175000017500000001236011001355154031422 0ustar twernertwerner jboss:service=Naming jboss:service=TransactionManager org.jboss.cache.transaction.GenericTransactionManagerLookup REPEATABLE_READ LOCAL JBossCache-Cluster 20000 20000 15000 1 200000 org.jboss.cache.eviction.LRUPolicy 5000 1 100 1 true / false org.jboss.cache.loader.FileCacheLoader false true false jbosscache-core-2.2.2.GA/src/test/resources/META-INF/conf-test/local-lru-eviction-service.xml0000644000175000017500000001107210746355150031514 0ustar twernertwerner jboss:service=Naming jboss:service=TransactionManager org.jboss.cache.transaction.GenericTransactionManagerLookup REPEATABLE_READ LOCAL JBossCache-Cluster 20000 20000 15000 5 200000 org.jboss.cache.eviction.LRUPolicy 5000 1000 5000 1000 5 4 10000 4 10000 8 10 jbosscache-core-2.2.2.GA/src/test/resources/META-INF/conf-test/policyPerRegion-eviction-service.xml0000644000175000017500000001614210746355150032737 0ustar twernertwerner jboss:service=Naming jboss:service=TransactionManager org.jboss.cache.transaction.GenericTransactionManagerLookup REPEATABLE_READ LOCAL JBossCache-Cluster 20000 20000 15000 5 200000 5000 1000 5000 1000 5 10000 10000 8 10 jbosscache-core-2.2.2.GA/src/test/resources/META-INF/conf-test/string-property-replaced-service.xml0000644000175000017500000001033510775002562032750 0ustar twernertwerner jboss:service=Naming jboss:service=TransactionManager ${test.property.NodeLockingScheme:OPTIMISTIC} ${test.property.CacheMode} ${test.property.SyncCommitPhase:true} true optimistic-entity udp-sync false 20000 20000 ${test.property.LockAcquisitionTimeout:15000} true true 5 ${test.property.EvictionPolicyConfig.policyClass:org.jboss.cache.eviction.LRUPolicy} ${test.property.EvictionPolicyConfig.maxNodes:5000} 1000 true / false org.jboss.cache.loader.FileCacheLoader location=${test.property.CacheLoaderConfiguration.location,java.io.tmpdir:/tmp} false true false ${test.property.BuddyReplicationConfig.enabled:true} org.jboss.cache.buddyreplication.NextMemberBuddyLocator ignoreColocatedBuddies = true numBuddies = ${test.property.BuddyReplicationConfig.numBuddies:1} ${test.property.BuddyReplicationConfig.buddyPoolName:default} 2000 false true true jbosscache-core-2.2.2.GA/src/test/resources/META-INF/conf-test/local-tx-service.xml0000644000175000017500000001016710747454754027546 0ustar twernertwerner jboss:service=Naming jboss:service=TransactionManager org.jboss.cache.transaction.GenericTransactionManagerLookup REPEATABLE_READ LOCAL JBossCache-Cluster 20000 20000 15000 5 200000 org.jboss.cache.eviction.LRUPolicy 5000 1000 5000 1000 5 4 false jbosscache-core-2.2.2.GA/src/test/resources/META-INF/conf-test/local-null-eviction-service.xml0000644000175000017500000001016310746355150031664 0ustar twernertwerner jboss:service=Naming jboss:service=TransactionManager org.jboss.cache.transaction.GenericTransactionManagerLookup REPEATABLE_READ LOCAL JBossCache-Cluster 20000 20000 15000 1 200000 org.jboss.cache.eviction.NullEvictionPolicy 5000 1 10000 1 10000 1 jbosscache-core-2.2.2.GA/src/test/resources/META-INF/conf-test/mux-service.xml0000644000175000017500000000563010746355150026620 0ustar twernertwerner jboss:service=Naming jboss:service=TransactionManager jgroups.mux:name=Multiplexer tcp org.jboss.cache.transaction.GenericTransactionManagerLookup REPEATABLE_READ LOCAL JBossCache-Cluster 20000 15000 10000 jbosscache-core-2.2.2.GA/src/test/resources/META-INF/conf-test/replSync-service.xml0000644000175000017500000001534610746355150027613 0ustar twernertwerner jboss:service=Naming jboss:service=TransactionManager org.jboss.cache.transaction.GenericTransactionManagerLookup REPEATABLE_READ REPL_SYNC false 0 0 JBossCache-Cluster true 15000 15000 10000 true jbosscache-core-2.2.2.GA/src/test/resources/META-INF/conf-test/mixedPolicy-eviction-service.xml0000644000175000017500000001556210746355150032120 0ustar twernertwerner jboss:service=Naming jboss:service=TransactionManager org.jboss.cache.transaction.GenericTransactionManagerLookup REPEATABLE_READ LOCAL JBossCache-Cluster 20000 20000 15000 5 200000 org.jboss.cache.eviction.LRUPolicy 5000 1000 5000 10000 10000 8 10 jbosscache-core-2.2.2.GA/src/test/resources/META-INF/buddy-replication-cache-service.xml0000644000175000017500000001774010757176162030577 0ustar twernertwerner jboss:service=Naming jboss:service=TransactionManager org.jboss.cache.transaction.GenericTransactionManagerLookup REPEATABLE_READ REPL_SYNC JBossCache-Cluster 20000 15000 10000 true org.jboss.cache.buddyreplication.NextMemberBuddyLocator numBuddies = 1 ignoreColocatedBuddies = true myBuddyPoolReplicationGroup 2000 false true true jbosscache-core-2.2.2.GA/src/test/resources/META-INF/local-service.xml0000644000175000017500000000353410757176162025206 0ustar twernertwerner jboss:service=Naming jboss:service=TransactionManager org.jboss.cache.transaction.GenericTransactionManagerLookup REPEATABLE_READ LOCAL 15000 jbosscache-core-2.2.2.GA/src/main/0000755000175000017500000000000011376174035016515 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/main/docbook/0000755000175000017500000000000011376174000020125 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/main/docbook/images/0000755000175000017500000000000011376174000021372 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/main/docbook/images/ClusteredCacheLoader.png0000644000175000017500000003617610750374443026132 0ustar twernertwerner‰PNG  IHDRÚúò 5gAMAÙܲÚ cHRMz&€„ú€èu0ê`:˜pœºQ< pHYs  šœ†IDATxí} Œ%Wuæ×V`bÇl‰åÇ3NlšÀÂ.`ÜØ€‡$óÀh“!Fj`Øxv`6îX±Ó›I†Fø§Çc?Gj"ñÌJÛ‘ìR”8-”g AòcC/êh·õ:DÛ4ðåIéà–çì=Uï¼{_uÕëzõ_·¾¶žëïþœ{ÎùÎùæÖ­ªZ­V#ü ø@º>@tn~ùê>ž®C¿Ð/ûÇ9µ…2 ø@Ú>R‘/©è;ü# ñ>²  ¢³ŠiàÀ’ô!, Å!IÚm!^À\0cÚwÿµ å ðä}@ã+ÿÄZur£m‘¼è´ê> ñeÜ ©ºR0~†4|À[Õ{Þã×¶€¯§áëh³Ú~¥ñbÛ?¸ý•ª˜`Ë;±V½m‹j'Ø? Ðø±H5©¤a<´Y® `‚­ê‰=ïñk[”ˇ€yØ« > ñbb‹T}À[Þ‰µêýk[ Q•!QAÆrù©ÆˆEªIÀ(0Ò°— ¶ª'ö¼Ç¯m¿LÃ×ÑfµýJã Ä"UbqîÜ#ôôÓ'©Ù¼î¹çÃÎ÷ù_í¢ ¶¼kÕû×¶°ßﲎ-ˆuð)/‹T’;ƒìñÇ?Ko¸êºô¢óéﺾðþ½Î÷ùÜ5û/vÊ€`Ø HlUOìy_ÛÂnŸË’T ÖÁ—Äß4¾@,'Ýîô÷î£7¾æzòx^8£^qzvðÇçø—á²Ï?ÿpârˆ±±Íø&ØòN¬Uï_Û"_Ÿ°“ˆuð#Ó—5¾@,MèLX¹‡Þ´—Î=2H&¼ä‚¹Ì/ßãÔašF¾ 5ÁVõÄž÷øµ-ìð­?* öëà;A¾¨ñb‘HôÑVY˜úôIDATÛèÖw^¨ô}Æ‚ëò£¨üž‹ Ãá|ù@m‚-ïÄZõþµ-ÊçGEÁ>b|'È5¾@,Iâ÷Þûúüûö*}Ç#ü® ~‘Váp¾| 6ÁVõÄž÷øµ-ÊçGEÁ>b|'È5¾@,IâÀly'Öª÷¯ü5È_w;Xß ò/‹Dˆ¦¶0`«zbÏ{ü:ðÁ_ƒüu·óˆuð Ñø±H„X`AÀly'Öª÷¯ü5È_w;Xß ò/‹Dˆ?‚Å߉û¸)·ÇMí® ¶ª'ö¼Ç¯ma—ú4Î#ÖÁw‚üJã Ä"bÁŠÆKc8?À™`Ë;±V½m øªŸ¯†=‡Xÿñó/‹Äˆ3y~Í-¿²vÔ§CøÕ¸x¥·`5ÁVõÄž÷øµ-ìô5¿`ŸÆ9Žuüù‚:b]bù# ;eݦƈE¢ŽÁcåò‡Å¼ڛË08¹¾pjg°7Á–wb­zÿÚvúZ–‰Dbÿ£±þľ§ñb‘(±`å2A`6Ïß á5~oã”Ϧs.‹/›Ú LlUOìy_ÛÂ^Ë’\p¬ã™VÄ:øˆ…Jþiƒ§ ù>$/Æä×tó[9ùZüã}>Çßá2\6myÐ~ú6Ò±NfçÔ§ ñËSÚùùCŸ”õ×5¾@,|‰3ö4žØˆC,L£/-ý>üŸ³Å&ØòN¬Uï_Û¢ø~câ=Í}Ä:øBRþ¥ñbÑ'^ÆžÆ;&’"â~³¿øÅ}ý1I9ló &تžØó¿¶E~þP,"ÖUÛþiù Æˆ…ó$F³~ÆKšX˜}È,ßåµO>©ßŸa–Ã~¶AÅ[Þ‰µêýk[dëEÁ\Z³~ãC¬«ži|U”X0cÏã ˜i‚MÀ-³×\ó*Ú¿ÿÄk10‹‘ÈM°U=±ç=~m‹üüApšÕ±®:¶Îʧ‚úÑøª±0;ÏRDYí¤Ô0ç³ ¦ßùÎÎZ žÅøøÇÿ#f1Rxu»©o¿}ly'Öª÷¯ma²A¬ÃŒ­_L°åX«Þ¿¶E1ˆâF6q£ŒzN:eÑžÆW‰+ Œ/3ÌÅÖihcãËôÿð'‘Hª ¶ª'ö¼Ç¯máï«ßÿþŸÐÏ~_$;Gõ;Ä [DÕgP½2ê9h,E=¯ñUPbÁ “_˯¬õé~5n^¯ô†ÌÙ‰¬€õä“Çé£} þy4=ý»‘Ž ¶¼kÕû×¶ð÷S¶ñ‹_üïèÈ‘ëèG?:ÉÞ£ú&Ç:þ|A½d±2ûûШö·©¼ÆWA‰+›?$Æ‚ò‡Å¼ڛË08¹N^_8…ÌåÛ¿üËÔh|œ®¹æUŽ/ X@,Îí);1[s¶±”9ï¼=tÓMo¢ï~÷ŽÔ †Ä þGQÙbd.Ì Âèç;'Ô~ÍÓ¨dQž 3cþn¯¹ð{§|6ËpÙ¼¾l*ú€ÌåÚ?þã4}îs×ÑK_ú¢~rlðÄ¢ZÄ´ý;Þñ:çÛC/¼p&5’Áqƒg<Ëë s9ãä«$·‚™Â 4Oòý1^ŒÉ¯éæ·rò ´øÇû|Ž¿ Âe¸l’ŠŠÚd.ض·¦¿ú«OÓøø~_2!@1‰…¹Ïþæ˜ËÈ¿öÝòúßqì•,õÁ}¹ýéÛZæ±9c!eÍí¾}¯ 3g&è_ÿõÁTâ âF6q£ŒzŽš‹²¬'X)±Ű3ðcCüL2¿ð„¼Ïçø@*ãŠôIDATš”+Ò¶Ì2ÿå_¦o¼Ú Äþð›é‡?ü³Âê9ŠÍò“ºúêÁÛ ¿­ÌXð5Ùç~ÃsùsËOËa¯>ŽE!iê‡ÛvÛF,¤þk_{ ýÓ?}Q‰œN",sÜ(c|¶=Ö¥å§Þv¥"ÞAà8 Æzý›¿¹^ÿúKéæ›ßìb¾×ÌÇû··§Ló°çsÏÍ:$ᢋ^ìŒS€á·5ÉÄ(²J[’8±ÍOÚþØÙmÆ‚ñ~ö³ï¦gŸ½Ç*Œâ϶•­J¬ËÂn__c‘…2Ї²ü¯°~ð]yå¥ôÄGàÉÎÂ:bRÁäâCzSªÿZËÃaˆE~„ ©žuàÓ>oúÛ0bÁäzuõÏ,˜u°ï¯Ë¢ë¥ª±.M»h|X P(Òðoÿö8Q§‹/~ <ùAúÕ¯}½±`‡äóñtÊýùŸ×zi:jÖm# I¥÷üÚÑÏ?ú ^¸ùï¤ÿdHÖ¾^Õþëü}? Ðø±è'Ð$[Æ6Ì©Àõõ™ú0‰…ŒËÙz{„ÇèG0r%[›ÔnµhiiÉùµZK´²Þq3tw–Œk\†¯¯u¶‰®qýeÚÜê%ömÕæb‹V6º½îf»³F-u~uSÕwþ¶hm¹E͹9jηh}°8Ñ–êq‘Ú«›½òD›«mG‘ו©M?÷¶½MëªíùæÍ5TŸ"œjÊ‘o‘–ûnÓZ[Áè§ßaÈøüƒ«I,®ºê2çIñylýuV&½ Ö¥kC/ ’ÒUvQÛ÷› ô“ÕXH9¾]bëí£I0ò$ÝåYß5 ók[ÔiÏø^[ØØ¸6MëŠ3tWÜ6§—4!àü¼ÑšrÚk¬0ƒØ¤Ùº»àQ‚F­6IÎ%.¬þZScnÿc³ärŽ.ÍöIê:ÜÆ˜Z»3ØvcÂÛvš«½VzòÕ&šäÐíUšàvgÚnÇþ/òˆÿz·lãË.{©óä?1低ãrÆJĺlì¦ñbQ¹àÁS|Ãﶇ_àdgñ;/çl¿=Âãd‚±¼|×P=ˆ>¼[ 6U=âßêÜ„“˜Ö:´µ¥f]2qt~VuumœZê_ö[Ý®zK—:.ñ|CÿÚ†JÍ[47ɉ¼Nm•»W›n›­A^Aí™qUf‚VT•ÍÖ´ÚWuÆghMõÛnp]ÜpF²½>ï^ç2µ)ZëMr8rtVè(Ÿ¯7hƒeêªúFÛë Gݺsо­/öúšœwdwÇÌíª±ñäÌÆ¢S~¦ÍÑþ´-üí~pwîïÁñúŽýmF/ˆuÑuF¿Þ2_=ÌuôIDAT *Be«ü<û“©@¾áwÛÃO6v¿óÞs¶ßñŽ7ì±[´d¨Í;„à(­õšè¬Ì9IvrþÿPÓùWÿ$-w¶ÒÁI}k‹3üVïÚ4¹4€hÉIìãŠXH›S´> V‡ãL$ÔQÿ5œÙŠ êçò­Ušš¢¹×êÒœÓ÷ÍÎN:€ Kÿ¯³DãÊwê•Þ)¿¶ÕìGÿîG‡fǤo–¯7¢Ú˜l®QÇ™µ#/ê÷bGÛ¢:˜ë§¶•C¬ËÞÇ5¾@,T8ÊÞY÷v*ÐO.v¿óAçl¿=4î ól!2Ÿo‘ušR6àvÆÆÆh\ýœ6ǧhå¹5wV w]úšh®ª5 «ýkõ‰ 'Éóõúì’š赩fúyûÞZqn7Œóí†î2ÕUùñÙe_©:m¹•¢f6šŽLîí·¸Ü¾™VâÓv} ínÔÌ*JãÊ769MSLnƧ©9Ë33^"ä+ZàIÑO­p¾ü±±.?j|X¨ ”Ÿ!Òî{Ô©@?yØYüÎ;W…Û#ÃÆo^Ó` ÌwÃ/l¶hŒüô­­.ºë Ôí¾!°½±à\›˜[VdaÛ±P·,x¾B®mÐÂâ‚úñËÞÜE¯Mž 0ÿ¶Ö\‚0ÝRåzÄBÏ8¨ÛKMj4ic{ƒ¦yvAÉ5VŸ ú¸Kv¦ôüÇŠsûfœ–zw.üÚžT·rúêVǸj¯vtQ­ÛtÇÌ·\:K½[$Î5êW·£ma/æMß«Ò>b]þ>­ñb¡"RþIC†(S~r°³øs·GÜ7sº: —ü¼¥df`vÙ½Ï ëfÚ›´¹ä®µ˜l¨§'Úmj«?ÁOtlöòÜêÀœ„Ó¼´)õœ§7Úkôÿzëxu§š5˜æd®Ö[4ÕÓ$íy·/¾Mòýyw}ÄÔÜ-*²¸0çÎtôg vÞ†‘5NÛÛ2Óâ¶½Üj:³#ÜOpˆ|Mgц”­ÑÄœš‰‰ñ§Ÿ˜ƒIË Öß5¾@,T˜*†Q’’ƒ§ù%VæK®â´‡XH¿U¾=¢Á-#º 0Ý—N *);·Fêsôgá¦;s ýð–“÷²s­N=>2ÐùJo¦Y§¦Ú{ÒYƒ1I«Ì+ÔßÖz‹&{3\¶>Õ¤ µÎb’ ÇÀm”-šã[ª ‡þˆŒ<ûà6Õ[¸ÜöØÄ -÷quǬÊö8Ñj“×pÔȼÕÒkv¤ŒWüÛrÇ>ĺbÙOã ÄB¦b'ª<æT ¿ÄÊ|ÉUÔ6¹;KœúR·ª·G4ØFÊ*¼íªœˆu¶Ä:‹B'­¬§ýBÖÄBdøÉOfèæ›ßL¯{ݯ«¯b–÷é̼iÇYk@ÛÄBpV”-bM±Ä¢Ä"¯©@¿ “±Yþîïþk©ŸÑÉ,ë4Šþ¼ж±|å½E¬Ó¾hO¬±(±È{*Ð/ÈäM,X¦2ßÑÉÌ›æpœµ´-t0÷óyœK_?ˆuþ:¶#ÖX†Xa*Ð/ Xˆ\e¼=¢“YÖiýy5 máÔÅϰMW?ˆu»ë·Ü±Ä"wbQ¤©@¿€Z$b!ò•iÊP'3ošÃqÖжØ=°‹¯a›œ®ëF×e9cˆEnÄ¢ˆS~A´ˆÄ‚å,Ë”¡NfY§QôçÕ€¶ÅèÞ8NˆuáôäOå‹u ¹ ž ä×Y§ñm çŒz¾¨ÄBÆSô)C̼iÇYk@Û"^ ßÃvw="Öí®£°~TžXb‘*±8wîzúé“ÔlÞF÷Üóaç½ ¿õ[¯¦W¿úâÂ>Bé•™ƒ1ËÏãàkaAe9–ë«_ýzÅ+^J×\ó*GÏE‘Y'³¬Ó(úój@Û"¹`Ÿ¥Ÿ¹/oÜ8q¢NˆuÉûY9bˆE*‰’ÿøãŸ¥7\u ]zÑùô‰w]H_xÿ^ºvß:ðΧ_Ù^ºfÿÅN.[„€$3c–ŸÇQt™?þŽÑ;¯ÜCïRz.ŠÌ:™yÓ޳ր¶Eò¿ÎC† ¸X—¬yõ\ìXb‘xRïv ¼w½ñ5ГÇkô™šÂûàÏñ5.ÃeŸþáÄå%È@ædƒ€©{̲N£èÏ«m‹ôìmÚÞö}Älü¨,zÖø±H4¡3A`åzÓ^:÷È ™ð’ >æ2—¿|S‡'@Eæ+ sh[i°yÓ޳րMtÇ3ôIDAT¶E6 !—If/‹Äùc}†¨[af*¼‰šëðm^—‘e‚Ìé' 6ošÃqÖжHßîYâ8ë¾7²ñŸ²éYã Ä"‘DÎ ž6òº /i{Ìu¹¬f- s6³ClY§QôçÕ€¶E6‰!넟EˆÙÄ2êYã Ä"bÁbòS~ 5à ®Ëmp[YÈœž5ؼiÇYk@ÛÄ"jŒAÜÈ&n”QÏ_ ‰$ñG½n}ç… «Ñg,¸.?"Éï_ˆ úQêAælô¬Á–uE^ h[€XŒ+̲ˆÙÄ2êYã Ä"‘$~。Ͽo¯Â_Çßá2\6l»i–ƒÌÉûÛKƒ-tþCÁ˜ŸŸ§—¼ä%´oß>ºõÖ[éôéÓôÔSOѯ~õ+ß´-Ò±ušø,jÛˆÙøRô¬ñb¡PzŽÁÎÀ ñ3ÉüB*þñ>ŸãkiöµmÈœ¬?h°ùæ:œŒ©ï}ï{tùå—÷ ëû‚ . ·¿ýítûí·Ó×¾ö5úñìô¢m‘¬£bͦzˆÙpÝ6EôIDATøT‘õ¬ñb¡N6~ª©g ¶˜Õ5ðÓŸþ”ÞúÖ· Ñ»l/¹ä’þu`±šX„ÝÓµ»`ÄÄÄ*UÐ` Ì‹¸C?ûÙÏè‘G¡ññqºñÆûäAôî·E‚I7Á@¿ÕÔ¯` ÄI5Õ¤Šƒ518C`U!7ÜpíÝë>¡ÄAmzzšn¾ùf_rÁ×tà«fàa÷4}@ãk„‹õåãÔœ;LsÍc´ºyf !mwNRkñ8­lœ8σð«'å×:½v¶OQ»uŒÚk§È¹Ö:NKKwô­Ö´¹ §HÓ)Ðv:þ¥Á˜'q!„‚Ȅ藷ccctÝu×õ „\cRÁr¼›¯ûÅ,©#± ±.¼ˆž±-Ÿ~5¾B‹-NïïƒR*7W5‰Xš¾Â½^ÿmõg‚ëu—oqÊÏ´O)¸+vï¤q 3w’\“~d»Ô-Ÿ¢ØLü×ÉløßH2qýõ×ÌLˆNy{Þyçѵ×^K>ø Zý4½èE/ˆUB*¸c©ŒËà˜%uë€iñl}Aã+±Ø^Ÿt9vˆÖÕ¬ÁúÂA÷x긂ªjxkš&ÀÝF¯ Å wbX½îÊ„S~v¹GN¶î¢ºjc|ö.’kÓ‹'i«{Š:÷·Ý',ƒƒq¡"û€ÛH9µ²…G%Ï<óL_W­V«‡Üxd’ .¤má™a1 ±Î_gEÆdËÖf_!ˆÅÊÜÛ@έ64YÐS´Ð¸…æ—N8Ç­CÎõñúg[Wä€Ë «'äa¦õ%ÚÞjPwã‡XpÝÁk§Õ‹˜ø'}g«(8&ô×4Ø,ðç«°dâÝï~·33a’ ³As…—Tp9m ¿³Øëüõ#¨o‡^5¾B‹U‡X •-¿ÁŸ¦¹:ÿëà m(Â1;Æû×ÓššÙVOȃ"[“XÈ9w{u0c¡B£Ÿ p®Èz?6 ö‰’"¦.e}…©àrÚþ˜³ˆ늌3ÈæïÓYêEã+±XiðL„ºÅ±)‚7¨¥q..«Ù†#4Ö» 2^×ë0f–î£aõ„XL«‹-gÆâøÎ‹ö}*œ¡ímþIߨfé(è+¾¿i°™)°šûi ÑäÖÖ–³¾"ˆTp9m »‹Yˆuþ:CŒ€^Ä4¾B‹íc.y¨¢ÖÊ4-6®u:6{'-Ϻ¤cvA=ÅÁOr,vBmâSêöFp=!ÃÖXLΡvÛ}2¤ÕºƒúOà_í*D™ˢ 6IÕÚ2™8sæ í¶s·Û»i×W #\_ÛÂ?ˆuþz) ¢¹ýEôIDATÖ g¾öÓø A,ØXÕ#ÆÍÕ§'©³}’ŽòlÅÄaãI³´ä “Ù¨i°øåË@&L-j[ÀGõc”‡Ïìæ_#ÍX@±»)×á#^Ð`3S\y÷ËF&LMk[ÀO½~ŠcøD\Ðø±Pq¤çlfŠ+×~™É„©im‹ôì ,A·Uõ/  «T}@ƒÍLqÅß·…L˜šÖ¶@ò«jòøÓó}/‹T“ œ8='.‹n5ØÌWÌ}É„©im øeYð9Ëã«_  ˜±HÕ4ØÌWœýgŸ}6ô{&zè! óXgqF7(‰¶Ey‚5+lUÐø±P‘Ž ¤çlƒI.Ï£*‘ SÏÚéÙX‚n«ê_   V©ú€›™â²ß7Éj\ä2·|žß€Yö™‰ íÊX«ø1nž4}@ã Ä"Õ¤’¦Ñv9‚„[PºK)÷¼ç=TU2ajWÛ¢¾ŒÃNeò/  ÌX¤êlfŠKod"X·ÚHXeJXµþªñb‘jR ʈ4í¤Áœðâ^ K&ø³â¶Þæ£Cm øeš>¶«é__  ˜±HÕ4ØÂ¤¾ðe@&ÂëJJj[T3ð#áÃîiú€Æˆ…Š9p6è =Ð`“ô} 2]w\SÛ"={KÐmU}@ãË r[õ)xþ<~ÐA‚>5%2™xøá‡i·˜U¿ÍF¿À4â| } ±@òL0y´Á e°…ý=ûì3—=üpãÈ{Þ3þ÷ç·ç?½òùë®{÷“=ôàyæ™W†m»ÊåüôˆsÁ> Ý@7Q|€cL­hFröÕ?è`Tÿ)š_ï&ÈDxâµ›.Ër]|uTGyÄEöñŸ"ú;ˆŒU®È`ó‰ê‘ ÓÄWA@¢ø€øéSEÙ±±±á6E\à‚LT›L˜þ#‰!JRAñӧвbbb‘2±•Lpù¢È‘’Ä’’ÅÄŠˆQ‹˜ÄâKÿ£FŸº»F‡OÔèÄ7\€œùVîüJ¾¤¶¦ÃœiÕèx£F'ÕÖ9ÿ”*w¶Fw}Ý„uBôIDAT=n¨úÇfT[ª½#ª~£'ÛɯªzêøãÇÇ÷s{ªþÀyu|Ÿj×é˸ÆåOüuO¾õ¸í;ÕøN©öÌqy¿(`™H/!1F‘I|µÈx2eC¬+Vÿ‰â{i×±ˆA,Ž}Z/ #ê±VɘoR„ÀæŽóŠxìW宸CETÒ¿Âó„ÆŸTç•|7yÎK_LdßÜÞ¡Ú•¾Ìó¼H› kÃêøîàXÌqi_Æ›6püÚ™™ðó‹ sâ«EÂO,ˆuÅ‹â?Aþ•çy‹ˆÄ‚‰€cØ+ÕÌ„úý—îí+2pzÞÝ¿E ¨;Ϋd}@%û_P³Š„p{‡1á:‡T»||'“5ÃÀ?çÜ[ÜS|®×“…†*ÇçøÇdD®äkªŸ“gÝöö«¾äÚ®õT[‡orëq;æXŠºŸ5ØF!ÆCŸÃm øâ«EÅ’È…XWÌØ'þ#þT¤-ˆEDb1}÷ `ùãMªYIÜa‰'ûûz‰ÿ j÷äê§HÉ1uŽokÀ_§úTÄâtOféç&UîŒ"§™„¨——k}I`ó#rmX½ Õ¿Ó¯jïmª^íc.aYŠºÍl  IqñÕ¢bIäB¬Ó1XtR„­øO¾˜t ½$=ª£œè‹iŸ[’¸ûIÝCúç îR%}qÞ<1˜Ìƒˆ…Y‡÷O©¶D†kjäÈc×<õú2ª¶¤_nwT=e]^Æ›4P‘‰óˆß€Ùh4ˆß˜‰¿¬5P.Â#¾š5FFí±®˜qOü'éX—D{ “å´š`Ãû†vºãŠlSÿÒ—¤>1¯¯1˜emCÿ¼šàYgÆB%üc_QeT¹ûÔm–ÉO»íêëº Ið;f,zk9Ψöøçô¥úfùÞ«wödå[.æ5YâW¯/£’ç&¾-£fK¤_n£¨¿$Á&db|üºVð0A&²¦ÁýX¤KĺbÆ»$c]dÂlÄ"b’<£nW8‹-UÂ=®’ø±^ò¾B%o!תsüt?µq\%ÿ†" ì W¨u wª:GþÐ=æ/ÿ*¸þnuäëj–à“îµcª ª/¿[!ת>ïTm8ýœUõ[Z!ÇzíñZ‰¾|CêI›så8¤Æ ryláÈ„û:mwÍf&‚}ÖW@,ÒÀ&b]1c_ÜXg¤÷A," ð©ÇzëY`#P³ ÃnCðµénYq ©sö)õØj/ù˵CêÑS3P>èO,¤¼lù6†!ÜþAžyPrÞ¡Hˆ”5·f=óüXÿìÐôIDATMJ^jÊRÔ}‘{ „%?FK·…•¹ §îDsÅlRíoÒÜÄÎ6¦[N¹N{Æí³7ó&:\ØxŽf=çÜkcÔVµgXÖ?¥ÿÛ«?³ÌD`ƒfzòUr·½=DþÿM “5›]vä ÿ?Ó.Åß=óñ0_Uý‰ÿ¤Eâ´ bb±cÖ¤Ì@õ‚Éß¾¾sÏ þ·9Â$§ðiK®69ùQˇWl¯Ï ~ŠÖd Û¡Nw‹:k ½ëujmlÓöº{|tQ5Ö]¢1&ͳ›­i·Þ䜢äÔsÊÖçò²Ò¨«ëãÔR³[Ý.uÕ¯Óé:3!Îqg…ŽrÛõmð5% m,:mζ;´ìÔ¯Órg£O`¦ÖÅ k{˜ü+qª»ò9 …ú_{§Œøj™ñÙó#Eâ?q@ZuA,@,¬$é’ 39…Êx½B[4¯þ%^«MÑúŽjÝ^Rž ÙÙI'Ñó¬€þ[§éÞL@sU.lQ“g"ê347Ãä îÌ$è:¼×¡Fû3”¨FI|ë4ÅÿòŸœß1«ÐiË-•J7š*©ª58+B ºÔtIfz·/¤×β[ËÏøÝ^é.;¤`bnUUQ·f§èèÔÍÌ(2ÒRôf{Í`¹ŒßD“Ë»Ý^<;áüuÛTWe.pýUš4êqók½µjHq£ôIDAT!Ú’}‰Å¸Qê å» ²Q1Ï‹¾óċٷ_Ì’ë~1D®ùÕ“òˆu;ã±è-îVü'H«.ˆÅbô~|qˆ;zïšàw<ÈÃøZP=y…¼?â¬" ò­¹&Î"[óûýz†ÌGzƒJyÞ^«†ÈÈ[?9ƒúão‚]3e1ûã}~=¸Ùg^û^¹Ò!f¢òÍpþ'7[ÎíŠÉ¦{›@Ú é17±Õ'¨>îþËŠ·ú[rf#T"Ÿ×ɾ_w{…&8±]0qö¯ª[$Ë ˜âÛ%êoy¡IžQïJ™W ?7™&æ–UýmwÆBÍZóä®Ýï¯åp‰†JúŠgðí ÷¶Ê -¯µ]’26ãÞr Ñ6È¿<;N5µÎ3Ùà*(f Žýb_ ª'1¤³ëëÒ"qÚ±0’´€ˆ·ÃÞï”S ØyÕ5uõ“÷M «'€ößj©Ú8 ê†ý~G¿^Oæ3jvBœkZ—ûf¢Âçø#ÃäYœo‰¨÷^˜ß‘k»~KDÉo~ƒÄÔ_^û¢äg&L2aîI|—]™•˜l,Òr»í>…¡žêøáüQÇfSs ´¨žY\˜sȸZ¼¸!ë#Ô­ŠÅåej;On,÷žÊP‰}Í]—1å™Éè‹"³cGi¡½B+Ëmš›âÛ&jfa]=õ±ä.Ü™Ú=¹VÝÇ>T3r«„o{¸Ý•†ª?NËjBEä›[ug)6y¦IËjˆ¶ƒäïÐ,­©Å‚ÓSàŽi—âæ…éwXÌrÊ ÖíøÞ’è.Ï­øOV]‹b1ìýøìL_V·ذûÕloå­–ÃêIÂî£CÝB9Ы»ãš"ßýèÉ,oÄ“—9r©™–GHHœÒŸß7ANÍ»môå&‹"%<þIž “¾³[`–Ûqa¥á>µ!2:Ûß=I ô7x{D= Âë"ÔbɆÏÓµÚõò8­/0)£Eµ˜3读¶H½§Ï±:Í4ÛÎíwá¥koS®E:œ?ELœÛ7Gu’— &ý…›rצ¿¦c‚þçý.1Ûåý~Ûª?ù·Vç6Ëmð|ñÉ„ÈE/â»ym‡Å,–)(† «'ñ¥Cëâ?¦OeÄ¢—¤½ –7aú} „ËîŠ/«ý[z/ž:©ÞO1¬ž€MB¶LJ‚®™/ܲ ² °û³JiGÊÉé-דúÈÙ—{ÄBä“­)‹œs¶jüü ‘+Ï­È•ÀS[‘œ'<ÔmŽØ¾¥£sÝÞ@îlÅØÌÒÀÙp Q°7,fõÄ2ƒÂôIDATã‚"„ˆuŠ€W:օLjE±Ù¿oô_q«À&3œÐnúŠz³æ»ÿúó«'Éüšíà—cöcñ_uônßï`Àߥ ÷kö%÷6oQí “Sdé¿™Sµ'ß¹Gf,†Èrð^µv£7V™­‰Ô’®b.Ç+Õ¥†š™ò[,¯aj;k+Ü5£7>ðeG<ƒe_M£¶7,f ‹!ÃêI|A¬Kï\â?Eðe¯ Ä¢(53áýˆ$ô[8¹*’p‡ºýÀ·jkô " »}CDfÎú¬±otÈS!æw?‚¾=Âß9¢HÀ]JG% /Â&çý=ò ý™ß‘  ×üdBb~ƒdÔ€–FùìÁ6zú³¢Æ¶z,5øîKä!ò¬Jôfƒ“¸7ðáX|5 ŒÒ&bZð®þ‘6Ê÷–FÑoZeÅŠàË^@,ˆ;ƒïûñŸªÑõ=a> "«¦# žjO¶$e&¼ô€ú—¿\g‘-“ßkjJŽoM82önÅ8uºKÉÀß&çísîl‡ôÃ[ù&ˆoêº)K ªùÉ]j-FZ Û®ŒÇëèéGΡ¨˜¸@,ÂâÄ[±n0"ÖÅÈ…JÎ^yG~?~¯Í¨õ¼ý‡9n¨¤.‹=Ô·µ ˆEâÙºD Æ †é‘O¹ÄW‹„Ũ1+j½(cG¬ss–øOÖ~¦?‹Ä"Šó£Îî„- e¶å]ëEõOàaaeÄWÓÀÚÌ'þd©wñŸ<|w·>A,@,v±É,qûÊlÖgë Ä".~P¿<„$ûX_   0;™z‰ò®õ¢Æ±cöu%1 ™—'™ÉVâ?;cRö¾ì•ÄÄÄÄÂÊ‘@õØaÇ’Š”¬ KyHŽøÏ0Ë눈ˆˆˆE,ˆFh$1 ™—'™ÉVâ?y‘‡aý‚X€X€XÄJ*–äd+†-Á i^“ÄP¤dYÊCrÄÒôѨmƒX€X€X€XXA+¢Á¼êIb@2/O2/’­Äòòßaý‚X€X€X€X€XÄòh3%’Š”¬ KyHŽøÏ°Ÿ×5  ‹XIÅ’œlÅ0¢%øÜ‚/¿ÁWýÌ¡ƒ(>b1Bàea;øŠYèc4}d—,¬ÈÈ– ¢œÄØ ÛÐ× ¾²‹uáñUØ 8Ï ó@£é#;°Y’“­FøÀ—ËL†ièË__Eðe¯ …#^q˜ ›"èÆŠŒlÉ Šàq >b1Âm€Ùé–äd+†ÿÜéŸÐ t’½-ÍÊ ôIDAT€X€XìðâÏŠŒlÉ âØuàIùˆˆˆE,°$'[1 $†¤Ú/Åñ‹XIÎÇùì¨kEF¶dÀ£˜‚ËnG  ÌXÄòKr²Ã@B*{B‚üvø0ˆE¬¤b‡ÌqìhEF¶dq숺ˆð¤|ÄÄ3±|À’œlÅ0’J h¾Ç@,b%8_ç³£®Ù’Av` v,»A,@,0cË,ÉÉV  ©ì òÛáà ±’ŠN0DZ£Ù’Aı#ê"À’ò  ÌXÄòKr²Ã@bH*1 øR±ˆ•Tà|qœÏŽºVddK<Ú)رìv±±ÀŒE,°$'[1 $¤²'$Èo‡ƒXÄJ*v8ÀÇŽVddKÇŽ¨‹8HÊ@,@,0cË,ÉÉV ‰!©Ä€vàKq|Ä"VRóÅq>;êZ‘‘-ðh¦`DzÛÄÄ3±|À’œlÅ0Êž ¿> b+©Øás;Z‘‘-D;¢.â| )±±ÀŒE,°$'[1 $†¤Ú/Åñ‹XIÎÇùì¨kEF¶dÀ£˜‚ËnG  ÌXÄòKr²Ã@B*{B‚üvø0ˆE¬¤b‡ÌqìhEF¶dq숺ˆð¤|ÄÄ3±|À’œlÅ0’J h¾Ç@,b%8_ç³£®Ù’Av` v,»A,@,0cË,ÉÉV  ©ì òÛáà ±’ŠN0DZ£Ù’Aı#ê"À’ò  ÌXÄòKr²Ã@bH*1 øR±ˆ•Tà|qœÏŽºVddK<Ú)رìv±±ÀŒE,°$'[1 $¤²'$Èo‡ƒXÄJ*v8ÀÇŽVddKÇŽ¨‹8HÊ@,@,0cË,ÉÉV ‰!©Ä€vàKq|Ä"VRóÅq>;êZ‘‘-ðh¦`DzÛÄÄ3±|À’œlÅ0Êž ¿> b+©Øás;Z‘‘-D;¢.â| )±±ÀŒE,°$'[1 $†¤Ú/Åñ‹XIÎÇùì¨kEF¶dÀ£˜‚ËnG  ÌXÄòKr²Ã@B*{B‚üvø0ˆE¬¤b‡ÌqìhEF¶dq숺ˆð¤|ÄÄ3±|À’œlÅ0’J h¾Ç@,b%8_ç³£®Ù’Av` v,»A,@,0c( 7bIDATË,ÉÉV  ©ì òÛáà ±’ŠN0DZ£Ù’Aı#ê"À’ò  ÌXÄòKr²Ã@bH*1 øR±ˆ•Tà|qœuá?ðø|À>øÿÞ­ø飄IEND®B`‚jbosscache-core-2.2.2.GA/src/main/docbook/images/LocalCacheLoader.doc0000644000175000017500000005500010660354341025171 0ustar twernertwernerÐÏࡱá>þÿ (*þÿÿÿ'ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿì¥Áa ð¿Njbjb„,„, 0îNîN Dÿÿÿÿÿÿˆ˜˜˜˜ÀÀÀöþþþþ  öü¶"""""kkksuuuuuu,²R^¡Àkwôkkk¡‹˜˜""U¶‹‹‹k˜Ô"À"s‹Ô蘘˜˜ks‹‹'lTÀs ßéäÁþ{?sÌ0üG,b‹bs‹ÀööäÚ$ööÚ CACHE CACHELOADER CACHELOADER CACHE REPLICATION STORE STORE  )*01=>DEKNíéÛéÛéÛéÛéËéÛéÛéhï<»hï<»56CJOJQJhï<»hï<»5CJOJQJhï<»#jhï<»UaJmHnHsHtH )*01=>DEKLMNýýýõýõýýýõýõýõýýý$a$gdï<» Mþþ.:pï<»°|. °ÈA!°"°# $ %°°Ä°Ä Ä¥<@ñÿ< NormalCJaJmH sH tH jj b}È Heading 1#$ & Fdh¤ð¤<@&gdÙ@L5CJKH \^J_HaJ DA@òÿ¡D Default Paragraph FontRióÿ³R  Table Normalö4Ö l4Öaö (kôÿÁ(No List!(5<CNÿÿÿÿÿÿÿÿÿÿÿÿ'ÿÿÿÿ&ÿÿÿÿ)ÿÿÿÿ*ÿÿÿÿ+ÿÿÿÿ!(5<CFÿÿNÿÿÿÿ ÿÿ+£N )*01=>DEKO˜0€€Ø®€˜0€€°Ê€˜0€€Ø@­˜0€€°Ê€˜0€€Ø@­€˜0€€°Ê€˜0€€Ø@­˜0€€°Ê€˜0€€Ø@­˜0€€°Ê€˜0€€Ø@­˜0€€°Ê€˜0€€Ø@­€˜@0€€°Ê€š0€€Ø@­€O:A8ÐNNMð8ð.*.@ñÿÿÿ€€€÷ð ð)-ðªð( ð ððH¢ ð) # ð €ÿ ðð ðððb ð!ð˜ ð ðÿ#"ñ ‘ðððT ð C ðÿÿ™¿ËjJÿð!ð|ððN2 ð 3 ðÿ™¿ÿðu ¤Ý ð ðN2 ð 3 ðÿ™¿ÿðñtYÜ ð ðN2 ð 3 ðÿ™¿ÿðu tÝ Ü ð ðN2 ð 3 ðÿ™¿ÿðù taÜ ð ðN2 ð 3 ðÿ™¿ÿðu ø Ý ` ð ðN2 ð 3 ðÿ™¿ÿðù ø a` ð ðN2 ð 3 ðÿ™¿ÿð}ø å` ð ðTB ð  C ðD¿ÿð) ) tððTB ð B C ðD¿ÿð¥ ) tððTB ð  C ðD¿ÿð) ­ tððTB ðB C ðD¿ÿð) Ü ­ ø ððTB ð C ðD¿ÿð­ Ü 1ø ððTB ð C ðD¿ÿð­ Ü ­ ø ððZ ð 3 ð€‚ÿ ð!` a|ð ððf ð S ð€™Ì¿ËjJÿ ðñ|1˜ð ðððh ð!ð˜ ð# ð ÿˆ#"ñ ‘ðððT ð C ðÿÿ™¿ËjJÿð!ð|ððN2 ð 3 ðÿ™¿ÿðu ¤Ý ððN2 ð 3 ðÿ™¿ÿðñtYÜ ððN2 ð 3 ðÿ™¿ÿðu tÝ Ü ððN2 ð 3 ðÿ™¿ÿðù taÜ ððN2 ð 3 ðÿ™¿ÿðu ø Ý ` ððN2 ð 3 ðÿ™¿ÿðù ø a` ððN2 ð 3 ðÿ™¿ÿð}ø å` ððTB ð  C ðD¿ÿð) ) tððTB ð!B C ðD¿ÿð¥ ) tððTB ð" C ðD¿ÿð) ­ tððTB ð#B C ðD¿ÿð) Ü ­ ø ððTB ð$ C ðD¿ÿð­ Ü 1ø ððTB ð% C ðD¿ÿð­ Ü ­ ø ððZ ð& 3 ð€‚ÿ ð!` a|ð ððf ð' S ð€™Ì¿ËjJÿ ðñ|1˜ð ðð`B ð( ƒ ð0D¿Ë8cÎÐÑÿðððZB ð* S ð€™Ìÿ¿ËjJÿðð ððZB ð+ S ð€™Ìÿ¿ËjJÿðð ððTB ð, c ð$D¿ÐÑÿðððTB ð- c ð$D¿ÐÑÿðððB ðS ð¿Ëÿ ?ðN-½D ½0t,!D !0t+0-$ht*eûÿÿ0‘ ht)‘ ˆM( t(u ˆµˆtMœþÿÿ-$D t±úÿÿœþÿÿ‘ D t O OŸ4v5°2Bÿÿÿÿÿÿÿÿ"„Є˜þÆÐ^„Ð`„˜þ56CJOJQJo(†*‡hˆH.0€ „ „˜þÆ ^„ `„˜þ‡hˆH.‚ „p„LÿÆp^„p`„Lÿ‡hˆH.€ „@ „˜þÆ@ ^„@ `„˜þ‡hˆH.€ „„˜þÆ^„`„˜þ‡hˆH.‚ „à„LÿÆà^„à`„Lÿ‡hˆH.€ „°„˜þư^„°`„˜þ‡hˆH.€ „€„˜þÆ€^„€`„˜þ‡hˆH.‚ „P„LÿÆP^„P`„Lÿ‡hˆH.Ÿ4v5ÿÿÿÿÿÿÿÿt¬        ÿ@€à<5"N0 @ÿÿUnknownÿÿÿÿÿÿÿÿÿÿÿÿGTimes New Roman5€Symbol3 Arial ñˆðÐhó±Fó±F !ð¥À´´€~4µððÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿjV‰ÿÿ Manik Surtani Manik Surtani þÿ à…ŸòùOh«‘+'³Ù0 ˜¤°ÈÔä ü $ 0 <HPX`h'Manik SurtaniNormalManik Surtani1Microsoft Word 11.2@d‰@ºPChDÇ@¼´ÌiDÇG þÿÿÿPICT €Zÿ ÿþHH€Z €€ÿÿšÿ€´€ZHH®± ®¦4€Z€Z§ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿÿÿàóÿàûÿ/ÿÿöóþðóóÿõóðóûÿ3ÿÿöó~`ðóóÿõóðóûÿ/ÿÿöóþðóóÿõóðóûÿ-ÿÿôóðóóÿôóðóûÿ-ÿÿöóüòóóÿöóüòóûÿJÿÿùóþóóóóôóóÿùóþóóóóþõóûÿGÿÿüóþþóþýóþ÷óóÿüóþýóýóþ÷óûÿYÿÿüóýó~`ýó~`÷óóÿüó~`ýóýó~`÷óûÿCÿÿüóýóþýóþùóîýóþýóýóþ÷óûÿ'ÿÿîóúóêðó÷óûÿ-ÿÿïóýøóóÿïóýøóûÿEÿÿñóóóóùóóÿðóóóóúóûÿIÿÿòóþóþóûóóÿòóþóþóûóûÿEÿÿöóýþóþýóþþóóÿõóþþóþýóþþóûÿ[ÿÿöó~`ýó~`ýó~`þóóÿõóýó~`ýó~`þóûÿGÿÿöóþýóþýóþþóóÿõóýóþýóþþóûÿ5ÿÿZÐV°ZÐRJOçóóÿZÐV°ZÐRJOçóûÿ!ÿÿâóóÿâóûÿ!ÿÿâóóÿâóûÿÿÿàóÿàûÿYùÿþO BgBhBgBI> F†BIBhO B*> B*ýO çÿþO BgBhBgBIO > F†BIBhB*> B*ýO õÿ!ùÿîO çÿîO õÿ!ùÿîO çÿîO õÿùÿìçÿìõÿïÿ¼ÿïÿÒÿìÿïÿÒÿìÿïÿÒÿìÿïÿÒÿìÿïÿÒÿìÿïÿÒÿìÿïÿÒÿìÿïÿÒÿìÿûÿæíÿæùÿ!þÿûìO?ûòÿüìO?ûüÿþÿàòÿáüÿMþÿûO?üçç%))J1ŒúûO?òÿûO?üçç%))J1ŒúúO?üÿ%þÿäO?òÿãO?üÿ%þÿäO?òÿãO?üÿ%þÿäO?òÿãO?üÿ!þÿýèO?ýòÿþèO?ýüÿüÿäîÿåúÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ§ÿþÿ ÕÍÕœ.“—+,ù®0Ô `hpx€ ˆ˜  ¨ µ'  Titleþÿÿÿ þÿÿÿþÿÿÿ !"#$%&þÿÿÿýÿÿÿ)þÿÿÿþÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿRoot Entryÿÿÿÿÿÿÿÿ ÀF€ÙàÐiDÇ+€1TableÿÿÿÿÿÿÿÿÿÿÿÿrWordDocumentÿÿÿÿÿÿÿÿSummaryInformation(ÿÿÿÿDocumentSummaryInformation8ÿÿÿÿÿÿÿÿÿÿÿÿCompObjÿÿÿÿXObjectPoolÿÿÿÿÿÿÿÿÿÿÿÿ€ÙàÐiDÇ€ÙàÐiDÇÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿ ÀFMicrosoft Word DocumentþÿÿÿNB6WWord.Document.8jbosscache-core-2.2.2.GA/src/main/docbook/images/OnlyOneCacheLoader.png0000644000175000017500000004723510660354341025554 0ustar twernertwerner‰PNG  IHDRs½¥gAMAÙܲÚ cHRMz&€„ú€èu0ê`:˜pœºQ< pHYs  šœ†IDATxí] œUãúž.º¡{¤t§2ŠÜ%É¥\¥†$*!])ÑetUÉ(¦Ü"åD8u\Æ‘!ƒc\rÉ!—üëpâœ0Èe0êýÏÚóÍ·fÛ3í=³×m¯g~¿=kïuûÞõ¾Ïû¾Ïz¿o}+---Mø¡ˆg1 ²»?Þê€wãÔ/õ Ω%•ANc€¤Â[RQìxÅxO 8Œ2ÄBd™büPÄ@21  ‰…ˆE2íËs1^ Øcú¹[¥rè Ä@ò1`üËûÄvrcl‘|;Ów¨Ó°cÀø—­+$ìJáõ308»³…=±{}ýÆĺXç9Ã+ã_$ìþa÷—£°;›×‰5ìí[„;ÐþN`Àø‰…£IÅ ãñœÁ vg {b÷úú-‚…!ú<í ÿ"± ±`ÅÂQ ØÍëÄöö-˜¨‚¨(c°pjü‹ÄÂѤBÇ–c8a/»³…=±{}ýÆÄ¥Xç9Ã+ã_$Ž‹Ý»—ÊÖ­³dÅŠ¡2þÖß±Û舩ïˆvgó:±†½}c‹ÔÇÛ±…±Ž˜2þEbáHr‡“=ñÄHéÜ¡‰4kPS2Oª#ãΨa}ðë:µolíC‚‘Úiw¶°'v¯¯ßØ"µ1ç&©`¬#–4ÞŒ‘X$Xüøc¶ô9­¤RK6ŒO“]9jŠÓee?X‡mØûþþû]I—C›Koßîl^'Ö°·olá-&RÅ'ëˆ#;–‘X$5¡ƒ @¹çv©!»—–%Ñ俱Oˆլcà¤v#ñ{j8­ÝÙžؽ¾~c‹ÔÀ–—1¢2±®c]JÇxã_$I34J‚½{µµˆB,QÑ:T.Ø-’zßîl^'Ö°·ol‘z8s“d N!^(T×bmc¬K]ìÿ"±H±xüñr„êÚˆ§Ríp8Ý"—áf€`[Î;¹ÝÙžؽ¾~c çížÊ¾ÅXGüÄ·ñ/‹¤$r0x ÆÄ¸‰hÒïo‹s°j‘ZNkw6¯kØÛ7¶H-ŒÅ òN­c¬#vÊÖñ/‹¤ <>Š'=b ÔŒ—XàXœç*Ïp\<§¶;[Ø»××ol<ùÅ÷ëˆò°hü‹Ä")Iü¡‡†Êàë(}W¾bcñ(*æ¹(Ïp\<§¶;›×‰\æÚ¹ôIDAT5ìí[G~ñ}Æ:b§<,ÿ"±HJ_° ¿Œ=½†ÒwÕˆæºÀDZåŽëƒçÔvg {b÷úú-‚‡#¿ø>c±S‘X$%‰ÓÙèlñ8›×‰5ìí›ÀG¼–‡×=­g¬#vÊÈñ/‹¤ –élñ8[Ø»××oñZ^÷´ž±ŽØ)#Æ¿H,’B,8 ‰Î³yXÃÞ¾ |ÄkyxÝÓzÆ:b§<Œÿ"±H ±À#Xx/HU7Å9ø¸ij9®ÝÙžؽ¾~c‹ÔÂXyÞ‰õŒuÄNy¸2þEb‘bEsÒ:\,‡³;›×‰5ìí[«±°ï:Æ:â'VŒ‘X$X€Écš[LY›èÓ!˜—Sz§¦³Ú-ì‰Ýëë7¶HM¬Å öN¬C¬Ãë ú2Ö%-8a'·Ïiü‹Ä"©ÀÀ‹Ä \¼X,ž©½±œÇð §©ìíÎæub {ûÆ©‰57‰Žu¸)b¬#ž€=ã_$I%P.Ø<Þ‚1±fãÔ¯MÇ>Ø—o6M]Ç´;[Ø»××ol‘ºxs“\ Ö¡ÒÊXG<‘X¨äï´ó¡Tˆ~H ÆÄ4ݘ•háƒïX‡÷‚`ìë´<<¿ó6/OÇ&™íV¯‚äÇK[x‡‡òpÔõŒuÄ’Æ®ñ/V,Mêp:<žuóÍgÉQG$Mšì-'œÐZyäJ ž¼—K»³y™TÙ¶ì¬r­—xHÕ¶ëH0ì±N}O£³9ä o“Ûo¿H:v<@:tØ_n»íBKÏ‹](‡Ö\=´¹àû·ßÞæ(¹IÕ@”ë2þÅj…×䯨‚I ™þÃXG<OÆ¿X±HzRùå‰rÉ%ÇJƒueðàc䥗Ɨ¶Åk‡Æ~™™f¿üü ¥Ûô>\ßaíÎæub {ûÆÁÇ•bcqdÇ¡ñ/‹¤$s;cG%Õ‰X•;±ÐÑÇê*FyÇêý¹ –3Û-ì‰Ýëë7¶†üäó:^¡ËXGÙ±iü‹Ä¢JÄ¢"ÆnW¸þ‹XèmX&z>û±üîO'·;›×‰5ìí[ø+~öáDcc]ø0fü‹Ä"ab¡{e* {r6Xt{º+Ðûsé_'¶;[Ø»××olá_¼øÉ—ub¬#^âÁ¥ñ/‹¸‰E4c¯Ì˜ˆx‰…݈ºÝúõëÈ AeÇlØ÷ãw:¿ÝÙ¼N¬aoßØÂŸXñ‹똣lj1Ö/ñ`Óø‰E…Ä¢*Œ=–!*C,ôy¾ûn‘dg(}ÊdáÂþ1Çqèý¹ôG0°;[Ø»××oláløÉG눉ªâÑø‰EL¢¯i ôIDATbÆîÄU!v£¿òÊD¹ôÒã„U ÿ»³yXÃÞ¾±…ÿqc÷w'¿3Ö É—ñ/‹RbÍØ˜c"YÄB!Vã›o–^“ÞKÝÙžؽ¾~c ïðà_d¬ ·ý ñ/ ëI 'ª±Œ—lbaoCW1Ð/б6˜ù3ìûñ»»AÅîl^'Ö°·olá.üâsNU'b]c]ø0fü+¤ÄŒÝ‹0t6íܺŠÑ©ÓÒ¾ý~‚±¬bxçävg {b÷úú-¼ÃƒöS·–Œuá±µ[˜*¯ã_!#vÆŽ*EeF;—§ÔxÖ»A,ìr¼úêõÖX T1.¾øhV1˜ºÝ®ïXßíÎæub {ûÆ©ŸlëX±œ\gü+ÄÂ+ÆË€n -ªwÜ1@tcÁV1´nœ^Ú-ì‰Ýëë7¶HMbÁX·Lë¼Ã¶ñ¯&^3öX Ë+ba—UŒ!CŽ·ž(AãÅÇq°§ƒ• »³yXÃÞ¾±…wÁ×î‹ÉúÎXÛžŒu±õ’,ÜEŸÇøWŠ ¼ŸÃ‹±Ñ .ï·ˆ…–íûïo·ª;·víöV1œqB»³…=±{}ýÆÎØZû–KƺømÈX¿®ª‚]ã_)B,4coذ®5ÿ„Ûc'â5†Ÿˆ…]æþÓT1ìÆ*F+vgó:±†½}c w­ÝÇ’õ±®j¶c¬«šþ*±ñ¯‹Ý»—ÊÖ­³dÅŠ¡2þ2{öyÖc•mÚ4“ö·*±Þ(Z‘"œÞ-3ùqØætû‰ž_3{¾M5K~\‡ße>3½–tlQÍÒ3tîG™“Ä>‡ÝÙ¼N¬aoߨ"ùA58*/n0Ö%×^Ñzf¬KŽ~ùœXüøc¶ô9­¤RK6ŒO“]9iÊË~°Û°öýý÷»<­Pæä€4Úç°;[Ø»××olá?Œ2n¸c“ êÙq,Œù˜X€ @Ðs»ÔÝKË’‰hrߨ§eÃjÖ1O<ŠHö>”Ùàl»9y>»³yXÃÞ¾±…¿pZ™¸Ñ"€±Ž2û wÉŽ{Æ¿|J,PªêÝ«­Eb‘ˆŠÖˆ rs$[q2§¶ÓTdûжÙ-ì‰Ýëë7¶ðV7¯t+Šk±¶yë(³0TQüqs›ñ/Ÿ‹Ç†$,®ôIDAT!G¨®x*чcÐ-‚qn*•2ÓÑbáÍîl^'Ö°·olá¬2n¸c‹ ê9V<ñó:ã_>$`ð؈qѤ!Þß8çp«jA™Ý­ùÙ¹¢e³;[Ø»××oláN2‹ÆBôoÆ wâFõ• ü6þåCbG1ñB¬šñ ‹sà\n„2»£g7l™ì6ìÎæub {ûÆþ ŒîÄ ê9ÙqÈóÿò!±xè¡¡2øÄ:J•¯XàX<"‰9#ÜP(evGÏnØ2ÙmØ-ì‰Ýëë7¶ð±`Üp'nQÏÉŽCnœÏø—‰¦–{z ¥‡ª Ìu‰´ÜP(evGÏnØ2ÙmØÍëÄöö-üA,7܉AÔs²ãç3þEb‘âDàQf7œ#ÙmØ-ì‰Ýëë7¶ ±¨,΃7‚(seíãåqÆ¿|H,‚X¶¢Ìî”4½tšÊ¶mw6¯kØÛ7¶ð±`Üp'nQÏ•7^güˇÄ"ˆm(³;ƒ°¼tšÊ¶mw¶°'v¯¯ßØÂÄ‚qøD=W6Þxyœñ/ <„÷‚TõqSœçrCÑ”Ù=»aËd·aw6¯kØÛ7¶ð±`Üp'nQÏÉŽCnœÏø—‰ÄÉL(³?‚µôå—·ÊÆS*ERíÎöÄîõõ[ÄÆêÛoO‘¯¿^X);WwŒ±mQY}–w\õ\Þµøu½ñ/Ÿ 0LL‹)k}:Sãz5¥7ev'H¸åX6Œ— /Ø×«7›j}Pæ`:Ú¿þ5MFê!ûî[»4¹hßÀ’Ä"\ÄÂnûNhm½{h×®ÇHâ*žA‹u”9˜ñNç«d.µÏøžXà¢Q*DÿcbšnÌʉ ´ðÁw¬Ã{A°öM¦¢*{.Ê g+.¾K}t¸œrJû˜dB;ŠXØ¿ñüÆ>ún?²ÿÔéü!*nêmEÚ3ÝZößöŠ…Þ×¾l×n?ÉÉ$¿ü²Ø‘8øáNÜ¢ž+›‹Üó̵ҦM3éׯ«ˆÑ׌߹¹×:L½°çwß-²HBƒu­ëÔŽki'‰ÈªÏ¥'—ÞiÀØ"¶ïì©bA¼#Gž,ÿýïü”òƒDðœjû†%Ö¹a7ã_>cá†2؆ ²¸ ËÈ8BÚ¶m&Ï>;Ú ž tRrqÞy]½[óÂñ  ïA²Z6Ï`ÞŽ·ŠˆÈõGͰ|Á~ ¿ÇÖ¥ßõÖXç¤]Œ‘X0P(Òðë¯K$+«¯4n\OfÍÊß~»³T/šXX?sf†µß-·ôµŽs¨nŸ»"‚Ab‘¬ôîÝyLà‹ c Ü|õUçŸ qëam±.6ö“ã_$¥ 4Š â9ì¥Àÿ{Οôa'úú°_ªvàc O‰EÑ)ÈË“üü|ë“——/›¶F2ôÎm’oÛ†}°}Ka±H™m8~£ì(*IìÅꜹy²iûÎ’‘EqáÉSë7ïPÇ[E²ecž¬Z¾\V­É“mew)RíçæJÁæ%û‹ìØ\`É åÈT _îˆ>w±lSç^³j¹,_µNµ©…S§²äË•¥ Ë–u ¶vJŒó‹ |±ƒ«Xtè°¿õ$ˆÆ<—±u$½0Ö9kCã_$*$9«l¿ž?V)0–¬±ˆ…ÞÝ%©Ú=‚k´ /‰ÅΑ9]´ãêåš-ERX0/æøuÛ‹ËÙ–%ÛÀ96EΙ•oòóö¼ÉÖù–nƒØ!Ù‘ºÍ´´ábmÂÎê/orz¤ýôl‰pŽ’]2HÒƒs¤ËSO–=÷ÒÌès§ÉªÍ%g)‘/-s•Xt£x³dâ¼ó " Wâ¿–Gã7z ï¿ÿ¾Ö“xb(z;3V2Ö¹c7ã_$¡ (¢#V·G¬À °ÄZ¯×¥z÷®ãwnªPZÑKãlêðJþm^ži%æu[ ¥¨HUr#dbÌš-²ii†ÚÖSòÔ}ÑβS} w ê ¥Û¶«Ô\´]–G"Ï•»7¯Šœ3¯,¯‚y=Õ>™²I²#/K}WÇôœ'[T»KqLšŒÉÝn]Iñ¶5‘íØ'm²l))rXrn’1XŸ±T¶C¦êxÛ¹·­96s¹¢/"ÛrKھƒ=rÍ8¯º6g¶çZûÏ+(©ÔX$öÏØ"v }ï½›=Ÿ'?ü9¢FÙôIDATÛVñè…±®òº‹G¿Ñûÿ"±P‘É]å{Ùž.¢#V·G,Ù–Xë£×¥z÷HôõÆûÛ8[bIÐì]$k,B0F¶”¬,Ü´ÜJ²Ã×| «¬»þá²±°È"HêEEÈðE%Û²$BDò­ÄÞS }ÎɲÍ4¤¾ÊÒž*™÷\ª¾©ïVµ"SJsyÑfÉž^œ¦Ú~ŒuîcÜø‰… GîÀí6ã-Æ’+^b¡Mõî}ñ.³Å‘ùbî²M&ãÎ_}ÒÓÓ¥§úXçì9Y6©ñcJ¶év°Ì\µYQØ\º-#3ÓJòØ–‘¯*%çTÕÒ¼Ž¶‹6YÝ =Ñݰs£d¨ý{foŒ)UaîJQ„í«,™"Ý'‘Ýu÷M¶f%1ÎQæÜ;KHM¶¢4ùÒ‡gÉd›žY²*•™h"S´rWjÅk;î¼ØÈXçÍŒ‘X¨ ä!œn;ÑR`,y–Xë+Z†î‘Ю߾Í8[¹ù®â ;ò$ >klÙœg ºÐ!P¼}µ-sùFEŠ# Õez…ÞÖsÌRY—»N}0À²¤vQrNTìE["!+OíWB,LÅAuW䯒¥«re{ñvÉBuAÉ•ž‘)=#dgò:SÿØdußô”ü’ž‹X箺rJÿTW*icrÕ¸ÍÈ5£Ë¥0¿¤‹ÄÚE„JŽï‹±Eêú¼{aúÎXç=¦‘X¨ˆä½Aœ¡2¥ÀXr,±ÖdzŽÝ#ˬäÑa|É/z/]ÈÞégÐãæìù‘±ת§' ¤@}ðžèØQ’—o.S“°N¯Ï©³žÞ(Ø"Ÿ•Œ{X‡Ñªj…d®Æ[¬RO“¬‰´…n’kÆX×5yù:5¿I®ä®[©t”V þÜ £ÇTXç.Ö•–ȹ7æ­²ª#h -ß*kІÞWUb–«JLþLàKMŸÇ'SqÆ:àÙø‰… Sþ0J²ä@)“XÙ'¹ªÊ¹«B,t»aî1ÎV¹Œ€piA%e«k$c¹ä[7#•Ý–HÞ­mRÂGÊ4¾©d¦ý˜4u¾Gúõë*­[7• ?=b’Ytšão·5`lAb¡ýÌ/KƺTŠu$¾$^•c¯ˆ…–åÿ¸.ÐO˜dæve{Ñ0¶ ±Ðþåõ’±Î`1ub‰…¯ˆ…×¥ÀXAÆkb™‚Ü=b’Ytšão·5`la‚y,Ìsóúa¬‹­ãÔˆu$¾!~(Æ ¨~ Z® v˜dæve{Ñ0¶ˆÔ5θtV?Œu{Öo°c‰…çÄÂO¥ÀXÕOÄBˤ’¡IfÑiŽ¿ÝÖ€±Åž»Æ—ÉÓc]⺠f¬#±ðŒXø±+ˆú‘X@Π” M2s;²½h [$àcù×ŧGƺøôTž‚ëH,x™ì·ß¾Ò©Ó–žý"³Ifn§Q¶­c‹ä{7qîç¶¢ãFVV_a¬K>΂ëH,I”0þOŒ”ΚH³5%ó¤:2îŒÒ½]59âàšÒ´~ éÔ¾±µöõCÀ(OæãÛT³äÇuø]æ‹O¨-'¶­&')=Cç~Ù$³è4ÇßnkÀØ"ùß>ì… åŠƺäb,ZÏþŽu$IOê?þ˜-}Nk'é‡Ô’ ãÓdWNšò÷²¬Ã6ìƒ}ÿý®¤Ë‘H¡ÌÉ vÝ›dæve{Ñ0¶pÎÞvÛ§úwÆ wp=ÿÒQÐôIDAT"±HjBA€rÏíRCv/-K&¢É~cŸ– «YÇ<^"Êìlp0ÎæøÛm [8ks/üØí6+7Z0ÖQæø}Åø‰EÒ’9JU½{µµˆB,QÑ:T.p7eŽßi*kãln§Q¶­c çí^Y¼á8Ä Ä+$ÝŠâZ¬m^Æ:Êì,î‘X$-‘?þø9BumÄS©ˆv8ƒnŒËp3°Pfg ¶4ÎæøÛm [8ow7ýØí¶7ÜÁOÐôlü‹Ä")‰ 1n"š4ÄûÇânU-(³;Õ!ãln§Q¶­c wƒÛ ßö7܉AÔ³ñ/‹¤ <Љ'b ÔŒ—XàXœçr#@Pfwôlœ-:Íñ·Û0¶ ±¨lŒaÜp'nQÏÆ¿H,’’Äzh¨ >±ŽòÕÊW,p,‘Äü •uúDŽ£ÌîèÙ8›Ûi”íEkÀØ‚Ä"‘Xaß—qøD=ÿ"±HJ_° ¿Œ=½†ò¿ª Ìu‰´ìŽìÔwÊ쎞³E§9þv[Æ$•+ŒîÄ êÙø‰ER’xA@™Ý ÆÙÜN£l/ZÆ$$‰ßòƯb¿1þEb‘bIJev§¤iœ-:Íñ·Û0¶¨8@V6é†á8Æ wâFõlü‹Ä")Ä"ˆm(³;ƒ°Œ³¹FÙ^´Œ-H,*K‚7܉AÔ³ñ/‹¤ <„÷‚TõqSœ窬Ó'revGÏÆÙ¢Ó»­c ‹Db…}_Æ wâFõlü‹Ä"iIpNãÕN)³[Îæve{Ñ0ÏY›G'âTü­ãnŠ‚ë(³3ø7þEb‘Tb‚fŒ÷†`ÌE¬Ù8õkÓ±öõêͦ:àQfg ú5ÎæøÛm [8goíSaX"n â´XG™Á¿ñ/‹¤ ” Ñ?†Á˜˜¦³rb-|ðëð^ìƒ}ý„(³ÓÎæve{Ñ0Ï[ûÁÝ–qÃ,AÏÆ¿H,MêÂ3ɘ |Ç:ls;ÄÓeNn 0ÎæøÛm [$ׯñøUªïøá¦ü¬gã_$¾Lî©„Ât}ÆÙÜN£l/ZÆî$0áœ×JLÿ"± ±P]7é0ôIDAT ÎéÀ8[tšão·5`l᜽éKÔmX1`ü‹Ä‚I•ÄÂQ gs;²½h [0ù…5ùñºÃ¾ñ¯ˆÅ¶ceÕò!²|ÕhÙ¼#§L0..œ)y¹ceÓöì2ëaÄXÇéý·–œ§x‘ä–‚-‹ÄÚ–7Vòó'–~òò&ÉŽbçB°Q·NaÀ8[tšão·5`lQ1ÞcÅ,»ë*Ö¡Ö—áÑ“ñ¯¸ˆÅÉÍêPúØœ>xÕfC"ò³ZF¶g “¢Ò;àòÛ¹q€µÿ¼‚E*¶(Åïœ$=Õ\éó&‰Þ¦ÛÑËüá11ul­ñëve{Ö€±Eyø*?fiŸd¬+Ow\¯1Ö¥ñ¯8ˆEñ¶ÒÞW¶©ªÁ¶u}"¿'‚¢©’©HAä¤-%w{¤ QÑq;7 ¶öÏÞXBNЦH†:GÏì)¢·eåΔ¢‹¤°0ò).%,pXÄë6ÎöçDÇ5îjÀØ"v ©(fYØc¬‹Ä|Æbê!ŒÅA,6-?Ö"Ë7/)Qæ"Y·t€¬ÉŸaýÞž××ÚÞ3£‹µÌPäNXÑqš<ÌË›#ÅEKdçö‰±À±e·eËÎøè¶c„ &Ê[gSnÁ?O5`l{Å,ø+c]l½1–Q/À€ñ¯8ˆÅf‹Xt‘ME±”—-Ë3P­è#Ûe‘d§ã{/Ù¢*§ÉƒD/íÄB¯‹,ûIa †D@Dz ×ù ÇžfT6niÀØ"¶T³DëüäW”%6†½Ô‹ñ¯8ˆÅ¦¥¨D¨.ŽúB–HžÄ™»QU¶’ô’nžfƼüRÑqšXd©ŠE‘U±ûçŠEÁ r¤¸Ý6—^‡m'Ž?ãlÌî^kÀØ"¶+ŠYŒu±uƘ@½h ÿŠƒXo!}%oÓTÉ]ÚÝ*y¤gO’ÙÒ‘½N=Å'9r‡X!-s˜üXÁqšXT4Æbxö()(ˆ<’—7QJŸ aåBÅg‚9(:0ÎæuZeûƱý‡±.¶^‚âk”Ó[ûÿŠƒXÀX…›GÙh¦IFÖ),ž)cP­Èb{d™ä[Ot<õHjÌãÔù4±XºÉ ÞÄÐŒ¥fŒ…R/³7.bB%¡4~™Ö½×€±Eù8fÌb¬ œß‘d”q§tcü+Nb$'2²¨ì{²²Ç¹¯˜=_ e¢ŽÀq6ïkØ%0¶Ø“ +³*{ÜžäávÆÿcÀøWBÄÂÿFðÑF~Àq¶°§uï¯ßØ‚~â7?¡<ÁǤñ/ í‚oP^ƒmhœÍûÄv Œ-ü‹ú2mñÆ™þôIDATT ÿ"± ± ±rÆÙžֽ¿~c &¯ &/Êí_ìÿ"±p4©Ð üënÙÆ8›÷‰5ì[—náŸí„kÆ¿H,H,X±pÆÙžֽ¿~c‹ð{&vÚÚ- ÿ"±p4©¸eP¶ãßàaœÍûÄv Œ-ü‹ú2mT ÿ"± ±`ÅÂQ g {Z÷þú-˜¼‚š¼(·±kü‹ÄÂѤB'ð¯¸eãlÞ'Ö°K`lA\º…¶¬ÿ"± ±`ÅÂQ g {Z÷þú-Âì™Øik·0`ü‹ÄÂѤâ–AÙŽƒ‡q6ïkØ%0¶ð/^èË´MP1`ü‹Ä‚Ä‚ G1`œ-ìiÝûë7¶`ò jò¢ÜþÅ®ñ/ G“ À¿Nà–mŒ³yŸXÃ.±qéþÙNx°fü‹Ä‚Ä‚ G1`œ-ìiÝûë7¶O°gb§­Ý€ñ/±Ð+¹T¯‚Çëàù¡’ˆïÓ*% O3®Îc@„Ä‚É3‰É“N[¾ÓÂÙøñVÄgùø¤n¨›daÀ" vÞ;êŸú'‰È—½•&üP‰b@ãÇñ#ÍBQ¦pZÚvtbH4¡p’`@ãÇ>CbÁ5KôÄ1àtb Q Q¨ 4~H,WyÇe«äóô{ d_E:¨ß-G§‰Þ¦ÛÑˉj½­ô8[[£ýYÆîŠ`hY°Œ%§>§nG/g¼jÚÓëôÒ.‹^§—}Á²·éÕw-c +$;¼[%ˆ0 ƒW‰I·›£îÂ-YÚ©Jº“Ÿs[Éï¡% T%àc)ÐòŽÎ¬¯è¸ìÕ‘ý(‚aµ£ÎÑE£Ã•ÌK¶!A/Qdb‘ª à“£ˆÞVz\ ±ÈYkÚŸªŽAÛ *i’ú­Ûˆ%§>g´§ÚÒ/r~ëmÉb§äŸ©ªh× uçåRÛƒÄÂçõ£Ò)ïV‰b@'/“Úžªº? ËÇLÂu£êrP¥lŸ§ªªV`¿.%‰µ¢ãtÂî§ŽÍQI9[‘‘.%Çþi›"<àôIDATÙ%ä@o‹&S•<Ñ2ÎSÄëô¾åÉ©Ï9X“Õ–E@Täö’S*gE²(R‚k@ÕÅk›¡}\;>~Œ%¬Xìðn• <À€N ^'©%Äbª"±dRB(æ©d6 ]$™ÍT•ŠŽÓÉ\_£^‚””·m‘:¿Þ¦É‚–G“˜ÒêDŒ}Ë“3Ö9­}»¥ÉÜb¡åÓK»,zµT×?ê±ØzÒ²ºµÔr‘Xxà¼~T:eâÝ*1@ èÄàV"*¯] ½Þ$̱ŠlŒVwø9ªÒвäθC Á€ÜýT5£¢ãt2ï«*KÊ«X<i/ƒ6KSêã«„o—wŠ"$h×.£;1@§"9c³’ºž[uÅ¢YúܦÆn”TLtµÆ.›Wß5~üKX± ÙáÝ*1@ x€¼JLºÝÒ¤¬íX•hG—$Ñ–*™ë„>ÉU‘„‰ªû¡‹Jði}ÒäM:b§“yiåA‘ gcÑ]’JèxO_à }\w%ž±¶©}î|2B,Òº©Šú=EíoɡΉA˜ɹ°„<èö«.辯:i{È¢IÎh5æÇaÌ…Ö—KÈ‚‰…ÎëG¥S&Þ­Ä€N ^&'Ýö"UÞ·|ì22òHf/$/E"ìO‚è'/Æ®WûÄ:ÎÖM¡“ò2•üqþ.Š0èd®¯_/ABbnSÕtMXm©ïzT¦(–©jGEr^}·í˜’dÜožªrØä,=gÉv»,¥× ÚéSÒþ”’1!Z^,µÌ~Œ%¬Xìðn• <À€N ^$¥òÚÄ ÊlEÊÛ^ÞúÊWÞù*Z'Iô`ÏŠöKõm?$8¯•N™x·J :1¤zäõ%NÔâÑ™Æc +$;¼[%ˆ0 COßu×W}ñÅçîéœÇsôë]tá#¿üòsÝ=íËíþÇ(‰‰‰1@  cà?Škäç¿Ô}âÄ ·zh‡Íûï¿ß¯¸bØ=k×þ-ã矪—xï½wÓ£+‰Ï}ýE6H,P(tb91íA{¸…ï¿ÿ®ÁêÕ€—5,ìÚµËÛS§Þ<c"H ˆCC   b€ ÊÅÀ§Ÿ~ÒváÂãðø'º8Î>û¬§–.ÍÁ±$šHD/I,PÊ (Ñ`áob õ1€.Ž ^ì1~ü¸:´ÿ¨yóý¿>üÊeÿûºs9"õíŸ '± ± ± ˆcà»ï >üðª‹/¾xàà 6øî¨£Ž|kúôiÓÞzëÍ£’‘hxŽp‹:|¸žö¦½5>þø£ö ÌߣÇÉÐÅqî¹çüýî»—]ùå—ÛÐûpI¼T$$¼[%ˆ` ¸ø÷š/¼×sܸ± Ûµkû &¢1bøÒ§žzò좢_êT&ðX ±A@‰ex®c@ R……ß6ÂÌ–\´]GÝí3¦O}ûí]iÿÔ·¿W6&± ±àÝ*1@ ¤6oþðÐ[o7±{÷“òÑÅ‘‘Ñwí½÷Þ3쫯¾lîU¢a»á"1$)Pè¼ár^Ú›öð¾çŸ_ßk̘ëµiÓz˶ü⪫FÞõÌ3O÷ùõ×¢ÚÄ qâ6H,H,x·J ÃÀ7ß|ÝdÅŠ/Á4Ø ÔÿþØc)¸å–¬›Þ}÷#ÜN"lÄ%$ (Ñäo:51 |øá‡Í;熓N:ñåúõ÷ýáüóÏ{â¾ûî½üÿûï~Ä@80*-`ÀôIDAT;“XXðn• |ˆtq¬_ÿÜi£G_›ÝªÕ!ÿ÷—¿øùÕWZ’›ûLovqHø™dXø0 ø0”px…øƒ>piÿþ¬AUâ¸ãŽ}mÖ¬™7nÚôÞáÔ»sz§n“«[  Þ­Ä€‡ø×¿Þï4{ö¬É'œpü« ýúÿøòå÷_¶cÇÿš1á%7áQŸîè“ÄÂÀB»rê™zö~ûí×ZÿøÇ³g\sÍÕ‹9äà­|жk¯½æ¬Ã6?ÉJYè;•Á‰‰ïV‰bÀa  ú€*ª¨J :1gÎìIï¿¿©se7aÂ÷3H,(~6>ecp"œÃÆE`|ÆI€L`ÜÆO`õîœÞ©[ïuKbAbÁ»Ub€Hð¤žØÀ“ô—ãI<Ñ';ð„ž÷ 6pÇ$I(«;`¥ž©g¿asH`.‰óÎËøª˜csM|ðÁ¿:úMVÊCÿq $$¼[%ˆ0€Ù-1Ëå1Çý:f½Äì—=´"³aº¸ÙI‚Ÿ1@b‘@@ñ³!) 1à ÐÅñôÓO…÷oà=xÞË‘—÷ü©ìâpFçÄr°õJbAbÁ»Ub€ˆÂÞzÏ=w_7ƒâ ¡'ŸÜý%¼1oeÒ vÒ£ýœ·‰ET@!èœuLûï¼óv—3¦O=úèno4lØà»üuåʇ~ÛÈòR&ú‘_1@bAbÁ»Ub ”(*ú¥ÎSO=yöˆ×¶hqÀö¶mÛ|:nÜØ…/¼×³¸ø÷š~ Ú”‹„Âï ±`R eRñ»cR>g’Ç—_n?àî»—]yî¹çü]=zœ¼aÁ‚ùã?þø£öÔ¹3:§^çW   b ¥1ðÖ[o5mÚÔéGuä[èâ¸øâ?ü𪋿û®°!“^ø’mî¼ÍI,˜TR:©0ˆ8Dü¦ã_~ù¹îßÿ¾îÜ+¯¼âîæÍ÷ÿªC‡öM˜0~þ† /öøãâ~“—ò„£©ns   b ðؾý?-–.ÍqöÙg=….Žž=Oyá¶ÛŽýôÓOÚ¦zçõ‘˜ø $L*O*~s*Êã| ß½{Wµ7Þxýè©SožÑµk—·5jX8xð •«WÿuÀ÷ß×€6pÞÔ1u\H,H,H,ˆ@`à矪·víß2®¸bØ=ûï¿ß;ìÐ'Nœpk~þKÝÙÅÁ$W^’ãz÷±AbÁ¤ˆ¤Âàà~pðƒÎ¿øâóïºëΫúôéý ºLì'ÈôIDAT8zõ:õùE‹nóÙg[ZûA>ÊN\ÒîÛĂĂĂð ÐÅñúëÇÜtÓ”[Ž8"ýÝÆ}{É%™+}ô‘ øáûú ètê‡úñH,˜T|“Tüà”ÁýÀüÓO?îýÄŸùåCïk֬鎎ûà†®ŸûÊ+/Ÿ¸k×Õi÷mBSçUÁ‰‰‰1T  êðÞ{ï¦W˜þýïm-Y²øêÞ½ÏÌEÇé§Ÿö\vöí£·ný¿C*:ŽÛ˜ðˆÿc€Ä‚I%©I…Nï§wÒF˜C¯Ç+Åíí€l¼öÚ?»ñÆÉ³ÒÓ¯iÓ&_rékÖ<ÚçÎöµïËïáÆí|û“XXXIÁæ’èÖí¨7ÓÒÒ¤zõj»Ôïü±~—]6d9ˆDçÎÞŸmóþû›:cÂ'L;›ûLo fÄû,î¿ÿ¾¡xܳHΙ3{ÒôéÓ¦á®oá¼úêQK0m5*ƒ]¼ªÿ Öôí{î:<šyÚi½Öã['œpü«è–8òȮёíÛ·û¸uëVŸrÈÁ[ÿò—?oÙ²Å0õ5N6iÒø¼O£~ý}Ø{ïz?Õ­[ç—:ujÕ®]ë×Zµöúm¯½jþ^³fb|jÔ¨þ*ŠHìÖd¾¬V­Ú.ì‡cp,Îsáœ87Ú@[hmCÈ™ d„¬²ãðJs\® ׈kÅ5_pA¿Çðt¯@'ÐÍØ±cn›4é†9xOÈìÙ³&/\¸`Üw.uß}÷^¾jÕÊA¨¦<ýôSgám¦¯‹q!Ÿ|òq»ÿüç‹–xUúo¿ýZ‹I’I’H$$5q úEgx‚áóÏÿý—M›Þ;3á+VOc,ɰa—ß òòwï½÷ ø‘õëŸ; UL)þÍ7_7á^Áõy¿Äž ÉAbAbꊪ}´¹*<²ú"„›o¾)kĈáKqŒ;ãN:þ wÓÉ$Õª×:{×—úMšKÓ–­¥E›ÎrpÇ£¥]ד¥ãqgJ—SΓng ”ãϹLN¾`¤ôºxŒô¾l’œså49ïêÙÒÌŠ ×¢ôIDAT8q± ¾q™ ™¶\†Í\%Ã箑Q ×ɵÙÏȘ;×Ëøed⽯Ȥ^—)+7ÊÍ}O¦=úd=þ±Ì\û™ÌúûV™óÔç2÷™ÿÈ­Ï~%óŸûŸ,|þYôâwrû†䎗’ůü"K^-’%ÿüUî|í7¹«àwõ)¶>9¯ÿ!9oì’¥oî¶ö·ôE9ûÊ©Ö5ÔÜ«–Õ%rý}¯ZÇàXœçÂ9qn´¶Ðæ‚õ;,æån·d‚l²BfÈŽkÀµàœ¸6\#®×VZ¶M—ýþÒN6k)u÷i(5jF®!Idgwƒõ¿oÕêÿCÕOÃdf~h̘롚B’Š9<ðÄ «%$"A"Ѳ’XX¤,±À“˜¹¥n”¿ñ^‰K/½äÁSO홇{½zuN4iìU»®•x@Úv9I?é9¶O¦œráÕVò:ÿš¹2hÒ]24ë!¹jÁZ›“'“|C¦¯ùÐJ˜·å}k%èeo‰¤òD„ È× òtû†ï„$gÊÊ·eÂ=ùrÍíO˳WË%7Ýc¾^ƒÆZ¤¥ë©È¡G÷’ƒ=JšØFêÕo,ª[(fwQEøC& CuD]aèÃÓ5_½£it0ço’¿`€Ä‚Ä"%ˆÅ·ß~Óø¹çþqúܹsn@7D—.G¼ƒ¾üŠ7¶( bÐæˆ ¡Gÿ«¬;\ƒásUÉñ™ºz“º›þÒºÛr’¤ìÞ‘9Tun{¡PUa¶¨ªKEL.›þ Uy:ãÒë­Êª'éÐU4=@ªU¯¾G"‚ñ)Çwìk˜XìöÛ]÷ÒKNþñÇûø%¹PŽð ‹@‹ßÿm¯gžyºÏ5×\½ƒûIˆˆq·ˆ.t+ >hRŽU>Y@ g²õ.ÙR÷åë•tQ¡ëgÄ­Ë…co³ºÃŽè‘¡ºl—Zuê•>}c'ÏÕ«Wÿã@Æ·àùç×÷âØŽð&w/‰‰‰E ˆJÀ Ù¸qãoìµf­Ú»[u>ÎêCŽ >\ Þ+{“XXžXÄ ¤x/ÅM¿+WÌú«õè&ÈÂDYõömTnÖÁKì&-Z©wHg½·£{¿rÖ°›Ôc¶õNŠë–CoT/ʼCäò[VÊÕ‹ž´æÀKµ0]xöK;ãN]·UŽä {lñ+?[¤Ónƒ”‚à%gx'^l†¿a~ˆS^§¦àbN`æÀvGHãæIízû&Œ àSy3¨< ‚véÔûeÌ]ÏË-û4)ï–ÑXe‚W‚÷ÊÞ$$¡#ñ$_Œá@‚ÁÛA1¥2¦?wÄ ëѽn§°ú¸lßÅ*U£º¡wU—8×> ›ªDu°Ъ£UæÆO;ß[%²ó-²‚YÑÏ~z櫊Òwä-Öà=< €§dð†O¼é³ÌÛNU’ÂL&Bƒ)ÍqW bƒëœýä6k&G¼i/K‹¼áôçÈ[N£Þpj»©Ö)4-òÔ߭Ĉ úû‘¼#o6ýÞjC¿ÕEBë¦ê©È!+dÆ‹¿h1΄×f›)®Âõ ÎôIDAT½ßè[­¤ "WöM¦ƒKßdŠ»~T®ð9T¤P5ˆ ®–â)Œ}6SóP«+#ýä¾V•äÄdèŒ2zñ³rÓªw¬·¹BOZwN.5.½J4l7\„†Ä‚Ä‚Ä" j"q")cŒÈÈ5·?%¨N "‚ÙAñ¢)¼þ}çx#*ÈfFÄkºqÇŠ*I|eq3HO' .“£¼¤OáêãBwÔi©±8WXd¯`¿pÜ"kLÎÈùOX/©Ãø"¼Þ¯ƒ5F˜àÕི7‰‰‰EˆE²î6qG»øYßj‘ êC¥wꨜ ›¹Ê¤‡Çjñˆ!ª}.ŸbU/ð.£¢Lô™[âŽoÍÄSm»t·ÆŒ€ÐàEVè¶A×^í  âÍšH®xy[Ý}XwóH¸¨¤ÔÜ«¶ÕåS£æ^ÖàXÜGÈýn¿šµÛ0×öE7ŽÅ9p.Tpn´¶@¬Ð6d@RÙ‚l²bœ dÇ5 Ñãšð&Z\#®׌kÇÓ?g_q³dŒšeU@êð4ÞˆyYÝx•;^ì5uõûVudþ?þk=aïX…dÙÙíóXP¸I2H,H,H,|D,ÜN8l¯rc2‚¦7   &ûÀ${WÁªI"@-¨EÞ%ë7Ëèy«¬2ÿ¨{6Ë‹pËÌòe¬ã0Ñö{ÏFYT²}ÎÊ<1s¹ ™¹F&­5¯¢Ïya‹5ySä¸s“Eþ²WfÿiŽí»x›”·mb>‰…xÓ6­ŠÿñXÆïx1À®VGSÑÁщÀös.z B†> 5¬X8¡ …ª…D%± ± ±`2³ª#F·HA$ ¥KŸ™ª+D'úÄbˆE¤ª¡“¡!†Xè¤f-Ûeʨlj(!e¶¥Í+·¡ÛäR뵪K­çD÷'!© H,û–ƒŽôIDATH,H,J“gxÙ‚Ç dô=›T5¢X<¹YFŒÌ°HưµºŠðgb1eöI—ÑëÞô؉bÑç¶2ñÆié2¡ÀªJhbÑïêw±šP sžª&R_V—$$•!•=†Ä‚ĂĂÄBfL‹‰^ÓòÔü›dÈО±[\Ò=±SGu…`À§•°º‘Q¨Y2s–[]iiÃeÆ«jlFIUbðê9=42гo–lë>ašWB?’¯ÆoèöÊ&F…ªéƒÄ‚Ä¢²$¡2Ç‘XXXXȲ׷˰¡r¡“Pßy‘§;"I]U,ú”cõ‹['ÇZ=#§õÈ’)ë#ä@“‡Á«KÆX¼¾EúXûfÊÄ";u[z‰±$U#±ô§õ[™$ÁcHJʼn‰‰‰Ei2×)ÍØŠø’ܾ,Õa¬Äîõ: ’ƒDÉAUö'± ± ± ±ðuRô:)§Bû$$U! ‰KbAbAbAbAb‘â ± ±H”Te  ‹O*©pÇÍkˆ¯Kª<=‘XXT…($z,‰‰‰‰+)Ž ‹DÉAUö'± ± ±Hñ¤RÞ],×W­ $ý‘XXT…($z,‰‰‰‰+)Ž ‹DÉAUö'± ± ±Hñ¤¤;kÊêL…ĂĢ*D!ÑcI,H,H,H,X±Hq XX$Jª²?‰‰‰EŠ'Vœ©I¯$$U! ‰KbAbAbAbÁŠEŠc€Ä‚Ä"QrP•ýI,H,H,R<©éΚ²:S]!± ±¨ QHôX    V,R$$‰’ƒªìObAbAb‘âI…UgªAÒ+‰‰EUˆB¢Ç’XXXX°b‘â ± ±H”Te  ‹O*Aº³¦¬ÎTWH,H,ªB=–ĂĂĂĂ‹lj‰E¢ä *û“XXX¤xRaÀ™*@ôJbAbQ¢è±$$!­Zò§-·>û%ï²Iˆˆ=``îÓ_È)]#ð›Ö­[}–h‚àþ$$•Á‰‰E`ˆE~þKÝ6lXˆ Y½FÝNè#ƒo\&sžú7ÌLî®)kÕ*,YO|"&Ü!‡ÝKªU¯¾þÒ¸qão^yåå+“$x ÉE¢ ± ± ±¸ÿû߯ö¿öÚkî¨W¯ÞO˜úÓ´ek9¶O¦ œ¸X®¿ïU¹ãåŸH6H6R ŸÿFÆæäÉ×Í—#{õ—M[”ú|cß}÷ýaܸ± ¿þX¨Ê<ôIDATzGÓD“÷'¡¨,H,H,E,4Ðüqç>=´"ó û?Ú Aƒï4Á°-wƒl¤w?Wθd¢\rÓ½2þî—dî3ÿ‘¥oîNù„ûþªÝõûIw¾ö›d=þ±\{G®U‰8å«­jDý&ÍËýýökö¿ÌÌÁ=úè#þòËÏuµÏpI¢àH,H,I,ì²k×Õß~{c×%K_}饗<Ø©SÇÕ¨Q£XÚèeÍZudÿƒ;HÇãΓλRúŽÌ’!Ӗ˘;×Ë´G?E/~GâÁj‡+¸« Ø"»“W¼)W-X+¯_"½/›$Çô$mŽ8Qîw T«VÍêΈÆ1~×­[ç—nÝŽzsøð+—Ý{ï=Ã>üðƒÃì¾Áï$^`€Ä‚Ä"ðÄ"–ãüúkQíwß}爿þõáS§ÞNºœržtï7BÎv“ºcÌ–a3–ë–<'SV¾mï`·KêTªZ©Q˜ÿÜÿdúšeâ½/[dá’›î‘ó®ž-½U„a°vìéÒ²íá²o£ý*$ 6<înÙ²Åzô8yÃå—½oîÜ97¬]û·Œ?þ¨=Hu,üs …— ± ±HIbQ‘S}ÿýw Þ{ïÝôuëÖöE•ㆮŸ;xð •§œÒãÅöíÛ}¼Ï>{ÿh ê1ËÍÑÛkÔ¬%(M7?ä0i~‚t>ñ,뮳GÿQÒgèrþ5seФ»äò[VÊÕ‹ž”ñË6(b²Ñ*qã —ì—vJλ\¹K®jòLÅãÑ=¶ø•Ÿ-R0sí¹éáw-bpmö3rÅìÕ’9åné?fœ}ÅTõTÒurü9C,ÂÙþ¨SäÀvGHãæIízûÆ…;vT5bWóæûÕµk—·Ï:«ÏӨŒóú²YÁw$lÃ>ØÇàXœçB…çFhëÖg¿²Ú† 3×~&xªwüä 2O¸'ߺŒ3À5áÚP9µ"é "¿«IDATãÚû¾ÕêÖ‘;mð8©;¡ïåV• kÏ~‚§ˆÚÙCîx´´hÓÙªH¡jP«ÎÞ åv7$bÏ5ªÿѬYÓ‡Úaó‰'žðJß¾ç®C•äuÁ‚ùãW¬xð’gŸÍ=ówÞîòÕW_6ÿãâñà‘û< $$$IÀº^¾ürû|𯎠#O=õäÙ+W>4‘™3o™rýõç9"gР‹WsÎÙOž|r÷—Ž<²ëÆvíÚ~‚;VTIªW¯¶+‘$Æ}ÍSAÉÐE½zu)À|é釿bл÷™¹]tá#W\1ì<]1}ú´i‹Ý6fùòû/{â‰ÇÏá…¼žß³mÛÖƒøáûú»wï¢?%ÁŸ‚”D)ëŸI‰€ÐGøùçŸêíØñ¿f[·þß! )o¾ùF·—^Úprnî3½Q9Yµjå Ò[¼øŽkæÏ¿uÂ-·dÝ4eÊ3'L?á^yåwré_<ðáþý/Xƒ;æ>}z?ƒ K÷î'åwܱ¯Ð~xçM‡vè‡èúiÓ¦õ–ƒ>hª/ÐüK$ׯ}Û Aýï÷Þ»ÞOH¸¨ÊÔ®]ë×Zµöúm¯½jþ^³fb܃ U«fîöñë° û`_ƒcqœ çĹÑÚ±BÛId ²AFÈ ™!;®‰×tÁýÃ5âZq͸ö‰'ÜzóÍ7eÍš5óFT@ê +¼Ç[sÁ3Ï<ÝçÅ_8åõ× ŽyÿýM?ûlKk<¾ŒêÇ*ü990aR'•ʼn…’JeÈãˆb€ ü‚ÿÑ©fËÔ™ÓüIEND®B`‚jbosscache-core-2.2.2.GA/src/main/docbook/images/LocalCacheLoader.png0000644000175000017500000004754510660354341025227 0ustar twernertwerner‰PNG  IHDRMm5gAMAÙܲÚ cHRMz&€„ú€èu0ê`:˜pœºQ< pHYs  šœ†IDATxí} œVÓþþL%•K)9(Å(r—tr)—†Š¨!‰ê„E‰Ò˜$EF19.ãÒ8Žâ¯ã2ÎaȸMœá˜sépòÓáÄ9a8ƒF}ÿë»ßwÍÚ³í÷wÞÙ—µö~ú|¦ý¾û²ÖÚëû<Ï÷y×^{œÂúðDÛrñnãþbý‹þe °Î‰%:} ø˜ŠpMERìð# z øŒFÆ‚èá4ð‡>¼Ä€4,0ú /㋲ À@v­Ÿ¿VÑ9 0à=¿ÂO¬q77*ÞÇÜAŸÆŠ_¶K!qïœ?„Á ØÉ÷Äöù«Xë~`eÆWŠ_0¸üƒË_¾bÀN¶°kÜëW±ˆw€@üýÀ€âŒ…¯IÅà¡L³DÁN¶¸'ö°Ï_ÅÂ, óˆ— Pü‚±€±Àˆ…¯°“-ìÄ÷úU,¨LHTh£Y8Uü‚±ð5©€fÃxÙÉ÷Äöù«X—~`eÆWŠ_0¾‹mÛVÐG- ‡O‹´þø3¯ãm bô‰h'[؉5îõ«XDwAk ´˜Rü‚±ð%¹3Éž|r2Ü» uíØ† ŽoGWÒÚúãϼ®ï­}`0¢MH;ÙâžØÃ>‹hc.HS­–$Þ¿`,<7ß|SLÃNêEyû¶¥µWçÐÖñˆÓ{ÿñ:ÞÆûð¾?þx·çíÁÆ2\âÛÉvb{ý*áb"*œ„ÖGv,+~ÁXxšÐÙ pçžÙ¯5m[ÑØL8Íç}öì”kÃ$µ Ÿ£AZ;ÙâžØÃ>‹h`+L€ÖCNü)~ÁXx–ÌyHGØ(¸™ˆtëØˆð±¸,=²ÚÉvb{ý*ÑÙSäýü­~Üð¥øcá™±xâ‰It¨¸´‘ÉH…Ódð1|Y„çe¸ ëÌ%²lqOìaŸ¿Š…¹xÒA  uÀ¿`,–oEåç\¤ Ö›Gj;ÙÂN¬q¯_ÅÂ<éÂ}iÒmôIDATh°“ ‹Š_0ž$ñ%KFÑô“[‹þn™±àg]ðƒ´RëÍ#µlqOìaŸ¿Š…y8Ò…ûÐ:`'¿`,%|Àk*¼6µZì¤ÂˆâŒ…'Æš@¶LÈvb{ýJø€×Txmj=´ØI…Å/ OŒß‚Åïiéí¦\n7qíd‹{bûüU,¢…±TBïÇzh°“ WŠ_0ž îh<4„s#œla'ָׯb¬ºa5ÓuÐ:àÇ +Š_0ž vòC‡ìOÃÅã¹›{w?ôŽ&Yíd‹{bûüU,¢‰57±÷c´øqÕ⌅gÆ‚;š_$ÆÛ]…LíÍûð{Bø¼á4šdµ“-ìÄ÷úU,¢‰57±÷k´rbKñ ÆÂScÁÍG.ø½!<çÂíiœòµé¼TàͦÑ%©lqOìaŸ¿ŠEtñæ{?¿Cë€#;¾¿`,<7ÜÑG‹¬v²…Xã^¿ŠE´0¦f@ë€%‰?Å/ _“:“ŽoϺá†ÓèðÃ÷¦.]v cíI¿ýí¥0€I@Fyi'[Ü{Øç¯bìÉ+h0¥øcá[r«­½î¸ã\êÓgêÝ{wºýös¬¹K—žCÔ<°ñç/¿¼Ý·6),¨Ë]Xìd ;±Æ½~ ÷XÃÙõ ´.»~‹Þ¿`,XšOX;ÙâžØÃ> óq¥ƒ6@ë€#;¿`,+*v²Å=±‡}þ*Áb@Îù5:áv~кøaLñ+¦Æ‚{OÀô“l’Ür£oß=è€v#ž‹QŒðHn'[؉5îõ«X„‡ÉÓ –кøÄ:(L¥ªGñ+fÆÂîØy”"›ÙΩ:5“õA {;^ýk.bœþÅáý$v²Å=±‡}þ*ÑO6Ð:ŒØÚsAŸ¿b`,Ârìn ÚXÈ6ð(ÆwŽ&9бd F1dßø½´“-ìÄ÷úU,¢i, u÷´.‹ðÄ×ÎE¯>CëÜã ­sï¯pç,Gñ+bÆ‚ßÏÆÜ g§ú®ƒ±mûúë;¬QŒƒÞ“zõÚ0Šá íd ;±Æ½~ b-¹ÄZ—y ¡u™÷UK°«øc!{§Ní­çO=w"Ó`èd,ìm~ã 5ŠqÞy0Šáá†lqOìaŸ¿ŠE0Bkç˜WŸ¡u-‹´®eý—ÇŠ_‹mÛVÐG- ‡O‹¤›o>˺­r¿ýºRïÞ»[#noM×~os¶™;žÛÏçÁÛü®¿¹åKgÏw“ì¾ûÎtúé‡Ð‚ùZ·¹¹çôþv²…Xã^¿Š…â꾜º­ó>^Ð:ïûTñËcÁ${òÉÉtpï.Ôµc*8¾]uJk:¾W.õèÚŠ:îКúÐÙÚG—dªÍÇì—kµŸÏC÷6Ÿš×–úì™kõ3÷¹ŽmöBÄý.ÃN¶¸'ö°Ï_ÅÂ{QõG©tZçm¼œý ­ó¦¿47ß|SLÃNêEyû¶¥µWçÐÖ’Á߯¼Ž·ñ>¼ï?ÞêHÚì H½jʰ“-ìÄ÷úU,ôÃ(t#˜˜˜ØÏ:èX&mPüÒØX°Aà†žÙ¯5m[ÑØL8Íç}ºwʵŽaðdÒ^ïƒ6#^ÇÍÏòìd‹{bûüU,ôÂi6º±§Z‡6ë…;¯uOñKScÁCUC‡ìo7‘n¹à2¼î¸tå¡ÍÑ&MºØ§Ûf'[؉5îõ«XèƒUÖ Ö+NºétÍm[˜Z‡6냡túä6Å/MÅOL¢CÅ¥LF*œ„ãcø²Ï˲SÑfÍ ov²Å=±‡}þ*ú`ºLTa!¶ôIDAT,Lìg7=Ñy◆Ƃ–ËjÔmvtHgr9Ûf'[؉5îõ«X“ÌœXp~‡n£&ö³+&|WüÒÐXð­˜|‚ÛDÍLËepYAm¦Ÿƒˆ¥×uØÉ÷Äöù«Xèa, Á膉ýìµQžâ—†Æâ‘GÆÓØãÚ‰~È~Ä‚å[$ù™At(ÚL?K¯ë°“-ìÄ÷úU,ô0Ð`tÃÄ~öZ‡‚(OñKCcÁ–ž~rkÑ-3ü¬ ~VŠ6ÓÏAÄÒë:ìd‹{bûüU,ô0Ð`tÃÄ~öZ‡‚(Oñ ÆÂãa"pMlsäðº;ÙÂN¬q¯_ÅÆ"[œ›¨&¶9Ûø„yœâ—†ÆÂÄa+´9˜!Í0I“mÝv²Å=±‡}þ*z èF0ºab?g«7a§ø¥¡±0q¢ ÚÌ$¬0I“mÝv²…Xã^¿Š…ƺŒn˜ØÏÙêM˜Ç)~ih,øÖ ~/HKo7å2¸¬ :m¦Ÿƒˆ¥×uØÉ÷Äöù«Xèa, Á膉ýìµQžâ—†Æ‚;Àć™ ÍzˆµúôÓ[©ºzNV&ÕN¶°kÜëW±pÇê;ïÌ¡Ï?¿-«8g‹;è†{,²íÏTÇ™ØÏ©ÎE×õŠ_š v˜üÈX~dmsïáGã†õHo´9‘ŠXk×^Mçœs8µiÓŠæÍ;#«„c'[Ü{Øç¯báŽSŽqûöÛѯ~5ˆ>ø 0«x7›¬uüú‚á†iÚ쎡æÆ?Jû+~ij,¸³ùEbÜP~±X&öæ}˜œ|LXo8E›Í'[]]1Ýu×ùÔ·ï–$Y`,¶å†m ZZ¿Œe*1çË}ZµÊ¥³ÏîGo¼qïCêÿ(2MëÐfó5/š»^r‡y*>çXdjn!AìÏ1¿7„ç\¸=S¾6÷á}Ãz³©ì´ÙL¢ýå/óhÊ”A´ÓNÛ7$É ^ÂXÄËXØcì±=­wmÝZâ›É`ÝàOÓ´m6Sïd¾òr)9£½±à“æ¡B¾>Æ“1ù1ÝüTN~€ÿñg^Çïá}x_/;*Û²Ðf3ÈV_7=þøD:ñÄ\Í„$ŠÝXØ?3>2ùÎûÈ_Û‰ýçÞˆï £dp]‰úÔe-ûwûˆ…Ü×¾ìÕk7*)Cÿûß2_tºŒn˜ØÏÙæ¢ “\1ÂXÈŽa0ðmC|O2?ð„ÿø3¯ãmr?–&·ù.¤SOíC»îº#ÙŸÞ¾¶ýœMÌ?þx!õéÓør‡$†ÛRŽXð6ù™ëÍä;ï#ÿ%rûØ.ôIDATöŸ'¿&ÇwÙ!~ö—(?;c!ïÑ£ mÜx³h²?‰ÐdÝ0QŸ£®u~áÔY®ä‡QÆÂyøî¨q¿>÷Ü´ß~]iĈþ–óµfþ^^~…obF<¿új©e:vlo§$†ÛÒn&šÓVY–LœX†×*îÜijÄ‚'ñNž|ýûß‹#Ńæà9jûÆE낈›â—æs,‚è Ô¡D–…åçJûïß•ž~ª%ž î#6l.Î:«Ÿ¯¿ÖˆG&Æ"G»ÁÓXÔU'žé"‰+—«6l¡ÚªE®óCÖlªO±­6²ç¨I”YX© ççM³­òVÔ°ƒØLÅù‰ ²Îœœ‰dmâÅ¿ŠÙy‰úóŠ)á9ê¨89IRÃeäÑ3O7.{E³ì*[Ÿ,%Ù¾œ‚2²ìFýz*àrU%*ÎâÙ‰_ç’c¼ûî;Yw~ðCÎíøn¦VB낉›âŒEìă‡ù2†Ûe7ád°¸­—ë¢~y„Ï“ Æ»ï^Ÿ¶d8—Šlâð,ÿ­/-°óš µ´e‹A(O˜‰i«6PÍŠ|±m0Uˆ_ö[êê¨NüÕÖÖ74lÛ$Ró–MT:‘y>U‰Ü½¾,QfEc_AU‹‹} ¨F²¹¢P|Ç ^DD½U+ø˜šV¾É:“ú«ÛyŸœÙ´!9Èaµ£¶†¦ñúü´‰ÛT'Ž·•½qʹı¥Â¾m,OÖ5q•ÕöÄ9s¹âÜxpfS¹µÿ¢ªäHÕ‚æý§bá.´ï½wCèÏÁqâßÝc•I¿@ë²ï»Lú׹⌅P¦`;?ÌúäP _Æp»ìáÖ6‹Ûz纨_qžo ú=ôIDAT¦ßÙš—ÕÞ[h•e¦Ñ†äÊÚšR+ÉN\õW*³~õO¤êÚ-–éाe gø-Ém…”°D•Vb,Œ…,s6mT‰Oµ´b°HæƒWˆOâ³5ZQ@ ¹|Ëz*ž=›J+ø¨:*µê. â≖`ÃÒ𯶒 ì䯨I®r+[Œ~4\ý¨¥âÌ,ù9÷’#ÅÕ‰ë rÞâªÍ´¹21×bâ q÷DUU‰?¾ƒïèØœLÈ¥ëIXÅË2åqÖÝUèÃ伇5<»SŒr2ó-ÊÄÝ$U«uñe’êUÓ¬óš]ºF<ߤœÊ×”&F:F ~~FΩ°Ê®—#-‰²«+ʬÑ®‹8dûʬIr_1S*FbZðO _49Ÿ '£¸´N<+~ÁX™Ò#(^µƒ‡ù!Vö‡\µ¤ì– Yoœ/(²e—0.­DR¶.ä—R¥5q31r ëá%'ïjk[>%ýH£Êk’“0íÇäˆò*¬9i½œ„¹±‚&&G&xßüÙe´I̳˜È†£Ñe”-TÊ—,D–ý‘mäчd͉‰›©ËÎ+XDÕÉ[\ç,öMz¢õe<‡#‡ì—ZP†_äùJ\bi¶öAëôŠŸâŒ…$½‚“m{ìCü+ûC®²-“c°´äxyl\/(²e˜ý´Û­ÞºÓ¤Îšê}ã¬;H|*ÛÙZ‹hp^r+nK1ÞÒ¹ôIDAThžøUü‚±Ú£gšÓ.ù¸íæÜí‘iù^ YŸýòaÊõQ]*²9Ó¾Ý*æs>ª|iê¼ uúbWñ ÆÂèÄ&‡ù!UòÝM³¹Û½6²~n/_®áw“ðyÈõQ[*²FQŸ³T,ôç¨áß«óÖéYÅ/ #š_Cn"à—±àºøò¿›„ÖÅwð9*²9Ó¾Ý*ú‹´ã¸ZgV¿`,Œ3~º —ŸÆBÖåË#ŠlA§QÔçì sÄZr$ŽKhY8Uü‚±0ÆX1è&^A Yo/(²9Ó¾Ý*f ¶äG\–Ð:3ñ©øc¡½±r(ÐM¸‚4\¿¼<²Ë.¬W¹óù»µË”uŠlA§QÔçì 3…ÛÌgÛNh]T´ÆBë¤ôP › m,d>þx!ÑŸzöÜ•L¾{D%3gšÃ÷ {@ÅÆBòL—%´.JZc¡¥±k(ÐMdÂ2²-¿ÿý•Fß=¢’YÐiõ9{@ÅÆBò+ì%´Na1:Zc¡•±{(ÐMdÂ6Ü&“/¨dæLsøt¨X(1wÃ<Öùß?Ð:÷>ކÖÁXhc,t tTŒ…l—‰—GT2 :¢>g¨X¸‹ºÄ–þö´®éþ5[ë`,B7: º ªNÆB¶Ï¤!C•Ìœi߃…]b Kïú Z×ü¾4Së`,B3:º‰¨ŽÆ‚ÛiÊ¡JfA§QÔçì‹æ ¼7°.³~„ÖeÖO©ðdžÖÁX„b,x(gíÇ»=R3Ûõº y>ºªdæLsøt¨X´Lè%ö°lº¡uM÷Q¦82Gë`,|5Û¶­ >Z@?<ž/i=—á°Ãö¦½÷î¬í-”Î6³sûùÿØíé¸ýséxÑÏÜç:´Y%3gšÃ÷ {@ÅÂ{Á×Ãa´!•n@ë¼Å˜³ŸõÖ: Ï“ú7ßÓ°“zQÞ¾miíÕ9´µ$Gð½ñ¯ãm¼ïûãw{ÞŽæˆ Úì­Øû^%³ Ó(êsö€Š…ñ¶Ç>꟡ÁàÈ”~Vü‚±ð4¡³AàÎ=³_kÚ¶¢±™pš þÎûtï”kÃà CˆÐfÅA‘Í™æð=èP±ð7æað8è:³ÕƒôIDATÑ= Ô:´9s®(~ÁXx–Ìy¨jèý-£àf"Ò­c#Â#\F6gNšlã¢ÈtE}ÎP±ð?îÙâÅ„ãX7X¯8é¦Ó5·majÚì/î¿`,g¨XÀX4G+ìûB7‚Ñ ûYñ ÆÂ“$¾dÉ(š~rkÁ¿– ~Ö?HËNd¿>£ÍÁô³"›3Íá{Ð= bc‘­®@7‚Ñ ûYñ ÆÂ“$n"Ðæ`B‘-è4Šúœ= bccÑüøá—ž7Š_0ž ‡­Ðæ`†4Ùœi߃î‹ô™mÒÃqÐ`tÃÄ~Vü‚±ðÄX˜8Ñmf–"[Ðiõ9{@ÅÆ"[ÝF7LìgÅ/ OŒßÄïiéí¦\—•-é›sÚL?+²9Ó¾Ý*0ÍÑ û¾Ð`tÃÄ~Vü‚±ð,‰›ö0 ´Ùÿ£ÈtE}ÎP±ð?îödµÏÐ`ðcZ?+~ÁXxf,Øaò#cù‘µÍ½;„Ö#½ÑfEB‘Í™æð=èP±ð7æQ3Îóa­ã× 7LëÐfq¯øcá™±`òñ‹Ä¸sùÅb™<Ú›÷arò1a½ámŠlA§QÔçì%|þÆÜ™ˆ£ø]êÿ(2MëÐfð¯øcᩱ`aƒÀΘßÂs.ÜžÆ)_›Îûð¾a½ÙT ÚìѸÙœi߃î ÿâ-9‡%ëxš¦uh³?øWü‚±ðÜX° ðP!_ãɘü˜n~*'?@‹ÿø3¯ã÷‚ð>¼¯"„6ûM¶ Ó(êsö€>b­ƒnt#,™ÐÏŠ_0¾&uß6Ä÷$ó©ø?ó:Þ´dRÚì­P(²9Ó¾Ý*ÞÆ8^E}èF0˜Ò¹Ÿ¿`,´LîQ¡8Ÿ"[Ðiõ9{@Å"˜$'œã\)Å/  qé¢à_(²9Ó¾Ý*þÅ\B߯Š_0Hª0¾b@‘-è4Šúœ= bä×ä‡óöûŠ_Í0ðJDŒôIDAT«§SYé8*-›Jë7—4ãúÚ"ª(ŸN5›Š­ç º'÷ßP›,§~)UUL¥ª KÉÚV1*+g6üUTÌ¢ÍõþuÀ†¾õ ŠlÎ4‡ïA÷€ŠEz¼»i–ćÔ.h]ú>”ý…e|úIñ+#c±œÊ {7Ü6'.[¯LDea÷Äöü ´¥ápêãêªG[û/ªZ*´Et|Ý,,žå·hÉm²¹¬¬‹O€@ÆèÄZâ7è$Šú~Þ*©ð•Z³$'¡u©úë%FâºTüÊÀXÔoœ”0 yÃi£5ظfXâûìé S°e.S(´;•oJŒB¤;®®f¬µquÒœl™Cù¢ŒÁÅsHn+,/¢-uK©¶6ñWß`Xà¸×ÄóVdûy¢Ãš`{@ÅÂ]CÒi–…=h]Bó¡Åè (~e`,jJ²L@éúåÉÎ\JkVŒ¦U•ó­ï›*†[Ûç÷³–ùÂ0 Ó'ÍÃ¢Š…T¿e9Õmši >¶ñ¶bª«ã?Y·» ˜˜pÐæxÄR‘MÐÿBí wì¥Ó,æ+´Î½ß eèÆ€âWÆb½e,úQÍ·Î+¦Ò|­F›h)çñç!´AŒl¤;NšÙ¹´ ¹.±Aµ. €v‹ Öé„ ‰ãP3**·z@ÅÂ#é4‹Z§¯Ðw ‡Ù/Š_‹š<!.ql–'²œ*Ä$Îòj1Ú°i å%/ƒ ÎWó0U.¡tÇIcQ(F,¶X#Ó>bQµDˆA Õ×óŸ¬Ë0ƒº›?E6d÷°{@ÅÂ=Žé4 ZçÞgÐô‹Ä€âWÆ¢~ÓÔ„yÈN5s©|Å@kÈ#¯xU'LGñqßÉQ>Î29è›4ÇIc‘nŽÅÄâ)TU•¸3¤¢b&5ÜA‚‘ ¡Ï³)} ÈvZEý*îüÖ¹÷‹)\C;ßâWÆ‚ƒU»~Šm‚fåN¢Úú"šÆ£ãlw‚ÜC•Ö$½©BÜ’êzœ(O‹5jò&OÍ_¡æXÈFÊeqõR$T ã0 ñ‹´~¨X¤`WÍ‚ÖÇ;˜ŒÔ÷«o¿24‰†”$&Rniü ‹¦™íqÁwLÓç‚6¡š‡E¶ðkÜ[ bÑT ³Õ¬lkª=ØÝÑŠ_Í2úŸÀ‡é†E¶¸§õðÏ_Å<Ñ'hù˜Tü‚±jg~@qúÆP‘-üÄ÷¨Xè‹p±1Š_000V¾b@‘-îi=üóW±@ò25y¡ÝúbWñ ÆÂפèK‚ b£È~b{ T,€Ë ðzâƒ5Å/  µ ôIDATŒXøŠE¶¸§õðÏ_Å">bÄŽX…Å/ _“JPE=úЇ"[ø‰5î-P±Ð/à2bc*¿`,`,0bá+ÙâžÖÃ? $/S“Ú­/v¿`,|M* ¾$*6Šlá'Ö¸·@Ÿ ÿ¨'>XSü‚±€±Àˆ…¯Pd‹{ZÿüU,â#öHìˆuPPü‚±ð5©PÔ£¯x(²…ŸXãÞ }ñ.#6¦b@ñ ÆÆ#¾b@‘-îi=üóW±@ò25y¡ÝúbWñ ÆÂפèK‚ b£È~b{ T,€Ë ðzâƒ5Å/  ŒXøŠE¶¸§õðÏ_Å">bÄŽX…Å/›±+±¯‚ç×Áã}à!ÂO«h8 ]üÇŒ’§‡É¤MMZ&þÂíà35>Ñ7è¯0` ÝÄNžÜ=Ì!ü¡š‹‰Ýpö„k*tì‰ÕæbûC?Zb[·FÉÎy@žl0 ñ£®Ñ '$V³Á9Ž>Jü8q¥Ã÷aoƒì,ÄÉ?vLá3’ºŽXÍç8ú(ñ£%¶uk”ì,ÄÉ?ºáí¹qb@b5œãè£ÄW:|LjEs9®Ì¡ órh\QÍ¡1˜K^Ê¡éËrhnyãõLz·ãäþEâ8KÖåЬñ÷TYÛÄç™÷ª¿éâû±OI¥ØG¬_(–n‚ÂuMíã6rYÎ}d½övÚëãzf‰2–ŠºøXû6Ùž†¶ð9ÛÚ9]´kþÓ?¯ÓÙ† ¾ëL66ècp$VƒâFSõ¸i–<ÆMCä6·ãäþÐ:ÿ´QâGGNÃX¤1S'« 22ˆV+ Ì”ÛåÐr[9©Ž+É›Ëñ`² az‹ïݧæÜ&ë‘Ë™b¹­á8[]SÆü¼…Á¤ç¥[;e™²¹œÿºªO®“K{[ä:¹. –½Î°>ËöèH6´IŸ¤®C,$VÃ⊽ÞTš%÷qÓÞ–ê8©/ š­ó\%~tÀ²³ 0¶$-IÄËñ+Ü \/1R ~É/¼=ù}|2Š|”02¸SËëÓWüXbÿÑÂ`Xu‰2ú‰2zÏÉ<¹ôrAÂ¥bd€ÿJD[䶆ã’m.yJÕ?WÃu³Qá6Íßení”eãúD=ãF$Žãïr[º¶Xljö‰Ñ ®ÏÁÞa}–ñpß‘ÔuÀÄjX\‘õ¦Ó,kh-‡Ö5ëvy‹Æb®¸üÁÄ·Z%Ì)׉KbèŸÉ¶H&T1ZÁûõK&ÖtÇÉ„=B["³šþ*ôIDAT€Z,ÌH¿ä±?Û&ŒAqÒÈmNc1W´ÇÙÆEÂð:¹oªvÊ2ÇJ“#ê² ˆ¹#irÚ™®-”ð9𨋪0—|îü§[A{`lœX “/\w:Íâí©4$ÝqR_4Zç¹>Jü8q¥Ãw A7bÏO‹¹Â¸m—4‹Äñ£{%’Y‘ÙHwœ$›„\²)Iµm©(_n“fA¶G»atÂeßTít+ÓÚw@Ý’4²}rio‹\g-ÅùOYíÞO²­A-e»t Ú3‘«Aq#U=é4‹I¥!鎓ú"ÏQ.¡uÞé¤ìÓt kŒ… ŽáähÀÔÔöéÂlL¿ðK„ûîžüeÜ;i08È#ÄhFºã$Ù†‹ Zs±x0Q_ OÚLN¦”Ç ßÞÖ9Âp½ö6Êk›£E9éÚéVæ6Hâ|n•#iÚ2ìv1w#9b"Gkìm ë³Îd ‹ä¨WOƒ#±Wd½é4+†¤;Nê ´®±fË>÷b)ñ£#¿a,R‹B‰D;]$Ú©É$Ú]$s™ÐGsr&a¦¸üÐO$øœa9t§4.ÇI²5Œ<sÁÇÙçX åÏ ïÆà»/xVµ0N HüÀXd@^ÙYÎNÄw+ HüèH6´Éì¯ã'±š ®±ôω‰¯qéEy±ÀˆF,20¼^ eÀXØ1 ƒ3aà;LD&ø±cJ—Ï0000¸dbÈ$‰`˜ '$~t1övÀXÀXÀX„Tì$ÄçxŽdÈÄàLø‘ $~tÔm…ì4,¿uý‘YèH6´)ž"UÜÁå̸Œ~JßO©ðæz‹ä+ÁÞôà5­Â$ê†È¦q íÕS#3ÁZÐûhg,‚îSëûè£ÿÛ×Ô¶£ÝH¼À0  sfâÆÂÀëëŸ|òñ^={öøð‡¾o› 9±™äDÜ·8c€õuŽõ.Îý`â¹ÃXh,ÆŽ³’‡%W®|d¬‰ C›‘0` ) °¾±Î±Þ5µ/¶ë…' Ìśo¾qtnnÎ6&Ü‘G±„Ò‹Pˆâ xƒÖ7Ö9Ö;Ö=ô«7ýD?ÂXd,¶mÛš{ÔQGV1ÙäߺuUGÔa©+ÄÊt °®Iã%ëëŸéç—öÃXd,äРp&D‰‹Xá<ãƒuy¹×®u¸ôkNüa, 1ß}÷m‡½öêþ‰hü¹mÛí~øì³O»AtÍ!b…X©1ÀzƺæÔ:Ö?ÖAô]ê¾Ó¥o`, 155ï2oÞÜù '?ó²ºú‡é(´CÒ#Fˆ‘Î`=“úæÔ:ÖAÛŽ¶%¸caˆ±°–ÉfÿŽÏHÀ0E @ëÌÄ5ŒŒ&Dˆ(&œ“™IÄϸÁX˜‰  “ Èf&Ùü`” LDÐ:3q cc 1Å$‚s23‰ø7 31ca`RÙÌ$›ŸŒ²‰(bZg&®a,`,0ba ¢˜DpNf&?ãca&&`, L* ›™dóS€Q60E @ëÌÄ5ŒŒF, Ä@“ÎÉÌ$âgÜ`,ÌÄŒ…Id3“l~ 0Ê&¢ˆh™¸†±€±Àˆ…ˆbÁ9™™DüŒŒ…™˜€±00©€lf’ÍOFÙÀD1­3×00±0QL"8'3“ˆŸqƒ±00&ÍL²ù)À(˜ˆ" ufâÆÆ#b ŠIçdfñ3n0fbÆÂÀ¤²™I6?eQÄ´ÎL\ÃXÀX`ÄÂ@ D1‰àœÌL"~Æ ÆÂLLÀX˜|¬ƒ‡ôIDATT@63Éæ§£l`"Š€Ö™‰k  ŒXˆ(&œ“™IÄϸÁX˜‰  “ Èf&Ùü`” LDÐ:3q cc 1Å$‚s23‰ø7 31ca`RÙÌ$›ŸŒ²‰(bZg&®a,`,0ba ¢˜DpNf&?ãca&&`, L* ›™dóS€Q60E @ëÌÄ5ŒŒF, Ä@“ÎÉÌ$âgÜ`,ÌÄŒ…Id3“l~ 0Ê&¢ˆh™¸†±€±Àˆ…ˆbÁ9™™DüŒŒ…™˜€±00©€lf’ÍOFÙÀD1­3×00±0QL"8'3“ˆŸqƒ±00&ÍL²ù)À(˜ˆ" ufâÆÆ#b ŠIçdfñ3n0fbÆÂÀ¤²™I6?eQÄ´ÎL\ÃXÀX`ÄÂ@ D1‰àœÌL"~Æ ÆÂLLÀX˜T@63Éæ§£l`"Š€Ö™‰k  ŒXˆ(&œ“™IÄϸÁX˜‰  “ Èf&Ùü`” LDÐ:3q cc 1Å$‚s23‰ø7 31ca`RÙÌ$›ŸŒ²‰(bZg&®a,`,0ba ¢˜DpNf&?ãca&&`, L* ›™dóS€Q60E @ëÌÄ5ŒŒF, Ä@“ÎÉÌ$âgÜ`,ÌÄŒ…Id3“l~ 0Ê&¢ˆh™¸†±€±Àˆ…ˆbÁ9™™DüŒŒ…™˜€±00©€lf’ÍOFÙÀD1­3×00±0QL"8'3“ˆŸqƒ±00&ÍL²ù)À(˜ˆ" ufâÆÆ#b ŠIçdfñ3n0fbÆÂÀ¤²™I6?eQÄ´ÎL\ÃXb,jjÞ;dÞ¼¹7òß Aƒ^–ŸyY]ýÇâ(*8'3EqCÜZ‚Ö3©oN­clIÙ86lÂXb,¾ûîÛ{íÕývðö¿¶m·ûá³Ï>íÂCô3úð¬g¬kvãϬ¬ƒèûß‹þ…±0ÄXp°W®|d¬“lcÇŽYéP†þdEŒ£¸`€uÍ©u¬q9ÓÏÆÂ c±mÛÖÜ£>êM;áÖ­«:Òt¢ýH˜À0`Çëš]çX÷Xÿìûà³¾˜±0ÈX0‘ªªÞ<*77g“îÈ#XréK.ı²Çëëëëú2û¾ ºï`, 3 9Lˆ¡Asˆ4±Q°a:ä¥_\î5˱3õõí•ʃôIDAT?¶ùï¿Þ™'mØðýþü皃ßzkÝk×¾<¨¼ü¹¡O>ùÄÙ>ZvþÜ?þî»ïúÕí·ß6}á›gÝ(îǘ={ÖÍW]5ý¶Ë.›²üÒK/ùõE+3æü²Q£F®>üÌ5Æ }†¼0hÐ k=ö˜×ÙqvXÿê¼¼CÞëÓç ¿p@¯÷{öìñá¾ûîóÑ/~±×ÇÝ»ïù¯nÝvÿ¬k×]7wéÒù‹N:~µóÎ;ýw‡:|Û¾}»ÿµk·ý–í·oû=OdÚn»6?¶iÓºžÿZµÊýI8yµ°F.¤«ë·¶nÝê'¹ÃÇr\—Ées\×Éus¸-Ü&n·‘ÛÊmæ¶ó9qÄ€·øœøÜøù\ùœGޱúüóÏ{tܸ ä>á¾™>}Úí³f]»gvß|ó‚ٷݶ䪻îZ>åþûﻸ¬lå˜'žX=âÙgŸ9í¥—*ó/‘÷ÞûSÞ¼ßë_ÿú¤{mí—»üðÃ÷mME´ß<1ŒZÌL×:¡eõ¬qbÄb«¥…Ö™Á+cŒÅ·ß~³ÃÇÿó|»Qeå+÷»5g>üðC,[vçå ]wÍ53Mž<©„Ý-'¼ÁƒO|iÀ€Ãß>è ÿÆ s—]:ÕºÍ4f âO¿>`ƒÄhÏ=÷ØÔ«×þôïßï¯<í´aÏž{î9¿0áâûØÀ°yaówß}÷NXµêñQ/¼ð‡“Þ~û­ÿøÇûñÅç]~ú©¾uÔÎÇ qÍ6NÐ:ýôÈÏE­ ÍXð¨Áßÿ¾¾7üö·Ëᆮ/œ4iâ þÌ¿Œûöíóþ5í¥!ÈmÕšÚí°3íÜ¥íÚ½'í¹ßÁ´OŸ#¨Wÿ¨ÏѧR¿Ï¢§œGÇœq0r2 9 ½hqé<:ë²›iÔ´%tÞÌe4öº{hܼRšPTFoYESn[CW?GÓîz®¾g-ͼï5šõà:š³²šnøÍ{4ïñ¿RáïSÑSÒ‚ß}D Ÿù˜nyî_tëóŸÑâ?ü‡n{ñ ZúòWtÇÚÿÒ¯~KË^û-} -ã{ºëÍèîªÅ_½õW²î'*yk+­x{ÝóG²þø3¯ãmr?>†å2¸,.“Ëæ:¸.®sÉ ›­6,*ßdµ‰ÛÆmä¶r›¹í||.×Üÿºun|Ž|®|Γ­}ð(]tãƒT0ç×tÞ5ËEÝFg_¾Î˜x£è»ÙtÒØ«hШ)tìð‹éÈ¡c¨ÿàÔ÷ØatÀá'Rƒ¢îûçÑn¿èEºv§ö;v¢ÖmÚziö¶uì¸ó׋Û‡ôIDAT=zìû<ê2tè©åc™6íÊ¥<šÂ†„M*Oûç?7îÑ’h'íl“}KŽƒÖAëâ¦u¾ ž½ûÉ'ïÅCÝ<ü=wî ó/¼ð‚‡~ùËÁ<ÄÞ¡Cûïšë·Û¾½•xØìßïx:äø3è¨atâ9—YÉëìËo¡1³î¦ñ…Я–V#~MjýÚ¶ŸAæL1À#9·¿T+Fa6ˆQ—*˘\tãCÖÈÓ)^cLñèÉ/z÷§Ž»îA¹­Z5iDx~ ßþvñÅãï¿ãŽ¥W¾òÊÚ¾ù¦nG˜†h™g<¡uÐLu'ŒýtÒºf‹üa»çž{vØå—_¶Œ'÷ á*Äük‘/1ðe3«Ä>g³ÀBFÇ£NCSà_|‰Š/ýLºõ :gúíÖå°Cå‹K6‡PÛv\/Ó´jÕê'žrõÕW-yñņ`n‡ù&Z½hJ/LÞî§Öel,x˜'HvîÜù ûhD›¶ÛoëqðÑÖ5ô±³Wˆ¹¯Šk÷ŸÃ8$ç>˜ <´ýçÂÊ¿ nyök.͹WßAÇåO°F;Z·iÓÈ`wíÚõ?sæ\WÄ¿r¿|ñ]oÓ­û9î¡ñë“–h]FÆâÕW+ße—]¾”†‚Dþ¯ŠèÚÒ7­I…]ü@‡˜7Ž9OŽåI»§_2WÌñ8¬aT£K—.Ÿ¿þúkÇÂLèm&d| uq ž£?œÈDë22ül6Gv>ùF#0 4ž4ÌwÀ0oøŽ™¸°ÔÛ`@ëH‰ßÓcÂMë22r¤â—çMµ&S¢£Ów4úý×KN<÷ò†‘  ½ …Œ´Ü…~7nZ×,cÁ¤kÕºõ ~ŽÃÂgþ‰_®MürH›R“û‹GóFϸӺ啟—"“/eâÂRoƒa´.>Ü5YwÂh{SZ×,c1xôbVü“oåçIðC£øJü¦0NuB‚Ä?\ŒŸ•2òÊÅtØQâVÕ=ñ‚ÂÆ“‰ †BoC!ã#ã­ƒž©':וÓÁ¨ôIDATÖ5ËXðÉW~c=„êð“αž’(‰¨–¹Öó)òžI§\0S<Èè>ºúׯX·ðñ,S;mƒ˜Ø1ÀO-å‡s]qg¹5ÁcãpñS[ÞÕã‡wÚe7ËdósXø)§\–ÜO&.,õ62^;hôÀ®QþìµÖ5ÛXØ;—!ÍODäÇ8}ú…´GϾ֥INç²MÛv´û>½Å£³O¡ãϺ”†O.´‹ÍˆæÇGóc¦íåã3ˆíø±çü¼ŠÙ¿m=©•1Ìnç —üжN»í%žÚÙªÁ8±ÌxÛç 4ð쉖yæ Lnm•ÇÁPèm(d|d¼œ±„ÖA‹œ˜0å{Z×"cáÖ±ü^Šëý]²à7Ö­wü€,~PV‡vI)Ғ̼dÁî²gñ‰£­÷v 1‰N›p½øÅXl½“âÊå°Ì ÏïÀe]bÉÃï\áÏÏRáǺóc¿ùý.üÈï#‡Ž¥ƒŽ:ÙzÈ,¤3 ¹Öcä{6Èzd8?6žËåQ N4²îtKY–L\Xêm0d¼ÒÅTnƒÖA$‚\š už‹tÌï~à—ZñË«øâ©ã®µŸ_вÛÞÐöíwÌÈ|Hòó’_XÅCÓÝö=ˆzæKwšõ«“_z5lüuâeX‰wˆ\|ÓJºléÓÖ³ø¥ZœøqáůÔeœ$Ò¶e'2|ylÙkßY¦€»Í¦”¿äŒß Â/6ã¿ñó!~yÞ•âÜã,Ãɘ٫סԹÛÞ´}‡š6Œ~”7c†Gxí¹д»_¤›þß?7ûÛLùÜGL½ÕJÊlä¿ÉtlÛLùW?\ñKäxDŠG s=‰!ß…±c§®Â`h]ÊÈ;a¸5ÊÀæ•ÉøùÓÔeÏÓõeïZoså~Ê-ÝGâR&.,õ62^-{&ÇCë uoÍYš uÚ‹Li߇‡#9)ó 6#—ßñ ñèˆðÓAùESüús¾YM~9ôIDATvÎoDe³ÀOFä×tó/V%ÉlX\MÒk°oÓýÆïß`SÀwñ+ÔyŽºÃO:WÌŹÄ2+ü ös®ZjÍÉ™¼øIë%u<¿‡M¿Þn²ìøû³Œ? …Þ†BÆGÆ+lܸÕ­kZKdüt]ÆEëŒ7nÌfÿ¢ç_ñ ~÷‘eRxR4ð/u9™PTfMÒãÛjùC­vñkô‚oMãɨ–Ëà²x„€Ëæ:¸.6V\7·“:›-n·‘ÛÊó\¸í|œèùœøM´|Ž|®|Î|î|÷Ïé—Ü@ùSX£lêøn 6x“­¶.k\µâeëÅ^sû³5:²ø÷ÿ¶fÝg:W!›˜êpŒ8™¸°ÔÛ`Èx逿ڭƒÖù-ÉÖ8 <à*K~Ù„2ídƒ©ÐÛTX‚(æmqÌLÀÚˆy:aÀ®u0‹å/¬§©‹Ê¬aþ)÷®§åV›ê©èÁJš.æ8Ì´ýM¿·š–&·/\YA“ŠJi\Ñ*šõ”z}ÉK¬‡7%Ž«eTÑü§ÛoK”=½¤Š–¬Iý ©l00~`̤2¡uÑÕY»ÖÁX„l,Jž^CÝ“¿’d`º/§’?ÖÒÇúÄö<šU¹…¦ŒÉûÙ¤ÇE5Ö/­â•Å?ÛÆÇ_¶‘Rm›Y]À‡)¼2¦0ú› ŒXø«Ð:û7LãºíZc²±Xú`ÂŒ[½E˜‚Í4¼ÅæA<ùï¥Zñ·!±nÀ"*z©Ž–Š¿’§V%ƒ8æŠýJž.§Þ– ÉO÷Ø kû°ehùëuTT’¨£÷Œj*NnÎÛ*kEy‰¿’û!lRøU¿l0ú› /¿ðçr¡u0¹vÙüÄ’’„ ˜WNE囩H\Þ˜ZRm»4QGã ³1`'“ÿÜë ¬cf$ѶEËf[ëF¯f$iF¯¬K\+~©Šú ãÑ}jUö%©D˜ŽâJþcSãß9ƹlÉ;ŸðY_ƒ!ãgÌúuîкhk¬äëF,BO¨›iÒÔ‰–)H&†U‰K!„.Æb‹Ä¨†e&”±¶–½ hÊja"’¦£Ñ¶œEÉy²N,e¿¶t)ûfB_3aŒWKãŽãÝ4Ze\HîÀX4$o7³nÉê*šzo¨§%O¯§I“ó-“1á)9Šðsc1g#Þ,ôIDATï“GS_Pm”s'F?¨ŒÅ°Û«iæu ÓÒoFUbþEÒXŒxp³ø^/¨Åªœ(?Œs³“ÍžÀðYO£!ãV¢^'´.Ú:+¹c¡±˜?/a$†Ì«Ïw¨¡qã[Æbjy}òòDu\ áIPVL£)Чd–”Z—:rr&Òü×ÅÜŒ¤yûXœLŸ˜è9Œ'o&· œ±Fh]¤5VrÆBc‘lƒœH©æVdæn—cò¥Ödµ“ †B_C!c#ã‹D’þAë2ÓvÓ0(¹c±L Ú›½ØÉ&“–ú /`>{Ì£ïâÙw’;00ZÿÚ‚@ÙÉC¡¯¡±‘ñŠöpñLðaÅ]rÆÆÆÂg ØÉ&“–ú ¯°Äõ ˜ŠÉ Ÿ“Š©A»½7;Ù`(ô5262^à€w@_Æ£/%w`,`,0bá3ìd“É K} †Œ’a<’!âì]œ%w`,|N*­w 5µ/ídƒ¡Ð×PÈØÈx™Š7´š$w`,`,0bá3ìd“É K} †ŒWXâŒza Lŀ䌅ÏIÅT€ ÝÞ‰›l0ú /pÀ; /ãÑ—’;00±ðv²Éä…¥¾CÆ É0Éqö.Î’;0>'€Ö;Кڗv²ÁPèk(dld¼LÅÚ Í ’;00±ðv²Éä…¥¾CÆ+,qF½0¦b@rÆÂç¤b*@ÐnïÄÍN6 } …ŒŒ8àЗñèKÉ  ŒXøŒ;ÙdòÂR_ƒ!ã…ddˆ8{gÉ Ÿ“ @ëhMíK;Ù`(ô5262^¦â í†æ„…É  ŒXøŒ;ÙdòÂR_ƒ!ã–8£^S1 ¹cásR1 h·wâf' …¾†BÆFÆ ðŽèËxô¥äŒŒF,|Æ€l2ya©¯ÁñB2ŒG2Dœ½‹³äNÆÆ¢G}ÿúåySéÖç?E2ò9ìÞ=¬¾¼åÙOèÄs/'æMÏž=>„™Ð×LØc­3Ÿ{aq>®õºi]ŽT©>WV¾2°S§Nµ,’­Z·ÞÖ÷Øa4öº{há3ÿ„ɀɒ(|òYãU&ôIDAT=ãN:ðˆ!”ÛªÕ6æKçοxíµWKÅ-¬×Ëp@ë`,âjšsÞMi]FÆ‚Åïßÿþl÷+®¸üÎ:|Ë‚)ÿvíÞ“ŽV@çÍ\F×Üÿ:Ýùê·H40‘ÇÀm/~AÓK*hä•‹é°!£¨ã®{6p‚¹±ÓN;ý÷ª«¦ßöùç›w…yÐË<4hÌEs’lÔ÷ÍFë26’Œß|S·ã#<\pÎ9£ïرãWÒ`Ø–ÛØlä <“N¹`&]pý}tõ¯_¡[žû­x{[äNÔA§ó»ëͨð‰÷éŠ;Ë­‘ˆϹÌعK·F&Bb·Ýºþ§ `ì#?þÛsþ÷¿ïÚKÎ`i–±ñ‚ÖÁ`ÄEï¼Öºf I:^nÝúS«wÞ©î¿|ù²Ë.¼ð‚‡úöíó—Ö­[×K¡u.Û´mG»ïÓ›ú} Ö¥4|r!›WJÓîzæ=þWZúòW0íwWÕ[fwöÃoÓ¯–õÔÿËÿý¿À‰Æ ÿX-C‘.žÐ:èÔŸ —&hçÆ"¿þú«Žï½÷§¼5kžΣ×^{Í-cÇŽYy≃^>à€^ïï¸ãߨDÝu¸Ù¹½u›¶ÄCÓÝö=ˆzæKwšõ«sШ)4lüutöå·Ð˜YwÓÅ7­¤Ë–>MWß³V“jkˆ›ïp)~¥ŽJÞÚȯä ÁgJ]|ylÙkßY¦ è© tý£²ŒÁÅÏÑ%7?Fs~M£¦-¡Ó/™+îJº’Ž9cœe88üDګסԹÛÞ´}‡2Š;b4bk·n»Ö¿¿wN;mس<òPX8ÿ†¸ü‹/¾0äÿø`ÿ~ø¾m:  ^ýO ¾Ç ‰ìlˬ—?ø:ûÉ3¬ã7ºIIDATQ”á“o²&ïñ|—ÌE7>hªL¼eM¹m qrœv÷‹Ö“kKß´ ÍÜÇj¬_Õ|ížéÍOo$¾]‡MÎ’6Óí_Òk¿¶&ÁrÂ]öÚÿhùß_»»êGñWO%ë~²Ì}Î fƒÄÛxÞ—ác¹ .‹Gx¸l®ƒëºõùϬº¹ EO}H<Ó—ñsÙ|q›gÜ[iÏ3àsâsã‘#>WNú|î#¦Þj]Öb#wÒØ«ˆMݱÃ/¶F úA|Q¯ÃÑ>}Ž =÷;Ø‘âQƒ¶ív`CòrCsâÙºu«ŸºvÝuóö^ÜqǾ6|ø™kx”Íë’%‹¯~øá‡.xþùòSß}÷~Ÿ}öi·Ÿ~ªo ±̃_€ÖA뚣qr_´N;cÑ\ópä§ŸnÚã¯ýK6#Ï<óôé+W>2–GDŠŠnšsÍ53Mž<©d̘óËÎ8ãô§O8aà+‡Ö¿ºW¯ý?à_¬­´ô‹ž|ò‰³_z©b0ÏïÙ¸ñ£}þû߯wÞ¶mkns±ƒýaL´.xmòZã¢uÆ ¯„á»ï¾í°yóº~ôÑÿíË&åí·ßðÊ+kO(/n(œ”•­Óô–-»óòÅ‹oqÓM…×Ï™s]ÑŒW/æÛp/½ô’_wáƒçŸÞ££F\Å¿˜‡ ú° x|åÑGõ&šC9¸æ ƒü_úÙo¿žöÙgï<ú²ÇÝ>åäÚ¹ó._vì¸ó×;ìÐá[!Êl¿}ÛïÛ¶Ýî‡í¶kóc›6­ëÙ±²ÊÍU¿öù3¯ãm¼ïËÇð±\—ÅerÙ\×ÅÆŠëæ6pRg³Åmã6r[¹ÍÜv>Nô|N#GŽXÍçÈçÊçÌç>sæŒ[o¸áúÂ Š®ãÑ6uÜWlðV¯^5ò¹çžöòË/¸n]Õ‘þsÍÁ~¸¡'ßÒÇ¿Ø0WÉÝ+£œÌ°­ƒÖùÍ qw‹ßŒòÑÇÀ0 qÁÀÿ¾0– È샺IEND®B`‚jbosscache-core-2.2.2.GA/src/main/docbook/images/TransactionLookup.png0000644000175000017500000001267110660354341025571 0ustar twernertwerner‰PNG  IHDRŒ]Û«–€IDATxÚíÝM«&G‡ñó1òò9\%ÄDƒâà@¢W!℉ º‡Œ É"ƒ‹Y 1,ŒCüYe%ÎJ Œ‡ Nžî¾ë®ê~úõwñ'œ<§_ª«{êzªº»ÎÅS°J.T$ H’3Iú;àœŒ’´o.3£Î@›OÒ$ i4€¤IphI_|C÷“<憽)ð${]i$ $=JÒgÕÕ"¦\\Ò×Û!i é9$ýì¯ûˆÝNöÉÝ®yïÿv?ì.?´©¡öTq¿'ë >X’’^FÒCòë.Ðk¸`Å`×EYfvZµÁ¡_÷KÒ@ÒKö¤ã.r,¶ÌŠmN=“õk×%i éUH:9,œñÜÚ$ìI“4`’Ž—_¡¤‡~ iÀ$ýôÛ/k= Gƒ·¼†«zplh§U¬78´‹¡Ú»¼¼¼ªóÇ»p€¤'–4¢Ê-ÕÛÕ÷îÝûè£Ôt»¤yzrI_ýö¹çž»{÷î“'OÔt£¤q.//oß¾ýÕW_© i’^º€¤Iz|üñǺ€¤«%y0Ð $­'½F¼s$MÒ4 iH4$MÒ4 iH4$MÒ4$MÒ4 iH4$MÒ4 iH4$MÒ4 éÍœ'‡B»’ÞÔyúü=9H´® i’’Iƒ¤E„¤AÒ$-"$ ’&i’ž5,^¤æ³‘eañbo=ÿþËoîýìG¯¾üâÕ¿ëW^záîë·ýù×$ ’&éýØz»»¾)éÅ¿jôÌyòèÝ¿üéí[/?¼ÿæþþ»«O.?}ç¯øÅïï·oýøñ?ÿHÒ i’Þ•¤o ï¤ÃÝ] »âÉbCËôvßãÕ3û-–¡w#™¢Æ½ö _ÜøÐYè-dPÇÌ•¡þ“þ÷¿?ùüê“«¾õ•§I$MÒ;”t·õïaFÁ2Á÷ƒ›r’q¯ð _,ÀÐÁÙ·÷‡¾‹dv?„£r_õ¡»†¾öôUúzÜ[ë ’&é]õ¤‡:”EsdV¬’tÆ^Eg ?f±Œ¤«*°¶˜«¾òÃûo |òþÛw_¿MÒ i’Þ§¡3 ÌØý¬†šÎ~Ì· ïF3ô³¼úò‹ÏîCåòÓw^yé’I“ôž»ÑÇ$¥Ò¬´Œ¤Û6Ò`ʤ¤'q3IÞªJ/£uI“ôÎ{ÒñCXñŠÅ§¥’|5ˆ¿ê!µ kžˆ9ùÔX<À¾†÷åô¤AÒ i9îûiÞÔrO$MÒ"…™^|iXóÓÝ?xõ»ñÓÝÿúÓ¯H$MÒ"â=i4HZD:3ŽýíÝ·®gûäý·Í8’&iYÑÜݯ¼ô³¹»¯úÖæîI“´ˆø+X i´ˆ4Hš¤eÚG‘§ÝÎÚÞ†š­„¤AÒ$-k4ôj—, O“4H$½jI÷κõ4ñ÷"{{ÒÁT_ñ‡Á«ÏñŸ¤Z&9ƒ·‹¤AÒ éõºW·ÅeâµzÇÀƒñ@–™_þ²w1ž&i4HzÃ’Î,_ëãäv2Ÿ÷ö›‹eš¬Û%AÒ i4I_ÄöòqqyI»-MÒ iô>%¹']5°<ò’&i4I“ôAë½Åßèm~F,.CþJf†Ùãqr!i4Hz«¯`U=óuÌWÑ„¤AÒ$-óù)î"3´4Hš¤E„¤AÒ$MÒ"BÒ i´ˆ4Hš¤E„¤AÒ i!i4I‹Iƒ¤IzóàPh÷@ÒzÒ"¢' ’I‹Iƒ¤IZDH$MÒ$-"$ ’vžH:5õôóÝ,[ž¶C¹‘EŽBå“4Hš¤[š¤Ú†ææòUëŽ,CsƒØ]qÁ¶uä®Wå‰ëÂLu¨|’INÒA—¥·ÑéörnþïÍ߯K^7aÝÕkË4ˆUÚÔI9{7WW¼¯¡-wË\ÜiÕ‘[¨=ïCg3>5™ó¨òÛ*_ë ’Þ¼¤kØÛEŽ[äâ*Y¾RŒ/ð'bÍäÍTµë¸Æ†vzòa°‹!'eNhñl6÷)UþøÊ'iô¶%=Þ‚Ý.BFÒÁ’ͪîe68#˜ªåWÌ4ÊÉ…Œ¬Ÿ|5N(i•ßp k]AÒÛîIOÛ.ÞÎß÷ Š5¾ÀI»œt’ò‡¬8¡$†n'‹Y¬ákÍ$†VùùJZWôîIçUé+ç5ÔÖLxCº¡ÀUÅ®õĘÚKn¤í‡ü8jóɰ­ò“Õ«uIïAÒUU°d:ÐU££“<ÚÝPàü—‰ Ã”((LC%{‡ÅbÇ…ÌŸÍiŸSùµ•OÒ é]Izæ7U6÷jŒy¡Nå/r k]AÒÇ’ôJ¦˜Øq÷W*ÁÊ׺‚¤Û“Ó‚‚¤AÒ"BÒ i’’I“4I‹Iƒ¤AÒ"$ ’&i’’I£ù<8Ú=4fåÁƒ·nݺÿ¾ªpÖ4VÄ—_~yçÎÇ¿öÚk_|ñ… qÖ4ÖÂÝ»w?û쳫=zôÆo|Ø}ìèÁƒ~ø¡ÊqÖ4¾È¼d⬠iìꂃ³ø×GÒÐÜÃYH.8׌³€¤¡¹‡³4 ¹wÖ44÷pÖÿúHš{8kIÃg ICsg i`ˆ>ø@%8kH’$ I“4$ H’$ I“4$Ýbî*g Icœ5À¿>’†æÎ@ÒpÁ¹fœ5$ Í=œ5€¤ͽ³€¤¡¹‡³ø×GÒÐÜÃYH.88kHš{8kIC˜»ÊY@Ò4 iHš¤ i@Ò4 iHš¤ iìsW9kH{¸àv\ Iƒ¤7U Ÿ¿'" Iƒ¤IZDHZƒ ’&i!i4I‹I“4Hš¤Ï’‹o¸ùó5ã7Ûe%GÚ¼Ö²GѼ÷Å+Ÿ¤I$MÒÓHzÚf}A=L²ë›_/ÿªAÒ$ ¸fHú[?÷öƒ3ŸÄ=Ñ¡Ÿìwhûq©z»ïñê™ýËл‘LQƒc¼®«^×7>tâÓ}@¯“4Hš¤·7ÜÑXܠ׺¼wï™íK•9¨@±Í…¯*ÀÐÖŠcïùS–üNv4O“46ƒ¹«ô¤3ÝÄŒz3÷tó_ ò+6Kº·2ýìâFòÇØpDù¯ m«“4I$½IO2ÜeM/%édák+~¡Ó“NÞÝ i’HzÃ’ÎzO%é†{¨ë—tU¿yÂáîd1Hš¤’^û+XÅç›&îΰãÙçÕ«–ÌÜbÏ?§VôzQÛñ!W}Á „íÁ1’Hz]’õØë¤’^£zÙÊ_[ý“4I$-"$MÒI‹I$-"$MÒI‹I㨘qŒ¤EHš¤±b?©’^èÑâi·³¶·›f«!i4IË ½Zç% ÃÓ$ 4I¯ZÒ½³h=Mü ÈÞžt0uWüaq&µ`¶Þe’“}ºH i’^¯¡{u[\&^«w <d™ùUïí7Ë<4ù¶K‚¤’&i’¾ˆí;äãâó’v[š¤’&é}J:sOºj`yä'$MÒ$ ’&éƒ>8Ö{‹7¾ÑÛüŒX\†üˆÌ ³ÇãäBÒ€k†¤·ú VÕ3_Ç|MHûÁŒc$½~?Å]d†’HZDH𤒒@Ò"$MÒI‹I“4@Ò"BÒI‹I“4pvI8$ èI‹ˆž4I$-"$ýaÆ1’!i’ÆŠý¤HZ„¤I$MÒ‡™—ûùëžeü»YÃŽYe©C³ß©N%I$=¤ƒåLmÍ$íàTËg6è§ûÃ╳ԮOä´ «”ôä×Iƒ¤+é íþªÛ½ëý¤¸îÉZ'«t·VÜB²$U;RÒ½]áL±»ûêÖOCgŠÔ{^ªNtð¿U[*d¼¯¡«¨á¤Äg¶Xí$ ôXI×+þ¤W„AkUlmk·—dÌg$=þbIHU7×jí‰îþ7_ìüÞãÓ=òÐj/°¡YÐÓ$ ’Þª¤'·T±Õ‹¯Ì¯Ú$Ýv×°·ÿ×íÌe~ˆ4èðk>èò¶­Øf²*Iç‹]\,8Ó~ÿS-$ ô{ÒµbžYÒçèI·UBò“âhjseÎ)éª1äZN.éÚ Œ¤5¸ é…ïI—t­Wú|Åv|ž{Òµ½ç¤•Gîn6I×þ_~NIW•¤5¸ é³HºêÉçüãZ™—”26­ÚWü­â¬Ow7?d—´F­ƒ>_ò ¬ÌcSSÝwÏ;(d~T¼{Ÿ¢íÁ±^=CîÓà¢3Ž=õžô*ß"“M×yñÎI éeZg†ž¹¶WXçñØŒÉL´ˆ4@Ò"BÒ$ ô†UM=ó}•OÒIÏ1wwü:Мsko±mÝÍÔÙÍÏO©|’HzìÜÝ™ù™k'[žü=æÚ›:{©³‹’ÎO‰ªò*Ÿ¤ÍKzÌŒccfÄœní1z˜:{’©³ó}J•?¾òIض¤Ç[09…rq†ŠyæÖ®šóÙÔÙç˜:{Œ¤U~Ã5LÒ€žtãl™Ïyþþ‡©³g›:{*I«üü?%’Æf0ãØÓIçî9¥v±¯s޹µ—’´©³”ôa+Ÿ¤±A?©„éæîn›Õ93ÙòäOw7ØÔÙ“L=íƒc*¿¶òI$½+Iï~Új³xÎ_+™Âú°ó–“4HúX’^íÊ»)ðþªQå/Xù$ ’>nOZDL JÒ i’’I“´ˆ4Iƒ¤IZDHpÍ´I“4Hš¤E„¤5¸hÀŒcÏþá8$ À¡GI’€¤I’$ I«AH’&iHGÃŒc@ÒØÃÚL’IIÃçš’II$ $ ’m&Iƒ¤€¤á‚4HHÂŒc@ÒôN+À¡ ô¦*åó÷Dä !i4I‹I$MÒ"BÒ i’’Hš¤§ÉÅÿ9Ó–ãž°x=Œ\kÙ£hÞûâ•OÒI“t¡u^¤™^P“ìúæ×‹Å¿j4Iƒ¤IzŸ†úÖ'Ê,Öû  ×aC=Ñíµfh™Þî{¼zf¿Å2ôn$SÔ† On|è,ô2¨’Hš¤çôPs<ÔFýÈLƒ»vhSyaËdJï7Ù“.n$_€d…}íH~X\ýž&i4I¯k¬»ÛyÍH«Ö:µ÷t« “\±YÒC÷ŠýìâF¦­ð6I7¯NÒI“ô¬’¹ØT’Ž;ÍÉ¡à¥$,|íb™ oîIÇÕKÒI“ô2žÞº¤î¡®_ÒUýæ ‡»“Å i€¤Iz¾W°ªF_kÇ“k‹Ÿöªî.>†–p,~$>ó¨Z¼»¢×‹ròÁ±â÷Ž$MÒrô¬Í…ÇyY‹¤AÒ$-’Þ i’Hš¤E„¤AÒ$-"$ 4I‹Iƒ¤IZDH i’’I“4I/û,ñ´ÛYÛëL³Õ€4Hš¤e†^­ó’…ái’Hš¤W-éÞi³’“šu{ÒÁ\]ñ‡Á»ËñŸ•Z&9»§‹¤’&éõºW·ÅeâµzÇÀƒñ@–™_²w1ž&i€¤IzÃ’Î,_ëãäv2Ÿ÷ö›‹ešmÛ%AÒI“4I_ÄöòqqyI»-MÒI“ô>%¹']5°<ò’&i€¤Iú ŽõÞâoô6?#—!ÿ!3Ãìñ8¹4Hš¤Iz«¯`U=óuÌWÑ„¤AÒ$-óù)î"3´4Hš¤E„¤’&i!i4I“´I$MÒ"BÒI“´ˆ4Hš¤E„¤’>{¥8d’Ö“=i€¤IZDH$MÒ"BÒI“´ˆ4Hš¤Iº<ËôóÝ,[žñ«,~ɨ|’I“tK“TÛÐÜ\¾jÝ‘ehnWÕ¶ŽÜõ‰Û–UÝõ®§º T>Iƒ¤'é )éþª·—sóoþ6^òºMì®ÞÖ'ëýmU‡6uRÎÞ ÆÕïkhËÝ2wZu¤ÁjÏûÐÙŒOMæ<ªü¶Ê'iôæ%]kÄÞ.rÜ"W ÌÚð•b|‡<k&o¦ª]ÇUì´ûß|±ó{Ïfü½'ÿeKå7T>Iƒ¤·-éñìv2’–lVup/³¡ÀIÁä9¿bƒ'†>òDí™SJZå7\Ã$ ’Ö“®héªnýÎÓ“Îlè‹ n¾'4ƒ'’Å®]¬¶ÒFJZåçÿ)‘4HúX÷¤kGê$=í=é†Oî‰1#íÍž¨ý!¿ü¶$}ØÊ'iô~$]õdõͧlŠÕT åééî†W¸ön¼ê!  0 •\O±ØA!«Îæ´Ž©üÚÊ'iô®$=óË*›{»FƼL¥ò¹†I$},I¯d–‰xÕ¨ò¬|’I·'-"¦Hš¤E„¤AÒ$»õ5fôò#ŸçP­zÐI„¤’&é…M"i!i€¤“t0Epñof'Fnx[´mƒÉ—yª^°ñô™4@ÒKJºm‡1S2µMŒþÿ ')þÿÿÿ&ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿì¥Áa ð¿Hjbjb„,„, 0îNîN2ÿÿÿÿÿÿˆ˜˜˜˜ÀÀÀö¾¾¾¾ Ê ö8¶âââââ§§§¯±±±±±±,îR@^ÝÀ§K\§§§Ýǘ˜ââi òÇÇǧ˜ÔâÀâ¯ÇÔ蘘˜˜§¯ÇÇclTÀ¯Ö %êäÁ¾·{¯08ƒ,žÇž¯ÇÀöö¤š$ööš CACHE CACHELOADER CACHE REPLICATION STORE )*01=?EHíéÛéÛéÛéËéÛéhï<»hï<»56CJOJQJhï<»hï<»5CJOJQJhï<»#jhï<»UaJmHnHsHtH )*01=>?EFGHýýýýõýýýõýýõýýý$a$gdï<»Gþþ.:pï<»°|. °ÈA!°"°# $ %°°Ä°Ä Ä¥<@ñÿ< NormalCJaJmH sH tH jj b}È Heading 1#$ & Fdh¤ð¤<@&gdÙ@L5CJKH \^J_HaJ DA@òÿ¡D Default Paragraph FontRióÿ³R  Table Normalö4Ö l4Öaö (kôÿÁ(No List)*1Hÿÿÿÿÿÿÿÿÿÿÿÿ'ÿÿÿÿ&ÿÿÿÿ)ÿÿÿÿÿÿÿÿÿÿÿÿ+)*14ÿÿHÿÿÿÿ ÿÿ+£H)*01=>?EI˜0€€Ø0«˜0€€°Ê€˜0€€Ø@­š0€€Ø@­€˜0€€°Ê€˜0€€Ø@­˜0€€°Ê€˜0€€Ø@­˜0€€°Ê€˜0€€Ø@­š0€€Ø@­€˜0€€°Ê€š0€€Ø@­€Iš€€ÿ¿(|"0³HHGð8ð.*.@ñÿÿÿ€€€÷ð ð%-ñð² ð( ð ððH¢ ð) # ð €ÿ ðð ððN ð S ðÿÿ™¿ËjJÿˆðð ðH2 ð C ðÿ™¿ÿˆðððH2 ð C ðÿ™¿ÿˆðððH2 ð C ðÿ™¿ÿˆð ððH2 ð C ðÿ™¿ÿˆð ððH2 ð C ðÿ™¿ÿˆð ððH2 ð C ðÿ™¿ÿˆð ð ðH2 ð C ðÿ™¿ÿˆð ððNB ð  S ðD¿ÿˆðððNB ð @ S ðD¿ÿˆðððNB ð  S ðD¿ÿˆðððNB ð@ S ðD¿ÿˆðððNB ð S ðD¿ÿˆðððNB ð S ðD¿ÿˆðððT ð C ð€‚ÿ ˆðð ðððh ð!ð˜ ð# ð ÿˆ#"ñ ‘ðððT ð C ðÿÿ™¿ËjJÿð!ð|ððN2 ð 3 ðÿ™¿ÿðu ¤Ý ððN2 ð 3 ðÿ™¿ÿðñtYÜ ððN2 ð 3 ðÿ™¿ÿðu tÝ Ü ððN2 ð 3 ðÿ™¿ÿðù taÜ ððN2 ð 3 ðÿ™¿ÿðu ø Ý ` ððN2 ð 3 ðÿ™¿ÿðù ø a` ððN2 ð 3 ðÿ™¿ÿð}ø å` ððTB ð  C ðD¿ÿð) ) tððTB ð!B C ðD¿ÿð¥ ) tððTB ð" C ðD¿ÿð) ­ tððTB ð#B C ðD¿ÿð) Ü ­ ø ððTB ð$ C ðD¿ÿð­ Ü 1ø ððTB ð% C ðD¿ÿð­ Ü ­ ø ððZ ð& 3 ð€‚ÿ ð!` a|ð ððf ð' S ð€™Ì¿ËjJÿ ðñ|1˜ð ðð`B ð( ƒ ð0D¿Ë8cÎÐÑÿðððZB ð+ S ð€™Ìÿ¿ËjJÿðð ððTB ð-@ c ð$D¿ÐÑÿðððB ðS ð¿Ëÿ ?ð H-ÉD ½Èt+ È9t±úÿÿ ñ( t=ˆ=¤t=ˆÁ¤t¹ˆ=¤t ¹¸= t 5þÿÿ¸¹ t ¹¸¹ t ¤u t‰¤ñ t¤m t‰ ñˆt mˆtýÿÿ éþÿÿˆtPÿÿÿm¸t±úÿÿœþÿÿ‘ ( t)‘ ˆM( t(u ˆµˆtMœþÿÿ-$D tIIŸ4v5°2Bÿÿÿÿÿÿÿÿ"„Є˜þÆÐ^„Ð`„˜þ56CJOJQJo(†*‡hˆH.0€ „ „˜þÆ ^„ `„˜þ‡hˆH.‚ „p„LÿÆp^„p`„Lÿ‡hˆH.€ „@ „˜þÆ@ ^„@ `„˜þ‡hˆH.€ „„˜þÆ^„`„˜þ‡hˆH.‚ „à„LÿÆà^„à`„Lÿ‡hˆH.€ „°„˜þư^„°`„˜þ‡hˆH.€ „€„˜þÆ€^„€`„˜þ‡hˆH.‚ „P„LÿÆP^„P`„Lÿ‡hˆH.Ÿ4v5ÿÿÿÿÿÿÿÿt¬        ÿ@€à<5"HP @ÿÿUnknownÿÿÿÿÿÿÿÿÿÿÿÿGTimes New Roman5€Symbol3 Arial ñˆðÐhó±Fó±F!ð¥À´´€~4µððÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿjV‰ÿÿ Manik Surtani Manik Surtani þÿ à…ŸòùOh«‘+'³Ù0˜ ˜¤°ÈÔä ü $ 0 <HPX`h'Manik SurtaniNormalManik Surtani4Microsoft Word 11.2@@¼´ÌiDÇ@xðiDÇG( þÿÿÿPICT €Zÿ ÿþHH€Z €€ÿÿšÿ€´€ZHHÆ1T®¸¨€Z€Z§ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿÿÿàóÿàûÿ/ÿÿöóþðóóÿõóðóûÿ3ÿÿöó~`ðóóÿõóðóûÿ/ÿÿöóþðóóÿõóðóûÿ-ÿÿôóðóóÿôóðóûÿ-ÿÿöóüòóóÿöóüòóûÿJÿÿùóþóóóóôóóÿùóþóóóóþõóûÿGÿÿüóþþóþýóþ÷óóÿüóþýóýóþ÷óûÿYÿÿüóýó~`ýó~`÷óóÿüó~`ýóýó~`÷óûÿCÿÿüóýóþýóþùóîýóþýóýóþ÷óûÿ'ÿÿîóúóêðó÷óûÿ-ÿÿïóýøóóÿïóýøóûÿEÿÿñóóóóùóóÿðóóóóúóûÿIÿÿòóþóþóûóóÿòóþóþóûóûÿEÿÿöóýþóþýóþþóóÿõóþþóþýóþþóûÿ[ÿÿöó~`ýó~`ýó~`þóóÿõóýó~`ýó~`þóûÿGÿÿöóþýóþýóþþóóÿõóýóþýóþþóûÿ5ÿÿZÐV°ZÐRJOçóóÿZÐV°ZÐRJOçóûÿ!ÿÿâóóÿâóûÿ!ÿÿâóóÿâóûÿÿÿàóÿàûÿ1ÊÿþO BgBhBgBIO > F†BIBhB*> B*ýO õÿÊÿîO õÿÊÿîO õÿÊÿìõÿÂÿþëÿÅÿþèÿÈÿþåÿÌÿýâÿÏÿþÞÿÒÿþÛÿàÿìßÿåÿøôO?÷åÿåÿûîO?úåÿ)åÿþO?ùçç%))J1Œ÷ýO?åÿåÿãO?åÿåÿãO?åÿåÿãO?åÿåÿäO?åÿåÿâäÿÙÿùÙÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ§ÿþÿ ÕÍÕœ.“—+,ù®0Ô `hpx€ ˆ˜  ¨ µ'  Titleþÿÿÿ þÿÿÿþÿÿÿ !"#$%þÿÿÿýÿÿÿ(þÿÿÿþÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿRoot Entryÿÿÿÿÿÿÿÿ ÀF€šúiDÇ*€1Tableÿÿÿÿÿÿÿÿ®WordDocumentÿÿÿÿÿÿÿÿSummaryInformation(ÿÿÿÿDocumentSummaryInformation8ÿÿÿÿÿÿÿÿÿÿÿÿCompObjÿÿÿÿÿÿÿÿÿÿÿÿXÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿ ÀFMicrosoft Word DocumentþÿÿÿNB6WWord.Document.8jbosscache-core-2.2.2.GA/src/main/docbook/images/SharedCacheLoader.doc0000644000175000017500000005500010660354341025345 0ustar twernertwernerÐÏࡱá>þÿ (*þÿÿÿ'ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿì¥Áa ð¿ejbjb„,„, 0îNîN&>ÿÿÿÿÿÿˆ˜˜˜˜ÀÀÀöþþþþ  ö`¶"""""ÏÏÏ×ÙÙÙÙÙÙ,Rh^ÀÏuZÏÏÏ""S ïïïϘÔ"À"×ïÔ蘘˜˜Ï×ïï‹lTÀ× rêäÁþߣ×00`«,ÆïÆ×ïÀööäÚ$ööÚ CACHE CACHELOADER CACHELOADER CACHE REPLICATION STORE %&,-9:FGMNZ\beíéÛéÛéÛéÛéËéÛéhï<»hï<»56CJOJQJhï<»hï<»5CJOJQJhï<»#jhï<»UaJmHnHsHtH&,-9:FGMNZ[\bcdeýýýõýõýýýõýýõýýý$a$gdï<»&dþþ.:pï<»°|. °ÈA!°"°# $ %°°Ä°Ä Ä¥<@ñÿ< NormalCJaJmH sH tH jj b}È Heading 1#$ & Fdh¤ð¤<@&gdÙ@L5CJKH \^J_HaJ DA@òÿ¡D Default Paragraph FontRióÿ³R  Table Normalö4Ö l4Öaö (kôÿÁ(No List!(56=eÿÿÿÿÿÿÿÿ/ÿÿÿÿ'ÿÿÿÿ&ÿÿÿÿ)ÿÿÿÿÿÿÿÿÿÿÿÿ+!(56=@ÿÿeÿÿÿÿ ÿÿ+£e&,-9:FGMNZ[\bcf˜0€€Ø0«€˜0€€°Ê€˜0€€Ø@­˜@0€€°Ê€š0€€Ø@­€˜0€€°Ê€˜0€€Ø@­˜0€€°Ê€˜0€€Ø@­˜0€€°Ê€˜0€€Ø@­˜0€€Ø@­€˜0€€°Ê€˜0€€Ø@­€˜0€€Ø@­€fš€€ÿ¿(|â ¯eedð8ð0,0@ñÿÿÿ€€€÷ð ð&/ ñð˜ ð( ð ððH¢ ð) # ð €ÿ ð#ð ððN ð S ðÿÿ™¿ËjJÿˆð"ð ðH2 ð C ðÿ™¿ÿˆð!ððH2 ð C ðÿ™¿ÿˆð ððH2 ð C ðÿ™¿ÿˆðððH2 ð C ðÿ™¿ÿˆðððH2 ð C ðÿ™¿ÿˆðððH2 ð C ðÿ™¿ÿˆðððH2 ð C ðÿ™¿ÿˆðððNB ð  S ðD¿ÿˆðððNB ð @ S ðD¿ÿˆðððNB ð  S ðD¿ÿˆðððNB ð@ S ðD¿ÿˆðððNB ð S ðD¿ÿˆðððNB ð S ðD¿ÿˆðððT ð C ð€‚ÿ ˆðð ððN ð S ðÿÿ™¿ËjJÿˆðððH2 ð C ðÿ™¿ÿˆðððH2 ð C ðÿ™¿ÿˆðððH2 ð C ðÿ™¿ÿˆðððH2 ð C ðÿ™¿ÿˆð ððH2 ð C ðÿ™¿ÿˆð ððH2 ð C ðÿ™¿ÿˆð ððH2 ð C ðÿ™¿ÿˆð ððNB ð  S ðD¿ÿˆð ððNB ð!@ S ðD¿ÿˆðððNB ð" S ðD¿ÿˆðððNB ð#@ S ðD¿ÿˆðððNB ð$ S ðD¿ÿˆðððNB ð% S ðD¿ÿˆðððT ð& C ð€‚ÿ ˆðð ðð` ð' c ð$€™Ì¿ËjJÿ ˆðð ðð`B ð( ƒ ð0D¿Ë8cÎÐÑÿð$ððZB ð+ S ð€™Ìÿ¿ËjJÿðð ððTB ð-@ c ð$D¿ÐÑÿðððTB ð. c ð$D¿ÐÑÿðððZ ð/ S ð€™Ì¿ËjJÿ ðð ððB ðS ð¿Ëÿ ?ð  !"#$e.!D ÉÈt/ýÿÿ( ÁD t'( ]!D t&M ( t%و٤t$Ùˆ]!¤t#UˆÙ¤t"U¸Ù t!ѸU t U¸U t© ¤" t%¤ t¡¤  t% ˆt¡  ˆt …ˆt¡Pÿÿÿ ¸tMœþÿÿ-$( t-ÉD ½Èt+ È9t±úÿÿ ñ( t=ˆ=¤t=ˆÁ¤t¹ˆ=¤t ¹¸= t 5þÿÿ¸¹ t ¹¸¹ t ¤u t‰¤ñ t¤m t‰ ñˆt mˆtýÿÿ éþÿÿˆtPÿÿÿm¸t±úÿÿœþÿÿ‘ ( t)‘ ˆM( t(u ˆµˆt&f&fŸ4v5°2Bÿÿÿÿÿÿÿÿ"„Є˜þÆÐ^„Ð`„˜þ56CJOJQJo(†*‡hˆH.0€ „ „˜þÆ ^„ `„˜þ‡hˆH.‚ „p„LÿÆp^„p`„Lÿ‡hˆH.€ „@ „˜þÆ@ ^„@ `„˜þ‡hˆH.€ „„˜þÆ^„`„˜þ‡hˆH.‚ „à„LÿÆà^„à`„Lÿ‡hˆH.€ „°„˜þư^„°`„˜þ‡hˆH.€ „€„˜þÆ€^„€`„˜þ‡hˆH.‚ „P„LÿÆP^„P`„Lÿ‡hˆH.Ÿ4v5ÿÿÿÿÿÿÿÿt¬        ÿ@€à<5"eP @ÿÿUnknownÿÿÿÿÿÿÿÿÿÿÿÿGTimes New Roman5€Symbol3 Arial ñˆðÐhó±Fó±F!ð¥À´´€~4µððÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿjV‰ÿÿ Manik Surtani Manik Surtani þÿ à…ŸòùOh«‘+'³Ù0 ˜¤°ÈÔä ü $ 0 <HPX`h'Manik SurtaniNormalManik Surtani3Microsoft Word 11.2@FÃ#@xðiDÇ@H;jDÇG” þÿÿÿPICT Œ€Zÿ ÿþHH€Z €€ÿÿšÿ€´€ZHHŠôÆ $€Z€Z§ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿÿÿàóÿàûÿ/ÿÿöóþðóóÿõóðóûÿ3ÿÿöó~`ðóóÿõóðóûÿ/ÿÿöóþðóóÿõóðóûÿ-ÿÿôóðóóÿôóðóûÿ-ÿÿöóüòóóÿöóüòóûÿJÿÿùóþóóóóôóóÿùóþóóóóþõóûÿGÿÿüóþþóþýóþ÷óóÿüóþýóýóþ÷óûÿYÿÿüóýó~`ýó~`÷óóÿüó~`ýóýó~`÷óûÿCÿÿüóýóþýóþùóîýóþýóýóþ÷óûÿ'ÿÿîóúóêðó÷óûÿ-ÿÿïóýøóóÿïóýøóûÿEÿÿñóóóóùóóÿðóóóóúóûÿIÿÿòóþóþóûóóÿòóþóþóûóûÿEÿÿöóýþóþýóþþóóÿõóþþóþýóþþóûÿ[ÿÿöó~`ýó~`ýó~`þóóÿõóýó~`ýó~`þóûÿGÿÿöóþýóþýóþþóóÿõóýóþýóþþóûÿ5ÿÿZÐV°ZÐRJOçóóÿZÐV°ZÐRJOçóûÿ!ÿÿâóóÿâóûÿ!ÿÿâóóÿâóûÿÿÿàóÿàûÿYùÿþO BgBhBgBI> F†BIBhO B*> B*ýO çÿþO BgBhBgBIO > F†BIBhB*> B*ýO õÿ!ùÿîO çÿîO õÿ!ùÿîO çÿîO õÿùÿìçÿìõÿíÿØÿþëÿëÿþÞÿþèÿèÿýåÿþåÿäÿýíÿýâÿàÿþóÿþÞÿÝÿýúÿþÛÿàÿìßÿåÿøôO?÷åÿåÿûîO?úåÿ)åÿþO?ùçç%))J1Œ÷ýO?åÿåÿãO?åÿåÿãO?åÿåÿãO?åÿåÿäO?åÿåÿâäÿÙÿùÙÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ§ÿþÿ ÕÍÕœ.“—+,ù®0Ô `hpx€ ˆ˜  ¨ µ'  Titleþÿÿÿ þÿÿÿþÿÿÿ !"#$%&þÿÿÿýÿÿÿ)þÿÿÿþÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿRoot Entryÿÿÿÿÿÿÿÿ ÀFE(jDÇ+€1TableÿÿÿÿÿÿÿÿÖWordDocumentÿÿÿÿÿÿÿÿSummaryInformation(ÿÿÿÿDocumentSummaryInformation8ÿÿÿÿÿÿÿÿÿÿÿÿCompObjÿÿÿÿÿÿÿÿÿÿÿÿXÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿ ÀFMicrosoft Word DocumentþÿÿÿNB6WWord.Document.8jbosscache-core-2.2.2.GA/src/main/docbook/images/BuddyReplication.png0000644000175000017500000001145210660354341025347 0ustar twernertwerner‰PNG  IHDRyöäœÙñIDATxÚíÝÁk×pÿùòwô”àZÁ`R²ØHqÎÅÄTÁ)4íÁ05qkP1†RÓƒ!`˜ œCU &†@tÊIƒa«Æ TïÌ›ß{ófvVú|ø¬õîjöíìîW3;óÎÌ`g º&º&蚌Ú5I½º¦^‘ñNs½Ñ5uM]S×Ð51ž€z“Ñ5Ïü¢ñÂÅËS¿¬éʉ{8sLî(ݤà¶=ox|ÄtM@×ìîšUÊ\A×,n~eM±O¿l¼+]Ð5ëtͶjظyòµ o›¾Ã×þ«ñ¶% þØvö_ªkºfµ®ÙVï:ûbci˽ík1rµÖQÈ\Ú¶_ªkºfµ®Ù§¨µ]˜»A4«’v6æžA×tÍUêš‘bWÜ5«?]Ð5ïšm;µÓ.þ¢Î’§kœØ®¹xdLÛ±>ñ ¿·ó˜žÜcƒæÉ=ò‘cƒÒ㳿¿8žÏŸ?·Rºf^×ÂÒOTT÷±\¿~ý믿¶FºfGסÏ-å¤ëÃ=–7ÞxcccãåË—ÖH@×,¹1mö÷÷g³ÙO?ýd(]S׬ÌÞs@×Ô5±µµeï9 k6wMú³÷Ð5ç¶kÁIŽ]S×@×@×@×Ô5Ð5Ð5Ð5uMtMtMtM]]]]S×@×@×@×Ô5Ð5Ð5‡XVÆwZºæüÛ/DDDDdÌèš""""¢kêš""""º¦®)""""º¦ˆH4gþ_ü&‘ ƒÿ+"2µ<û矯_¹xþÜÛ‡Ekíì[ë³§ÿø“®)"RØ5³á»¦Î*"CçåÓÍÛøÝì¹·>Úû×_/Ùÿæó‡ûý¥wß¹ùñoŸÿ玮)"Ò«k¶µÏÆ- ‹¾V/lü]‹¦L/°J*"e9,š×.¿wð￾vùá%ׯ\<¬›º¦ˆHá>ôÄ–Îâ ·GF®ÓXˆËùÚÃñÌŠHÙ®óÙ…s‹Eó¨n^z÷£éº¦ˆHÆvÍþµ2±q1Ý5#Û>ã¿QÑ‘â\¿rñÁ­Wxüå'ë3]SD¤æô¬¢¹¸q±¬h¶íCW4Ed¸œ?÷ö«ïh¶eÿ›Ï×ξ¥kŠˆ”4t×ìÜáÙโˆHn"µêè:º¦ˆHÆ—5sš·Ÿ/)qÄOÛQ>‡þ´Ü‚ãèEDl×Ô5Eä„l.ñ}M]SDDÑùßqè¿9ÿëôqèOþþG]SDDDDJâüšº¦ˆˆˆÈP9š7èÑæÇGó=þò󉈈ˆHµéׯ\\;ûÖ«ùЯ]~Ï|è""""2^tMÑ5uM‘šZ‡c·í|µ]ÙÂ7Þªú8ôáqFCDtM]SD,ºæÐ]şCÝÑ5uM©Ó$çÈ™w͂ӶխmÊÇÎ ÛîÞ2«dúÓ Ó6 ã±Ä…·ÌŸ™XŒÎ™6?f=ݺ¦ˆ®©kŠHµ¢ÙØi"•+q«¶–Óy'YØÒw˜X˜Üë\˜5°Á1ŒìL/[øôDD×Ô5E¤r׌\¿¸ÜdÕÍà–ÎÈcŒl­Õ5Ó¥³my:›hÙ˜ÔzŽDD×Ô5Ed5ºf¼nF~cAwœH×ÌZŒÈãÕ5EDבUíš‘ïkÆ»fÿKFëšm»¶Óv–K]SDtM]SäôÔøuÆt*> (½ â÷ '®–^øÆ; ^ØY.;ÁŠâ“xtñ¡V4EtM]SDjvÍž7TJ*ŽótÓÓ*¢kêš"2R™èÜæ'µêÚDÆÓÓ*¢kêš""""¢kêš""""º¦®)"""¢kêš""""¢kêš""""º¦®)"""¢k®j×`|¶kŠˆˆˆˆíšº¦ˆˆˆˆ®©kŠˆˆˆˆ®)""""º¦®9nΜyó(Å7õàõ‡[°Æk&n^öWwðEDDtÍ1ºfÅòqüúY·í¹ ÅÝâµöì(¹¹¬•®D×,¸«1_DDD×£k&6 -þWã6§ã?ÿßô5šÁâÍs—3]ªâ ÜY‚ËßxomK’Õ¯ßÖì¼±àv.IbeˆüÀ‘_DDD×¶kæ»t5‰´ŸÅ$šMA3®»Àm%¯íß‘_šhuÁm·mmµ`‚#~¼Á¯8òà‹ˆˆèšvÍþenqcRÁ–¶Î픑…<~·‘%‰/p¼¶ŽÙ5³Ú|Ý®™Þü™µ&dÀƒ/""¢k»]³îFÍÎ/k¦¯Ù¹»6ò_ý8²/{éE³ú2T)šñþ.MÑ5WþûšñÆÜü¯Ä†±Z_Ö,XàànÜ‚º“¾yÿ®Ù¹œø"c°íE†±óævÍ!_DDD×õ8ôàQÉéÃM"›6üC„^°À=fî‘:¹µ Sp`PçcOå“>ô'}tNõ§¬lðEDDtÍéž_så>³§³Àñ®yÚFƃ]óTwÍ•Û84N,Érï4t/5EDD×4oˆˆˆˆ®©kŠˆˆˆˆ®Ù§kÌÍ3Ü®Ïy¿™'M^q«?ØÜÑÑ5WòœGUŠBÝïÃU¹+S‡Oí7ú ©ˆˆèšºfɾÛ&¡ŽŸ¿&}ɼƬ֦_Ê ÏÃ'Xíôôâé.Ò§kÎG™lÐÔáC rÏ®Y¶.‰ˆˆèš+¹]3²ßy¥‹æÜÔáãÍÎM°Š¦ˆˆèšºf~PüÊ*³Z›:|ˆA.ëšYO‡·$Ñ5uÍè6°Ñfµ6uøRyÞc&w‰ˆˆ®é\îÑŒ\–ÕHNùÔአˆˆˆ®¹´yƒ†."KßôuʧW4EDDtMsTŠˆˆˆèšº¦ˆˆˆˆ®i>ô‰ì ñ=æþñ‚ -‹¿ ÐvF÷yíy2½ó*w›¾íøs¬×ú¾A|Å8©ßg¨¸úÕ¢º«Ó_Méó®2Ù×ÔhOôŽJœÈà÷ù8¶ë𽿠úÅ3Ü4åã|¤õ™<ýôtÍÈù›²~cúÍh)o(§ÙùH¬ÉÖÓÄ9Å&Û5ǹ·Šï¢ÓM ñDÇgXî d:ohñSÚY –P×|³óÝ¿ÊÔÞÁòQp6¢Î«Îó%f~/[ªÜSZÆ—91¶‘iÐ;ŸúZwÞ6žñÉE«kd¦Äª’˜ùs;WWñ‡Vð¹H/vâátŽFÙFÖôìÇ—ªsVÕηŽÈÚØøÛ#+gÁ"u¾ƒe½¾‚o¶‰U.8Wíʽ¦ÊÖÏàÇPÖ§@ú¡‚•ª³ëLä Íj<èj¬k†Î¸^ejïÈ˾Ïl7ŧ@o|nBËà/ŒÌÉß9[åÎÛÞÈrÿ Ž|ÌKOV?ïwÎü>›ê##ük-wž…ȳŸ+÷OÊÎ¥m[ý÷–8inç/ô¬e(Øø9ƒûìžþk*wýÌÒÈR¥ßiƒ:¹»ì¦ó†f5n56zá|èYÓ%¦b×Lo)™5µcÁÚ³êi¢†FæXï|ã+¾óøfÎà[LçšYüLÅW˜â)àã­sSzÁué½ùÅ;ô#ÏBÖ ÓsõËšè5±Hñg¤ÿĹ¹ßéÜœsÂ^S»Ê>†r»fÁJUð7êßj<ÜjlÞ /Ê>Ʋ¶äå¾_÷Ù¨™»Ùiˆ9Ä«LžÜ RöDD¾Zvçé½3Y[þÚ>-úÿ¡RýÛ“ñéýJƒÍÈ–ƒZE³ÏÚžXÙúÍàsZqžÞâ}‘ñ­A}>›Úk*wý ¾AEÞÓßê¿RņYâšÕxÐÕX×,üHv¾ây±ËöÁ•uÍiNžÞÿgϽóà—_û|£±Ïª’{O•®ÿíÓ5ƒßc.8Ô)½E0~¼Tÿ2—»/¾ì®‚¯ ÷!=ķܦóšÊýS-ëÛSÁ ºfVM™þšÕxÐÕX×ìu`ÐêÜÃ9½x a1Ôº¦®)"""¢kêš""""¢kŠˆˆˆˆ®©kŠˆˆˆèšº¦ˆˆˆˆèšžl]s¨® ÀøNE×XuwïÞ=|ç¼uë–¡N ]`$/^¼¸páÂÓ§OßÿýÝÝ]蚺&@5=úàƒ^¾|yX7¯^½zøc蚺&@ëëë>|õïíímc蚺&@ßÿýÚÚÚÁÁÁ«üñÇK—.½xñÂȺ¦® Ð×§Ÿ~º¹¹yü’Û·oß¿ßȺ¦® PáݶîiDtMœ?þçŸ6€®©kÔ7›ÍöööŒ kêšõ}øá‡?üðƒqtM] ¾k×®={öÌ8º¦® Pß7¶¶¶Œ kêšõ}öÙg=2€®©kÔwûöí¯¾úÊ8º¦® Pß½_@×Ô5ê{ðàÁ;wŒ kêšõíîînoo@×Ô5Ð5Ð5Ð5uMtMtMtM]]]]S×ÉöööÎÎŽqtM] ¾ÍÍÍû÷ï@×Ô5ê»wïÞÝ»w kêšõ=|øðæÍ›ÆÐ5uM€ú¶¶¶nܸa]SרïÙ³gW¯^5€®©kÔ÷Ýwß隀®©k bggçòåËÆÐ5uM€úöööf³™qtM] ¾ƒƒƒ³gÏ@×Ô5á\©k k k kêšèšèšèšº&º&º&º¦® 0žÝÝÝÇ@×Ô5ê{òäÉÆÆ†qtM] ¾õõuã蚺&@}{{{³ÙÌ8º¦® PßÁÁÁÚÚšqtM]¨öŽ=y® ÐþŽóí"Åñ¹º&€®)º& kº¦èš€® èš"º&èšãuÍ3¿8þcÅ»ªxo¹7èõ·ª>ýG¸ÊhøÜ] Ú5‡è…ºæÐ]şcñæ>w@×(éšS×tNNÓ¶Õ-1+Oú¶û_ü¯ÈÆ'¨L\?ñïÈìD‘!êù¶íšé1iøÓ­k‚®©kå]³³N+WâVm-§óN²¶Ï¥ï0±0¹×/¸0k`ƒcÙ™^¶ðé+èš kT ýËMVÝ néLOÚß:[«k¦Kgb*ót-“*Ï‘ÏÐ5V¦kÆëfä7tljtͬň<^]Ð5Uíš‘ïkÆ»fÿKFëšm»¶Óv–K]Ð5Ø5ƒ‹4~1]€ŠJ/Cç8Á=ȉ«¥¾ñN‚v–ËÎc°â‡ø$]|¨º¦® Ôìš=O…3©SM-ãœziœ%÷¹º&@´kfÕšÎm~R«8Nd<ËtM]èÛ5EÌQ º¦® 蚢kº& kŠ® 蚀®)¢k‚®©kº¦èš€® èš¢kº&pÊßq '¯#Ð5@×@×@×]]]tMtMtMÐ5Ð5Ð5uMtMtMtM]]]]S×`¬® å]jÑ5Ð5Ð5@×@×@×]]]tMtMtMÐ5Ûë|R'¤î­Ö8”º÷ï™Ø<!uØôî<÷bõx1$u@êDæH] {k¿R?¾¾ÖXiJ]°ý¸<™NÈxËN !u¤®×£¯!ñE)£d™+[paœþc™ó ©¹ŒÔ•eoñ¿©[ü¿ñ˜Ì@˦ŸPH©—ºÌu¦0>"~ÿÚ…1¾Z:߀ÔÉÓ>S7Òƒ—‘¨;»»û§²§Í’:'ƒ:R7ØSw”Ôå\s¾©=uûIÝ£m&G„fʼY„Ô‘º)RŒ*O^|2ïïºÖ9߀ԉgê&KÝHÚø›M¼)¤ŽÔmÝS72ëo²g¯ëZç|R'/*uñ³"Í××Z!»I]æwqR'¤ŽÔ"uÍ ¥H:‘¾gê‚ùÜò³PfÚ7É–M0—f~°SïXM‘‹JÝý_Ž“ºx ßä›3Ã8{Ç¥“: u"Ó@¯V=uÏ$uÎ7 u"Œîà‰=E4²I8߀ԉˆˆF6©s¾©ÑÈ&uÎ7 u""¢‘ Rç|R'""kd£€3‡Ô©l=uÎ7 uòz3@n±µsNêX+Õȱ¬}v£ú™Rÿ½Ÿ5§hdo$u®ÏÉ·9ß:atÛ2©Û¢¨ƒåáu¢‘=]ê\Ÿóït¾ uBêþËß-›öÕ¿±¹ºÚÝ/Á‹oN¾ØÜQæýw/>Qr_AýÄ5¿þ¸Ç÷ox¬ö|‘šßl°ýÞ³‚Ô ©ÛZê\Ÿã Žó ©F·úßI‡Yk4ddcmk±$Äï7ûø¯Íã*KlJA Ë_M|tùb'wÍë„Ôm$u®ÏÉzp¾ uBêÞºnº½†æë ÉçFòí˜`w]ÇR¨Lµ7·–|x¦&u]§„:l$u¯|}&uH©“Ó5‚!7ùFÃãУ|£!¶t¸Ô5­Wêâ ’:!u¤îä×gR€Ô‘:9c£¡ë'á‘á=…‰à6•ºG— Þ™/Ò¦Ã/Iº—’º^ŸIRGêd´Ñ|¿0Öñ´R×|€­öL݈ÔøL©RwZ©{‘ë3©@êHôÍ®¶67cæßòDjÍÂÉÏëØ518–òì—I;Z+Þøì—™™ ’·økì‰RçúÜ5tÓù€Ô ©»Àòk¾ˆ“׳ï]4²7•:×ç›uê:R'SšãÍÙD´ìw3¢SUµï]HÝRçúœ¿à8ß:RwF©çRç¶!""Ù¤Îù¤NDDD#›Ô9߀ԉˆˆF6Üo@êDDD#›Ô‰ó ©sÛlRç|€KK›âf\:8ß@Oˆˆè9Áuz&¤NDDHH:RR¤NDD„ÔÔÀÙ¤îoo_çý‹wïy|óã{2›Z|%(Iü©Ç%oQ²–¾zg~ƒwŸ þimw_ï1¨ÿ®ÂÇoÞ¡-¸X‡NË®Óò±§wk•ÙûÁýO0!u uðrR×ÕÖl~6þ§dk/Óæn6²“>iÖgÚ»]uÒUÏ#­á‰›ÚY꜖™Ó2>g&J]—=6"¡C¤¤P”º|s3/™Ê4â3Ý.‹…ê)¥.ù©ÅN¤ÇȻד=Wgº—:-ƒ,ö 7{Þ»£›5¼Øºö)RGêp––ÄïBê€Ô¨õ¼è#­ç²uI]³$å¦ps¿kçÀãAm:ØÏi9Rϙﴹ‹µÄ5¼vž:R8I`?©ëzx©ÖzNv4­u$û²š­Éò3uqC9¹Á Ó,ó$Xíº|+¿ÐèÏ<ÂW5ê´ì}r²wÐétmNþ @êHH [ꂉ:vëé=Ø[Â)ãܺšÎs{ê2u5Þî*dYê.ÔS÷|§e<ÁI0ÏžRÇëHH`~OÝ™[ÏkFšyþ­ÖÉsˆÔm:ürg©›ÕSç´œ2©Éžøñ~ŒŽÔÔÎ(u#“‚ÔJÒ;›H¡µz”Ôm4ûåFR78QÊ&e}¦Ór ©KÙ%u¤ up ©k>Ö•Yñ¬Ù9Sxx)3˜­ù‘ø9¥ä¢[µ**L,I×ìëý~kvÚ앚%uNËYR×UoñbŒµg {{G…ÔÔ©[•ºSeÊÌø/X¶SÕÒóUøuOË3Ÿ«¤ŽÔÔ†¤nñá‘ÃCŠHHºk÷Ô‰ˆ©©RGêDD„ÔÔÀkK]ï*Ͻ3Räç,<2T^h«w•çæäõñ$kÏ2 ˆå'JIN128äáO ÍÝéÜÂw /,ï÷ñ|ð—{­¿ÜÁ¿»ÚsŒÉS=xÏã?i¯ƒÔÀ‰¤në¹ã“Óúõ6(óµ×V èZ5î¥ÔeÎÃÌ‹ù¯~í$_ë&u u@êöhfF7u5 Ë­º®¦aÙ¬vºÚ¾òò3(uåö®µÝÜx¾lÁf»õö—{Ý¿Üòšï] Ç7×Hl^HH\C꺞̩5 k«<ç¯ÉŸ™ËOæÄ­ä“KB7W…ŽKÛ»òu²bkûêíW©=4XÞiï(ÄLS>Ó(/û[s™{¹úËíõÉøüê ?+::ØUêâÇô÷ù½¿÷YŽÞNÄU~¢iü÷þL]m×S—ï“Ù¢dnOÝáRW8ÊRç/÷Ò¹ñÄ-ùYö”º»ÿÖ^©€õÔ¹i¸f¤™§hj=‡4 ~y©Ë÷Ô‘:¹Oð—[ÛõF_}ò𠧤žPêF¦¨•¤wN‚B‹ç¨¦áQ³_ŽÌïßõxÛ>=u];½„ÔeŽÈ_îÿr·“ºä`RRO%u͇C2Ëp5ûX OædC]ÏeãšXEå>´ÁuêòQ¦›™ˆr#©ëÚi—˜u5Ž›–õêÄbïVó0¹—øË-œ‡Í/4^ޝöÐcüµ×Aêà©;UY·ú Ê6^Ô ¿ôBvçù–ÏstþrŸï;íZÄœÔÔÀR·ød‹ˆˆH9¤¤ôÔ‰ˆÈ壽R¤NDDHH:R'""¤¤Hˆˆ©©R'""¤ u@êDD„ÔÔ©ÓàRR¤NDD„ÔÔ©RR¨IÝÛÛÛÚˆˆˆ::ÐS'""¤ u@êDD„ÔÔ©#u""Bê@ê€Ô‰ˆˆ:: u""Bê@ê¤NDDHH:RRב:6ÅͤôÔ‰ˆˆž:: u¤NDDHH:RR¤NDDH@ê€Ô>ooßø:½ìz}ñŸzwºi=L|Û±ßã×ßæ±¥=m]‰:: uZ«ç”™®RÕ¤înw'©‡|1v+ðÇ>¼´¼NHHº†ÔÅMÆ»}ìZ|eQ6îz?Þ¿áî#kŽl!Y’®ƒ­µû÷Eðúb§\SêjßHÜë8R·kŸíÚrჾÓ.}:miEHHºF'ÏbÛ4)3 ýægƒF×Þ×JR>ê¹R·vÔ‹¯…_s¿Ú7R{1Ó?o0SÚÚ§¶è©;°´¼NHHºovÐõJ݈’Í•º)ý“™šiöGÅGšy}MçšBÕ´¯AÓ›.uã¶\–ºÇ¯ò ¥%uBê@ê€Ô}sJWÕˆÔ%»ãv“ºÞzxœBc¼5¿‘ÔZ²6˜0Ó­¼33örM“ºJÛû©é=uG•–Ô ©©Rw|O]×Û®ØS·‘Ô=š[²iîHËÌÖƒ¿Ö}¤î¨Ò’:!u u@êö~¦n–ÔÅóp”ÛÓ;?S×|v.Ó¥¹³Ô5g@!u¤N„ÔÔÀñR·éì—kƒÐ’Íåæüù™ §tО©ËEæõ¼'¯÷Ë ¿|Ô¹ü@ÍüÌòÐ)Ÿê,Ú[;—V„ÔÔ©“+-n~þrgúöEHH:¹d›~­Ë‘~0:RR¤NDDHH€Ô‰ˆ©©Rg$[f†Œñ-wmgm’•Gëï'"¤¤H]{B¿'{riºÔÍ”ŸÔ‰©©€g–ºÚ’ÁZwÓÁ'—@h.Œ6eá Ìkï_[¨`íò+.ÄËÔtq­$ù»ÊW”ˆ:€ÔÀ¶R7²øxaaë[¸„Ý`Ùº¤.¹ÆÚ¢õe!ÿ©x_µî¾‰ëç?RîZRR(J݈M—º)½ˆƒR—_!º¹Á‚ e^¯=˜7WêòR:ØJêÊ`A/MR‡šoìN|œï$.IAbÇ f¤®ð©Ã¥.mKêD„ÔÔÀ³õÔå{çF:ÜÆ{êFŽ¢Öç–)Ï™{ê u""¤ u°­Ô{ —V ªbí㙪˯|P8x_‹‡pë\é!S˜®S+ßëuH! ZÕ<‘šgNóï´&{‹E}ükÊÔ§YpHHŽí© FæÇ6‡Yî uq×\< ”ÔáRW^|ü–^ú–[Øz­-XžqqÖ*ÞM©ëZ¶{í›¶…í,Šh~»®¯)ÞŒ—6_/,†ž¹š©ÞÝ 98(wä Ì—m°³n­ƒ³ë9:üI@ê.'uIAŠ' ™åcƒ[ȿҜû¤°ÇÎEø¥¥î)êZ·z¤Ën7©ßþâëqåäë9¹œzù@ ßoùõYR7¥0³ 9.uSÎÀ¹çeTs⟡::R·Ï,)͇èH©Ã®RWîkBFšF1Òew·ý|T²ü5©ƒ7¯]W/h³ÂO.uÁ Ù¤K4GáUÈ}¤®pLtQRGêRw-©kÎrùÞ[2óaƺe6Ò;Þ2Y’à=™3{$u¸dO]m^‡óôÔ%Ë|ˆÔÕD+ÿàÓ¸Ô­õÄN°áÙ­;qú¤¦ÏÀZ‘HhO€Ô½ˆÔí> Ø5Ö¿D9ý}½®ÔÆÇ½dÊÖì®Üù躖 k®R`úx!uÀ¥¥îí·ÜýëûßÞ‘é©[|sòÅæŽ2ï¿{ññˆ’ûjVH­“Ÿš»;RGê:¤nq àEèò¨`S™]ÏÁØ»jóø’ÖƒG×uÔñ:rÍ:!uÀ%¤îѦùžÀÖ¶™«÷GÅï7ûø¯ÍãZ<¨æÇ3ÕXøÔøî_t&uQOÝ\©kî¨×¯¥®¶Ó®¥Ï'ÝnR§§NDHðLR×tä3u[K]ò¹¾.M*¾· Ÿß©#u¹C¶–ºÇæµgd²“ŒÔ-–-Sà}Ž®6#Ká %u"BêRWs’Å!‘y©{ÚÕ'ŒÀì’ºf1Öú“ŸêýÖÖêÖEXOÝ1=uùеþ±Y=u…w8:R'"¤À™¥®«Ëndøea¢ÎZOÝÚ‹µc‘ºf9]„I©ò·ç“:ÏÔ‰©ž^ê’sŠˆÔ­u¯‘:œNêò^·6ûe0ídszÆ.KJÝÚö×vÚ|ó>G×uÔf¿R<™Ô%g¿\4d\y¢Ëfa‚$'~\ûìÈãsùQ Aá38XQ.¤n‹Õ68ÀÕ°ˆ:ÇJ]gëâ­ëu™UÃ[lÙE˜Ô±F'"BꀫJ]—<4gatç—ºÅͺ“:R\XêD\„Iˆˆˆö@êÄE¤îYÆ1Në8¸ªÞ›­}¼¼ÓY« ¹*¢=@»N\„IÝút­†õñë]öm7Û?–‘]ÔVe u"ÚH¸“ºÆmZüµÙðo“ãÇo¾½[Q-Ùú/+P\ŒÛÃÚn‹ÿ?¢ä®kŒk/®Ìü·¯ÇÐü“R¿/)¿˜ùÆ“G!"Ú©aœQêâ–kÐ:¿ÍXÑîÎËÖ%uM‹(ò-·æø¬ ®UErûµ]7_Œ—5ïí:[;´Â‹ƒG!"Ú©aœQêF¤hºÔMéE”º‰G—Y뼫 «VE½R7h¡™ªè•ºYEjv{æß)"ÚÀáR<âOãå¤®Ü ôlÄãôâ©>šæÖÕOõ˜ $‡H]³2UÔ]¹6ª°)]™¯òŠR&u"¤ÀøÉO~òåõäóÏ?W u‡õÔå{çF:ÜÆ{êšjúþ©³s¿ÌXS¯ÔMô·3÷ÔÕÆÖŠ©°5¿þõ¯?ùä“>|ôÑG¿úÕ¯TH][êFž©kö³KݦÏÔå­©yȽ&3>Zõ¥¤n‹gêH©pN~úÓŸ~úé§¿ùÍo¾óï|ñÅ*¤.%uåÙ/›ã “#0»ž@›8ûerüçÝDˆùiãå2V›c“Ã/{u1ÞE~ŠÎ®‡'wžý2>^R'BêÅw¿ûݯ\î+»S! uRW‹ö®ôž*ÎRküâ¿øè£~ùË_ÞþzæÇüóŸÿ\µ€ÔÍ—º ÷I¤<Ûˆ:øáøÕü(>|¸ýõŒ)Ÿ}ö™j©Û¶§NDD„Ô˜…iúR'""¤À“ØJ©#uÅ%¶\:q³µ>µQUŒ×aaxðà µÚó´ª: uÀ3HÝå¦ìy¿<çSÔ̵Çc¿¾®iâ—'w!uBê€ÔH]aIƒµyáÿoóÍ+ì°¤AfUƒäf&ÓWJH&YŸ™•‚’ÜZ ße¾»x¥`IÀfÞmdðýçêi—aÈ|Äl4Bê:८¼øø­´tðæ@cj³)6שk¾ðó…On£å ˑ[Hn-8ºä×ÔÜWáHϼ`zá{!uHðüR7"EÓ¥nJ/âFR×µäÚàAõJÝ …†0¢ôûH]¦äùbd^¼:Kêò!uHðÌRWîk¶¤3mýÞÙ,ºJ{·ýæÀ¶¤Ô5Ëœ9¨æèʵ1uMåÈTþ T¾èÌògQAÒž@êâÑΤNHRè©›ßS—ïépï©›ë#‚Ñ¥=[ ¿¬õ²Öºa{»IÝHo¹©@ê€ç—º‘gêšýlãR·é3uƒ¢Õ;¾ôºR7nï#R7>Ž·ëðÏüL©R€Ô¤nòì—Íщɘ]ϳMœý2ÒŒ'–Ìl6¹ý¼ÒäŸ%ë£%ÿÐZ¦„…‰Uƒ Ç¥îB³_Æß8©R€Ô¯.uVÓ)Lx#"¤©®'uA_–Èóyó\„Ô uÀ3÷Ô‰ˆˆ:¤ u""Bê€Ô¤nø9Àx¢”)V7[ûxáSUEs¦ÓÚîÆk5XØð ã9ÇËc©:¤ u£íÈK´){g±/ÏÀ¹Ã5#ûÝúËêZaÏïvàû ©R€ÔÏ/u…% ÖæX_œ&>~óÝ,ðû,i°Xæ®7'ª9ë}¾0Éú̬»P¨ù®éø›åé]ý"ØiïúoÉõ2ÇR[¨à$k$ô~"©Ã¦|ÿûß×Ü©H]÷x°Ââ`µµ¹ƒ¥áÊ#ÙšëÔ%ÛôåÌ>¹ýϬy]Û`l§· £÷VKfiøÁ¯)sva5ó‰!¢§N3p¶§“º)š.uSz7’º®AtƒÕ+uƒ÷çä‡G®•-ï‡ñólÍ/®°4yWQ•ºYßrí{$uBê4sg;pF©+w‚5[äI[hŽ6,—6ž÷"é-q±“£R“}Jñ ÖfÓ¼¹ëšÔe¾âdµ”È×j¡K¹&u‹å jæ„RWøI:Í\ÀÙ¼VO]¾wn¤Ãm¼§n|\_¡û1c½R7k`^oåÔÄin¿‘‘žºÞóó„=u5Í!uš¹€³8—Ô*Üàì—]•z’Ù/óß#©R§™ 8Û Hůärìk2I!uÐÌœíÀ)¤®9K¡©+!uÐÌœí u»öÔ‰ˆˆ:Í\ÀÙ:!uÐÌœí©Û~Ô\aì\ï”gæ:R’B½­-6x`…Ô¾ã-…ÔA3p¶W•º«Ïž×µ$Ã\©{ ˜rÇ*ÜÓœP>!uÐÌœíÀ©+,i°6%úãÿm¾ùnÒöM—4(”¿°N]¼êÃãtɾ d½Þ°xh™iñkØ,df™æ¡•güßS[ (0¯RÍ\ÀÙ I]yññ[uUë`™²Á²u­S7^þÌ‹ñë]‡Ü¬·µEêšohn¶i5ãKcÖ./Úà Pî­mR„ÔA3p¶ƒÔuKݱR”lÚ–»ìæ–?¹,xrÌ^°ë^©Ûâ ñ‹Éod#}Úôж–:kÖ ©ƒf.àl¦I]¹,èiÉ Y̼y°;1˜ccVùI]fPë¤n·R‘:R§™ 8Ûçé©«M4ržžºZïbYê]îŠR·gŸXíÐ&v5“:R§™ 8ÛIÝÈ3uÍ~¶q©Ûô™ºñò77•y}±ƒnÐFjoÈwg=±ÔeºjI©ÓÌœíÀ¹¤®<ûeù©³æP½}f¿ì*ÿÚªqÉY.ã׃S’³†Ž?Ùôæg° ¾ÖÚQ J]¾ƒ¯rúì—ϱà‡©ÓÌœíÀé¤î5—ØzŽu½eçïÈ—%Bê4sg;pm©[볺PÓülå' ×:yÔƒ©ÓÌœíÀóôÔ‰ˆˆ:Í\ÀÙ:!uÐÌœíÀ<©›;À¬k%ºòð¶ò„+'ÃyžmöÎr’Aªµ)v.4®rç³½÷/¢Y†‘Bnw€Opb:Í\ÀÙºã¥n¤‹SÒïãHÓ·?¾ÁY5@êÎ/u;œíµ ßß¾<òJê4sg;ð¢R´ÆÖæ|_\`-xçà¤öÁìóÁtóù­=B×îwý¸B¼µä*ù¥R’ñCºt’ ¬mamňcjíï«÷Ü8äl/øÛâÊ™ªÎô„—-óG½¶úˆ:Í\ÀÙ\FêšëŒåß™ßÔ”ƒfnæK[[5.³^yײiI£«•$P‚BñÆ»§»ªš+òxP±¡ðl¯ug­Uoïd¼÷lQk›µj¾SR§™ 8Ûgè©Ë¼!ÓÞíòÃq/Šw—ÜÚxiËÕX°Z“º‘’4_ßZê2{ÚƒÊì÷Tgû ¬N9䣤NO©ÓÌœíÀy¥înVƒÞ‘ãRojŠÔ­5à2‡ÐUÚ‚lÄÇÞ¬“µ½RW.Iðz¦xµ¥nîAm!u›žíã_Ó±R÷X9¤ŽÔiæÎvàE{êj/:¶“º®ä mέ±¤­•›Å[tjõŽÐ›Ò¸øSÅÙj©›x¶“:ªFê4sg;ðÒR×|ffS©‹Ÿ¡:§Ôž©ÛHê’Ÿ"u]C"ÏvPÓŸ©Ûúl?JêzçkÙBꣿößlùѬ=«ô¨¯lÖÊæs¿ßÞU NRÕR§™ 8ÛR7*u'iGn´ëä‚àÍõ÷¬¹ÅÓ~>ë|;#©R§™ 8ÛSK]ÐÍO­¿3^Ò #u™ý6K,b–Y™`qw]Eê]7,9ÿÚüûÍ»šÇ’YH ¿dÂF{Ï/­Q>sjþxP!#“…äËfN!uš¹€³x~©k.È›g~S™´7[ÞE~í¯ÝŠtë\,» u5ÕÌ/•VØÈàÞkÛÉMãAƒò×ÊP.[¦Æ¬¾-¤N3p¶OÒS—yC¦•ßå‡ÉE´ËblÚÆ½|å"uõJÕ^ov­”%YªÚ¡M<© gì©K*ëȇHž:!uš¹€³8©ÔÝMêlŒÆ¶ÓÕþ6ŒCoš?~k*ÜD©›«O]R·ƒV+ukºR8sg—ɸÙÎR·xæ“:!uš¹€³x¹žº-:U2݃µ7祮0¡âÙ¤.s\/(uã_ÓÄ™H:hæÎvàªR—é‹+8ÃóI]óÙ¹Ì3Z½Ñ[ªÚ3u嬅½wu#Ÿ_êzçkÙBêß—Ø<Þà›ÍÀ™Ž2~y×Ç;köËæ®Í~Iê4sg;ðTR÷R‹ ¿lQóóvøN/÷%>~$ŸäK¤š¹p¶¤î%¤®6µÃÓ‹Êå–ɾ\QúŸCuöY]Hf.àlHˆˆ©ÓÌœí©RÍ\ÀÙºk ì<6eXi~5êMñ*/€vàÁN<Nþ¬cs¤î_Ü”]ÔþN/7x›ÔiæÎv€ÔíÚ@¼b;)?ÇãRw¶'²¦ÌF¸µDDêfH‡WìáÏnº).Gê4sg;py©«Í ¬4p7?xrY‚xò÷Y3æõ–?¹xC°©®µï’ÓÖëFt-]_¥ ?ÑÿÚÄýÍ%ÅÖê¿Vcùom­ÍÞ¶‘)þ“KM4ϱ|ÿRóÀã% ÆÏÃÚ;ìbî®I4sg;°·Ô5[Û]Ë%ßz¾­/@\+[y¾Zù“‹t¯ÂÜ\$=^D;–ÛZmį~•]¥Še¯°f–=O~äëí0,Ÿ…óp|Ülï.Ü5©ƒf.àlö“ºc¥(##]vsËŸì¨YköºñcË;hJö¶ÅÇ7R(Iþ›*kF cñÉP“«q©›rÔƒR×uŒœ‡µÛFvqà®I4sg;°‡Ô•;Áš­½üÅ®Á–]ý<™[þ®!j™ñu]âô4R7]o’{/|×åù]Æ:yŽí#u#§Ð”n6•º­ŽÔA3p¶—é©«ÍqžžºZïâ¬á—O#u6u©j;k΂ç“:RGê4sUœí©›ùL]f…A©Ûô™ºñòמ;J>ÍU~–©KêºÊ–¦.3[IYo 5V›·fŠÔ5»yO.uùÂLÏçÜÛö”º)GGê ™ 8Û­¤®<ûe橳ÁQ‹›Î~ÙUþµ¡wk£ã’Oæ³êÌ~Ù%ÒÁq•g¿ŒÊ#cVË5®´…Þòõß5YkþñÔ¸¢âioÆÏÃéSPÎÚÅÜ]“:hæÎvà©;óêX—^wkŸòd–ü~Ê~Ö&õKî°ÁS-dw`=:hæÎv`s©[œGDDDÊ¡s¤N3p¶Wí©!uš¹€³ u""Bê ™ 8ÛR·ñ“<åqVãC³¦ŒòªM™X˜„&,Û ZÛhÜ#ñ’s´ìSÛ;?´f$©ÓÌœí©Û¯­yÅ&W×L’[KÝ[Ø_êv; š“pN¯óÓžá§š¼äuªÔA3p¶H]aIƒxŽõ»©À“²7׎›¸¤A¾ü™iã{׸_Ò ž"¿9¥~æ`ã××–(ˆ¿© þk¬ð•õ.]0þõ®C\™cbÁv8öw-¤N3p¶–ºòâã]ËpgZçY||¼ü½ …÷VBmÑçÅõ£k‹/Ư~•]¥šrî¯6>øõ®>å„)ØvÇ~à®…ÔiæÎvà’Rw¬åWL®uÙÍ-üèN³ ÝëÆñÒçÁ;óu;²‘BIòßTÙX2‹Åo'u#ßQ¯Ôu}q[l»c?p×Bê4sg;p1©+w‚5Žù!‹]ƒ-»úy‚U¤¦”¿k´[Í.ò-וºæFÆKµ§ÔTï¦R·EÁ¶;öw-¤N3p¶zê†&9OOÝôa{µñœW—ºG—;DêN2ü’Ô‘:RÍ\ÀÙl%u#ÏÔe&c”ºMŸ©/í¦¸ãEuI]WÙòÏÔ­uЕÍp°ÆÊ“Ölôp×ÖR×ì:/ØvÇ~à®…ÔiæÎvàªRWžý2óÔÙà¨ÅMg¿ì*ðŒÖâ±$ò &è™ý²K¤ƒã*Ï~·¹GƬ–k,žÙ²9Dvú4Œ]Ï”öÎí™é…Ο<[ûž³_’:R§™ 8Ûg–ºÓ.uõ%¼ö)OW‡Ï3Õð…ÎÀWðËoðT ÙXBê4sg;p%©[œGDDäÕÂÁHf.àlôÔ‰ˆˆ:Í\ÀÙ:!uÐÌœí©Æfd\ÖøP®)£ÂÎ9¢lÖ€·£¾5"¤N3p¶/*u—›nn¤Àg‡ÓVòÄ‚]ñ{!uÐÌœíÀRWXÒàqšõµiý›o¾››~Ó% â2gÞ–ÿlðâøº]Sðg&ñ_ü 2SÿOŸw~Ö.2Ë6$«¥ðSq©ƒf.àlv’ºòâãµ5ƒ7¯-V¶Ýâã½ =g>Û{¤µE¢×›nÖRó ò»Øn…èÌõŽü9ck)/b.Bê ™ 8ÛAêú¤nDЦKÝô¾ R—ì‰Z³”^¾…ë›ïÌW`~¹ç®ï¨`VåC”ºZœÖ:×"aš¹€³ØUêÊ`A×DïhÆ®Á–ùÒÆ R­õ% Ü©›%$©ëú¾‚/n¼§îBR<&u"aš¹€³x’žº|ïÜH‡ÛxOÝ”†þ`×QRWëY-ûÉÓHÝHõŠ:hæÎv`©y¦®ÙÏ6.u›>S·‘Ô<Ÿ©{&©ëíã:Ã3u¤N„ÔiæÎvàDRWžý²9œo|@㦳_fÆÑÅãE“‰<ŽÌ~Y°åxlí‹ÛböËBîγ_ÖjR„ÔA3p¶J%¼Î¿h[óƒ×ý:ò%wʉ:Í\ÀÙºåyDDdÓhî ©ƒf.àlÎØS'""Bê4sg;@êDD„ÔA3p¶¤nㇾjãèF¦p¬MsìH¿ÚPÃÞirD„ÔiæÎv€Ôí=ûÅÛè]“L’º‘ŦT©ˆ:Í\ÀÙ¼´Ô–4X›Ø}qÂúøÍwSÏoº¤A¡ü……ÝâUÞoìEÁ„ûÍ=.Virÿæ+ñìÿµ…â…ì‚Å D„ÔiæÎv॥®¼øømx1ëÛÊòÜå²u­Sw›º÷Ú‹ñë]R×µåÛúºÛ·ôŠç#ì|+/`("¤N3p¶¯%uÇJQrÀ^¹Ënnù“K««7_Ï¿8ò:©R§™ 8Ûgºr'XÐÏ–²˜yó`wb¼JØ”ò_Tê‚q§ùú Þ¶…Ô5w$"¤N3p¶zê&÷ÔÕ&9OO]­wq¼ßlÍ0ׯjŽìq°¯l|w…Ù/˳°ˆ©ÓÌÅ7 цR7òL]³Ÿm\ê6}¦n¼üÍMe^_”ºÅ’ž©#u"BêHnvqƒxz©+Ï~Y~êlñͽNÏ~ÙUþµuêòsQ¯çç,Ì~OûÙ;Ñes"Íòì—kÝ’ñŽDÄ=›ÔÔ‰¸Aºi+¹]1ç)ÿsXŠÎ4÷l7cRGêDÄ âR·Ögu!ñ8[ùŸI,"'âž RGêDÄ âJ=u"""îÙ¤ŽÔôCê=m|ÖÖö©—b7R'""âžMêHÝ•¤®Ki’Úó¸åÕ«¦gÛm™jºAº Çæm4²¼µòeÍéXn3¦–™~˜]ßÂôÅÄ_¼ÒDܳIHÝ£-¼屫-óJ,!_ÿÓ×ÿ»¸ÁG|ÿÁ‘R­õýÆ÷˜ÙòÚfãB.~jüXÜ HÝ„ËÙF-Èé›Ý¢œ#Ûœ"u‹%ɬ÷‘Û)Kx×6’_=B¥‘:qÏ©#uãR˜^¼©¦Ô»hJ]­ kÿ(åûÊl9¹Ùä§ÆÅ ‚Ôõ]΂Î÷ohN‚[YÑîý õÍ©óï¶³¶ëL«}qrÿ¸„që9_þÛÀêêù­eü$^„ _'·ô’ Ó+G¥‘:qÏ©#ug“ºäÇ3&3EêºÞ–—ºL=ì1_ín¤®"u·Ä2Ù‹ãߺFÙ5·è\~#oªoÐòžÛS×\¹î¶²:ysûqÉ{7¬•6ñŒqÏ&u u™á‘É!”µOÙÈ>R—ß{òS¤Î âŒR7«UÝÕzÎK`Ðb)aÞ7æJ]Wçá ŸÔ*ªë0÷‘º—­4÷lRR—é1›ÕOÕõñ)Ù­§®kš®QR籡ԽoÇ âÞÉ'z{ù¥®9E¹„õ]«‡“t:u}Ssý¤P9*mâ%âžMêð‚R7"KƒÕeA¤ŽÔáøžº®®Œ‘}¤®ìç~™?ÌÝüäüÃ/Ÿ¦ÒDܳIH]° \0JpqzÆÞq‰½o[›p¥·œûÊ ¹¤š©ÉÚ±¸A:R×xérR×üšöô“ä\5‡KÝ‹Tšˆ{6©Ã+Kœ|4¬¸A,uMqº'?ðøaó ½R—™r0(á­4ûer†ÃäÓ‰—\‹ÇÖÆCR39Æ{™R9*Ô‰{6H©RçAê^úr6²úÙÉËé›Ri"îÙ¤¤NÄ ‚ÔQ…ãm!³øµ¯I¥‰¸gƒÔ‘:7Rçr&""îÙ¤ŽÔ‰ˆ©qÏ&u¤NDÜ ^Yêæœ fÈ8ä ·/Ïù±õav} g˜óã<§ßF§îôÔ¾µñƒš^-ƒ#±ýaºg“:R'"nדºš'i¨m·Íý—4æÍoÝ”¶c~#ùÊyúÓo‘KNñúÄRw›ŒÇ¦{6©#ugzým‹­sÚÆZ©ži Êg:—¯m¥.øáùýî^Yœð½9¥ûÚîkÛYÛu¦Eµ8#|\¸e“/ÿmlIƒ|c«Ùv|ü*ãoví¸n‰…"2Õ•i;>Óél6Ù"O)®f?ÒôSz­Îó/v-ŸÉÒúÃ$u¤ŽÔ½xÿ™¤îTG:¾»§ñ:—¯½¥î–^7y±±›_<ÞNО®->Þûñ¦ú­¢¹=uͦóÝÿ&7Õ¬“ü§æVÎÓœ~y,©\}k±ÕäÏ®‘ì¼Àý«ýaºg“:Rw6Éyû-wÿúþÅ·wdzêßœ|±¹£Ìûï^|<¢ä¾šR«Æä§æîŽÔ‘ºiR7«ÅÓ»˜x²ý´fFJ˜o Εº®ÎÃÁ¶c­¢ºs\ê.zúe6›opçKØõ½otJ—ÿ :ZW˜¤ŽÔ‘º«Ý¢Ã4ßøÏÚ63c5cñˆßoöñ_›ÇµxPÍgª±ð©ñÝ=×¹|m"uïÛ(ͱO]ôv, ¶ªGZ“µ^”L=œ¤C ë›šÛv©œç8ýö‘º‚Xnô­m'ukרäipžº‹þaºg“:Rw ©Ë¼?óLÝÖR—|®¯K“ …ï­ÆÂ§ÆwGêHÝ&=u…&c­á²ÔuµÒÎ9ü²Ö­±iÛqîðË‹ž~;÷ÔTÑÃ/·è©›{Œþ0I©#u¤nÐI‡Dæ¥îqhhWŸX0³KêšÅXë?L~ª÷[[«[RGê^Wêò¿âŸ\êòÏvh;&' !u½£|3φM‘º¾µž©;›Ô½Ú¦{6©#u¤.ÓA—ìh*tæŸ1«õÔ­½X;Æ©Ë}¤ŽÔuôÄãß‚'Fš dÞÐÛ\ÎL”°6ûerö¹äãa—ÊÇÖ–'Lî¥Y9½ÏË]îôë»»8Cfóä, äüÖœý2YÔZiýa’:RGêžXê’sŠˆÔ­u¯‘:7ˆ½¥î¹3²2ÕÉËùšßŽÈÓ_ ܳAêHÝàì—‹¦‘ì‘+OtÙ,Lð‘äÄkŸy|.? 4(|¦+Ê ‚Ô‘ºoœ\62 ûjDv>ß^öÓ=›Ô‘º IÝ”>ÓÚÖýjܲ©qÏ&u¤îÔòМM„Ñ_½žé;rù"u""âž RGêDÜ HËÙŒq’µAYãã¸Fö¾é`Ô‘Ú˜rP;Ô[ù0·>:÷lRR'âAêv}ŽåŠMÛ®yÌ·–º§y†múl{ûõþ_ºˆ{6H©qƒ u}—³®™x²ìÇÿÛ|óíw§AŸÕGÔ\Ÿ YþÛÀ<ã·þ…§z§Å_ÛBWU¬}ñäò™ýf¦n¯TæèÙÅÜ]‹¸gƒÔ‘:7R׸œÅ­ÉX Þr ,[²a=«ü½‹M÷VBm™õüŠpå2,î"¹æò¬ƒªu—ìâÀ]‹¸gƒÔ‘:7R·z9;VŠnKQ÷þëÜòÇH5]¥×ãÇÏ‚wæë¶· IC‹«}ÖAÕlÙÅ»qÏ©#u"n¤nárVî ºwz‡,v ¶Ì—önûÁPÆrùóm÷`ˆfM¨®%uÍ›åƒï.ÛZê¶>:÷lR‡®ªp,.Dë©«M4ržžºZïâ¬á—Ï$u#Ã/I©©ÓS'âAê6¦®ÙÏ6.u›>S7^þÚóoq1ÆŸ©ÛZê Ï?S·Ãƒm{JÝ”£qÏ&u u"n¤ntöËÌSgƒ£Ën¶Î{Ë¿öLÝÚ¸ÍäÃTÁDˆ#³_v‰J¼¯E© ¿ ŠZ;¨š†í°‹¹»qÏ©#u"n¤nÃËÙÕÛ g+¹<ÍÎ=Ò}êíT ÙM/ ÷l:R'âä®9äùEî´åßNêÆ-bÿz;ÔmQR'îÙ u¤NÄ §è©qÏ&u¤NDÜ Hˆˆ¸gƒÔ‘:7º+ lÎÈ2^ÈíFNΙYøøÕ‹¸gƒÔ‘:7œZê®>­_ïzz#Gš\„}»bŸDt{7ÅåDܳIHˆ©K5Žƒyü_|ü¿Í7ß~w6ÿB‘ºÞÓ[þÇB.ní.f9ü[ÿTøÁB‚ñîòÌÔç`™k»˜»kqÏ&u¤NDÜ ®*uåÅÇo¥µ§ƒ7¯ÙH¾l½}VµeÄãÈÁ%Ë»­Î|<£—ÁkÝe#»8p×"âžMêHˆ¸A\Oêv“¢.Õ/äR×%~ɨ‹V–_»<_™åÅËÃeË<(uw-"îÙ¤î¤îíímí?DÄ âbRWî úÙzv ¶Ì—önûk»Ê?Wê‚}Å<­Ô”y»ž:R'âžMê §NÄ BO]¥§k°Ïê𞺌ˆ>>²µÏðKRGêDܳIHˆ©›öL]³Ÿm\ê6}¦®\þ`Ҕ̃Ÿ©—ºLÏêÜÛö”º)G'"îÙ¤ŽÔ‰ˆÄÙ¥®Ð¶ØO•ë˜yê¶ýì—ùòÇúÑ<êx_‹“4®=9ÖûÈbü &7XÓ°v1w×"âžMêHˆ¸A\[êj¹zãøiVÏ9¨*!¿‹é…áo"îÙ¤¤NÄ â ¥nññ0‘Œ+¬ˆ{6©#u"âñê=u"""îÙ¤ŽÔ‰ˆ©÷lºm«À±¸‘:!u uzêDÜ HË™ˆˆ¸g“:R'"n¤NDDÄ=›Ô‘:qƒ u""âž RGêDÜ @êDDÄ=›ÔÔm™òj@®;wS­Š´[ÅÞ¶\¹wmËw¯»|‘:!u uW’º÷ÍÙË-‹zWà‘òw}vnEmQíç)aí³;ŸŠ¤ŽÔ‰ˆ©©ÛOêâÆî£äÜu­Ü½òþÿ6ßüõöï¶0Ø4:Oò…÷µØÅ´øñæÛË<¾ëÚ™°öÝevºgÅöv©evíXîN~7ˆÍ¥îíímí?DDDH©{©‹G¾eÄc±u4yß¼¸Ù®‚4Ó“…ïúàc9ãíç}cú®Gzºb½<Þ-Ž®ÜwúèiÍ:=u""Bê@ê6—ºý½¨«}?Òe·µÔ-nddûƒ]gZWN9Þ­n7©ÓSGêDD„ÔÔm+u#ý`kC×F-v½ynaÖ†çÕ ßœä#ïùJX³Ó‰»ž+u‹eëúÒ7=ºÚŒ,IA%u¤NDDHHÝzê’].™žºr‡ÛÄv¾6J°°ÓYÃ/kýc³zêj_úÖGGêHˆˆ©#u¯þLÝnR·Å3u5¿Ê ©<´g’:ÏÔ‘:!u u;I]o‡X<3aòQº®Áuµvy­ÔM¥V›cpdGåE¥§|¼öÁä’qåeâm.N[b\(©#u€¸Æ^^êö~iDÊRÏ>Ÿ_¤¡\-ùÔÖB(c¢¹‘@RGê ÁpÝCêbIøzZöfOQa=ƒäDók“Ý/þK]f>ýæ§‚9î›Kðm±À]³»oíÛìZÖbí âòEê Áp=@êšË+÷.Çœïaë]oºÐ7øø¿Ér&k&ó©)Ç8kèiÆÌ ‹ƒ ©#uÐà¸Æ&u™ƒ·Å½=SÖƒ>Jêj¶ÓUÚC¤®ëû"u¤ŽÔAƒà{ŒÔ½ŸŸ#9Ø/p¹µÏ#ôzû¬‚™E6’º¸œ›J]áw–ºÌ—HêH©ƒÀ5vC©é©ËK]rއ_–˹Ô>ü²Ö³JêH©ƒÀ5öi¥nâ<W—ºà‰µ=¥®9 ©#u¤Ðàר'—º`½µæûãŸùÙ/“óIÖŒ±YÎÞ¹=Kïe£cL®S—Ÿý291©:R €kìVRw’hýŸ¿ö|G¤ŽÔAƒàKê8Ã%ëÍŠs¤ŽÔAƒàKêD„Ô‘:hp\cI©#u€¸Æ’:!u¤Ðà×XR'"¤ŽÔAƒàKêDHH48À5¤N„Ô‘:@ƒ\cIˆ:Rhp€k,©RGê Áp%u"¤ŽÔà‹e©{{{[û!u¤Ðàר³Kˆ:Rhp€k,©RGê Áp%u"¤¤à R'BêH Á®±¤NDH©48À5–Ô‰©#uÐà¸Æ’:RGê pÅ©:ÇâBDê ÁpRhp€k,€Ôà uÐà¸ÆH48À5@ê p:@ƒ\c¤×X u€¸ÆH Á®±R €k,€ÔAƒ\c¤Ðà×X©48À5@ê Áp:hp€k,€Ôà uÐà¸ÆH48®±@ê p:@ƒ\c¤×X©ƒ¸ÆH Á®±Rhp€k,€ÔAƒà ¤N@ƒ\c¤Ðà×X©ƒÀ5@ê ÁpRhp€k,€Ôà uÐà¸ÆH48À5@ê p:@ƒ\c¤×X u€¸ÆH Á®±R €k,€ÔAƒ\c]c€Ôà u€¸ÆH48®±R p:@ƒÀó_ 8¤¤ª¨¿ü±ˆˆÈ±!u u@êDD„Ô¤©!u©@êDDDHH:‘Õ¼=ÐûÙüëù7ˆ©©R'Ò-f—–:¢(Ïßü_ŸÿÇýO>ûÞ}üûßúòŽóíoýÞ?üûðïôÇõ³‘:: uBêÞòÿýع¼þø†Œ .þëãfK”çñx \(ÿßÿñ?ÿ·ï¾´¸Ÿý›?ùò¿¿|å—ñg?ÿwÿìOðéGßþ_üùHH:!u?¾óœ^Ù[üï¤%v)e²›1Þ£“kõÑ}itÿêŸïËÿxü×ÿüþå§øñÿö?ý#RR¤NûÞýìßüIüžŸ~þÿû?þ{¤¤Hx nò/wlN¯²ö$^лô42:¹z>þýo}õ]_þÅŸ}û[¿Gê@ê€Ô ©kOjÒìÙÛZê’¶Fêä©–(èy©©R'¯»H]æŸj¯ß+ãeJÒ~™™Ó÷.zêR¤NÄòq";åûÿÍßþOÿöŸÆïù?ÿ—ÿîOþÁß!u u@êDÈéòÅŸÿàOði¼æÁ§øñÿý¿þ RR¤NDDN—¿úÙ>þýoýçÿð/×Þð¯þù÷>ûÞýÎÃu¤¤Hˆˆœ'?ýüÿÝÿú¿úÒîûè¾4º?üä÷ß?tGê@ê€Ô‰ˆÈéòïôÇŸü­¿ùÅŸÿà+ûå_üÙú·ÿôÓ?üø³ïýÑÝ4*¤¤Hˆˆœ1ÿïÿþ?þé>ýèÛãË;η¿õ{òþÎÏÿÝ?[^Û€ÔÔ©‘‹†ÔÔ©“9³>n±µsÎ$Y+Õȱ¬}v£ú™Rÿ½Ÿ5k¨:: uò$Fwþ†þëHÝÎ߯RR¤NŽ—œ·ßr÷¯ï_|{G¦§hñÍÉ›;ʼÿîÅÇ#Jî+¨Ÿ¸&ã×÷øþ Õž/Ró› ¶ß{V:!u u@êäF·(‡Y“ºŒl¬m-–„øýñfÿµy\åc‰M)(aù«‰._ìä®y:: urv©Ë¼?óL×ÖR—|®,¯.Á¥P!™jon-ùp]MêºN RR¤Nž_ê‚!‘y©{š—ºxXéáR×<´^©‹7Hê„Ô¤©R×-u]]v#Ã/ En*u.¼3_¤M‡_’:!u u@êäÚR—œ(¥0Öñ´R×|€­öL݈ÔøL©RR¤N®4ûåÚÜŒ™.£òD—ÍÂÉÏëØ518–òì—I;Z+Þøì—™™c’·økRR¤N®´NÝÎ˯ù"N^Ͼw!u u@êäÍñæl"Zö»Ñ©ªÚ÷.¤¤Hˆˆ:RR¤NDDH@ê:R:¤NDD„ÔÔ©R: u""BêRà©.PœRRzêDDDO@ê:R:¤NDD„ÔÔ©RGê@ê€Ô‰ˆ©HR'""Bê@ê€Ô‰ˆˆ:: u""BêRÖºX| uôÔ‰ˆˆè©©R'""¤ŽÔÔ©R:¤NDD„Ô¤ÀsIÝÛÛ7jÿðØ|Y¶÷9¤;°¼å3"BêR€Ôm¨'—ºýË9kG;W,©RR—‘ºÅæû×/~õw½[‹/Æ›Z{óÝfƒ÷Üm<èpËô>-Ë”bܽù±Þ‚òTS¶›_Ù×ÿJêD„ÔÔÀóHÝ£“¬YJ0è1Þr`\‰Mé©›UŒÞ ªë¨=3Øé£ÖjwŠ©©€çé©”ºæ–“ž3.u™®ªZ1ÊÇu©3üRDH@ê\IêîÆövF+uµÞ¤ÌHÅ^©‹ÇXƽy‹Õ>kšä—EêD„Ô¤Àµ{êfuÓõ*S0ÃJ~ûåž«Ì6»$­KkÓ‡Ö 6ù 2:!u©@êV7•j?åu¬ÔåÝì5š·²Ã¥®kŒÃÈ%ŽÔ¤©ÛdêËæ(µxu©æÌx³ö›¬‡ ™÷ÏÂÔ—M#Zì(Øb‚Ä®ÝeVðKNyŒZ'Ù;&°9EgaœjaìåȤ¦k¥'sa%Æ®?äÌ\Ž'̽¬M}ª\3ù ݬ?ðÁÓƒÔÔÀ|©“c×U;Ézn;ã5¸œØ¯vV¿àõäB&u u@ꞤYsž–ß¤Ž 8«Uûë„ÔÔ©R:¤NDD„Ô¤©!u u@êDD„Ô‘:: u""BêR€Ô‰ˆˆ::8ã €3@ê@êÀ®üä'?ùòÿù矫 `kHH˜Ì¯ýëO>ùäÇ}ôѯ~õ+::p%~úÓŸ~úé§¿ùÍo¾óï|ñÅ* u uàJ|÷»ßýÊå¾²;::p~ñ‹_|ôÑG¿üå/o=óã?þùÏ®ZRR®ÁøÃ¯æGùðáÃí¯gLùì³ÏT @ê@êÀeîì]³± u uÀ- u€+>p‹HàŠÜâRW|à€ÔÁ¸Å¤pÅnñ©\ñp‹@êàŠÜâ:¸â€­ùðáƒJHHRR:€Ô¤ u©©@ê@êR:€ÔÔ u u©HâÇ* u uÀ-©ƒ+>p‹HàŠÜâR¸âà€ÔÁ¸Å upÅnñ©\ñ€[<@êàŠÜâ:¸â·x€Ô®ø`ˆ>¨€ÔÔx:©û&äp•¤î¤RwûË‹ˆÄ!u€Ô‘:!u¤ŽÔ‰© u¤NDH©“=òö»>›=ص/‚Ô¹ÊRGê¤.u]26EêîÞÃëH«< u¤NfJ] {k{¯¯™[`}ï?tè5‹ªÔ:R÷ŠRW–½ŒYå¥np×¼ŽÔºøðáƒJH©{†gêFzð2‚—ŸÙÛ#—ï`RˆÚlH©ÓSW–ºæPORGê¤ u¤Næ–Ø”‚–¿šøèòÅNîú•½ŽÔRGê®!u™÷gžéÚZê’Ï•åÕ%Ø]×±*$SíÍ­%®«I]×)AêH©{~© †Dæ¥îqhh^êâa¥‡K]óÐz¥.Þ ©#u¤ŽÔuK]W—ÝÈðËÂD‘›JÝ£ËïÌiÓá—¤ŽÔRGê®-uɉR cO+uÍØjÏÔHÝÏÔ‘:R‡óáÕ:R—çpmnÆL—Qy¢Ëfa‚äçuìŽKyöˤ­o|öËÌÌ1ɉ[Ì~ ػ̀ԑº-–5{Íu¨÷ÿ"ÎVϤ@êRGê.ÐoÎ&Âèv3¢SUõk~ï@êRGêD„ÔH@êHˆ: uH©R¤©R u©#u"Bê¤ u¤NDH:¯(uÁUäÇ* uë¿úè…=u¤NDHßq8ùø#R'"¤pÇ‘ ·jHˆ:ÀGDDH[¬ˆ::Rç+"¤î8"""¤ND„ÔÁGNš···æ+…m.nv£-w}ök¦ì·¹©)G-BêÜbE„ÔÔ‰tXGABî6ò(N5•ÚBêjÛ9•ÔE!un±"Bê@êD~ ØÚ¿.àc÷WFêÖ^¹û§ø=ÍùúÍÔ¸Ö¹÷øúZÆ6×çâÛšåY;|Rç+"¤î8òÌÃ/ï´!0¨øwÛ v±öbìuñëÁ›×J^½æ~›R—TÊd7c¼5F'¤Î-VDHÜq„Ôý8ßɶ¸Í.›(W™î¬Ê¼pk©s† ©«Üb/ˆ¾u!u urf©[t’æàÃA©‹{Ò ÿÝ´§n;©ë=.R'¤Î-VDHÜqäE¥.ó]—ÔÕìeü¿oáô-;ü÷D©Ë÷@’:!un±"BêàŽ#¤.kJ™W”º5§M¾[©Rç+"¤pÇ‘=¤n­ïnqÅ` “ø©°æ¨Â®a–]ÃãÇê’“m6__Ûi<‰å­5Ýe<L³0"¤Î-VDHÜqD¤aÈDêj=¢™ 'uBê@êä(¯kNiø8ÃGÜeTžè²Y˜à#ùy»†#Çr«Î~™´£µâÏ~™™9&9q‹¿&!u£ÏÔ5ûÙÆ¥nÓgêºÊŸt•¦ºÓ™|©3Q ©©“×”º)Ô¸ßç‹8[=ûÞ…Ô õ†=Nlx[ŸR²933÷Foã÷ÔÊŸ8º6Õd0d²Wp± µ ¿ Ì~Ù5—fr8®ž:RR't"…Ì&¢e¿›ªª}ïBê&þ9}ãâ—³o¼ìÞoÕuꞸB„ÔÔ‰ˆ©{‰[ìÕW;OùÏP‡×ƒEêHHˆˆ©s‹RwR'"BêàŽ#""¤Î-öy&þÆ{g‹¹âàÏ=qâw:RÈý¿ÄÁ=’:¸ãˆˆ©s‹½oV^îq©)ÓýOyÂm£]øìvŒ…x…&u u"""7K4%çqrüÇØ‚)ûçÖo.×Õf×3Hþ–XÜ<øì-\œ-¨‡µgê­ùee*g±¨×:Æ^‹<¨µª+w©­uf6k~Öâ¤Sî8œW‘ºòâã™6}ò¿˰éÊãµÂÇeÈo*^/®¶E-¯J×,êÉq| hPþZÊeËÔXPrR=u""¢§îÉ¥n/êÒŒ‘.»}¤®¶©æòÜqÍä+yŠÔ]ñkcAãuç˰›Ôé©©y©é[¾¸6j±ëÍs ³6L®VøÝ„g­)Ÿ¯äBmï,u›cí ´óHÝcå::R7­§.Ùó“ìÙ¹§n°ð O­;´ü|×Õ‘Ô‘::ÏÔµÿuk©Û♺šödÚ÷™fwòy³ÅcÜSê‚n½«ãQR×;_ËRç™::‘—“ºÞ±`Lcò ¥Ì€Àf¿¬>?Ž1𙜲Yó‹“ækûÖšý2V¦«còLè5ö¸ #½£™Ì~ R'""Bê|µ§Èá ÷mºÖöIŽñTu{­Ú upÇRç+ž  nJñ^Jêj‹Ô‘::Rç+"¤î8"""¤ND„ÔÁGDDH[ì댯›8šnú3»Ûn emz›—}úŽÔÔ‰ˆˆº³ßb3Ó-^åÉ·ÞuÀzççÜ¡~Êk:—?`äŽóöö¶ö"""¤n¦ÔÕ¦V–:¸›`=~óíwgÀ1„¤Et•ÿ–žˆ?8ºäÌþñ þµT¨Ï^¹j®R°VþÛú<û‹¯/–9y°{vx’:@OˆˆºsÝb{×øÎ¯\a,(Æë—ʱ—¥®¹ðôbåôö4®is›ÍeÜâ×Ëõ&¤¤NDDäE¥î)J6Öõí&u]C3rr×)W+glqe©Ë íZ¿»wû¤ŽÔÔ‰ˆˆºÑN° _(²¸8X®k°e¾´wÛOöÿäËQrÀê\©ËàŠR ”íê³RR'""Z5zêºûO’=uå·ízê’eÞhøå`O]ÒâæJÝšEOì©Ë|¹¼ŽÔÔ‰ˆˆx¦®ï™ºf?Û¸ÔmúL]¹ü½ÎÖûßSF®n$u#Ïfž©#u¤¤NDD„Ôo±…Ù/›C“#0kmôñÙ/»Ê¯S—½™™r³ì…á]Õu{˜¬%¹µÂì—Ía«¦¾$u u"""¤nZ®Þ’~)(¯S§+Ìå(Üq8¤.ôxݾ‘«—üØ9°:è©­=u"Bêw!un±"Bê@êDDDH[ìnÃ5'Ž?ÜyhsGSJrªq­ùbÔ¦äÙî0÷ßòÝë¤î8""BêÜb—{+¹Ø@¹E¾såŒÌzÚƒzb©Û¿I¼ãÌ]‰ÔÃÛƒ•søJ""¤nè[XÒ ˜VþnÂúæôïß¹…c” k­µP˜p?¹ªÛâ¿..X®ÌÌz§=¨|Caí<\+ç­´C\c·ÎźZ?ÁAÅKŒ¬-OêpŸGÛ<Õ/ƒçÿ1èØ•DDH]ñ[^|<¾{õ.Ÿ½¶FY¾`#Íßdá3o+¬žôŸÇ-d*3£Áòzç<¨®†B\!ÍrfÖL/×Ϭ†Îš0¯cíXH6½ãÄ—åÅßn2¿P$ßÜü­*ÿ‹Lïv£bܽ¹¹PêăÚîG%R7í»©%›¼Ì™+uMÓK¾áñ–ÿØ4Ï—3ù{væm§=¨^©Ëü‚Þ|ïëW‘:=u8Ôçjò·ªÞŸºšï©)ǦÅ(ÿ¢”?¨T!u}·Ø‘~°µQdÍŸÌ›çfíçÉZáãɇªnä?™ôJÝùêäR·xî5OÅÁ!I]ÂLêpÚžºA©›õË˸Ô5{ËÅ(× ¥NOˆºÃzê’·‡LOÝø£ðãì*ü¦Ã/;µzŸ<)7ÎpPS¤nÍÿ'öÔõö“:<¥ÔÅ¿µ\ê‡çJëº^um0ù3ÓI®?""¤n§gêv“º-ž©—ºÚX—|…Œ[-HÝø3u;Ô”gê’Ïfž©»´Ôy¦zê2ÈåΫé=u#<ÿJ""¤®(u½×ýø‘ëdó½k†ÉÚý&Å®ÂÇëÔåŸPOÎ|XЧre6çx<óAå%‹OíÇŸ*Ì~ÙžšŸú²ÜÒJN«Ðœ¨€ÔáºR72L±ð4õR7å'¼«HgêD„ÔM–:yâ¼ø-óЇl™IN(uùAÉ5WòÓlÎúýe¤µ‡™ó?3çG%RGê„×=ñ^`R‡óßqfÓ>Û…â Å`Y"BêHˆ<ùå8Égp„$©ct"¢UCêD„Ôî8""BêÜb/5".~¡÷WÏ)‹ ûö:ÀGDDHÝ“ßb3“•]åѦò±Ìš[lŠN)¹:`âgŸKÍ ˜ÛaìhsY òä“åÉ«O5æöüTÝÜú™þ,ënßì¬Bžg¾Ö|1ö™wÓïbðô°¤Aô¯™éìïæ¶jÎ'öþ#gaòLí*ÿ­Z¶Ú„øñŒgùÆDþõ xfš&uÀU¤®¹‘sþ0w¬ÔMicu­i>«U½ÛîöÿùxúÑPùÑyn“òZMêöï“°øøê¿æõ¦wÉéµéžËCó ‹×ÊŸy1ÿz×Ù³Nô£~mR‡”ºÌ59ùÃVï:çüaî± +¾6_oÞ[[Œ=¤kÙ†äo²ÓwwÈ'ÏcUǧeüžÁ£ ¾Ü®¯µùwüCó¬3ÆtÎü. wü©.VW²æ{ Ä H^âšk¢t­³óéñ*Rw¬%G°”_™[þB+¡ëuR'¤¯,u…kàÜßàùa®)Zã…ÌK]áÅG{ìý ·pÛtwùS.îd‹?ToùèÖ¶ßõµfx N†‰]âƒí“äßH¹š”ºü…¢ÜyW{³œ™«J¹~¶ë|ëÚõóK]¹,83?neÞ<Øx·ýü¥6_þ¤.ºµÔ ^Ž…ÔûH]þ³kØSý0×¼¶Ï-äãïúƒwºØCº 9.uSvWø!þ¿½:Ñ+uãçs¾®2'à ¥®ëâÓ[†à[+7GžkÝîGê^±§®6ÑÈyzêjs›ÞË'Bê€Â'ùC[×Å9þ .ù#Ý~˜;PꦌdiZVó7ܹR7¾»YRW> ©üõ¹ë·ìB/Í©~tŽU¤y’dj¦,u½g“ºÌ….Ó³Ûéᙺö3uÍÓq\ê6}¦n¼üÍM•‡î:!uÐS7>üòü?Ìí#uqss\ê¦ôÕ²Ów7½§nJõNùõ¹ë(œ§jŸ”ÿ z/D#=u³&+ÊxûÚxì‘«Jí{Üÿô0ûe{öËòSg½¿‚L™ªh°ük³?çŸ4ízȾù4jùIÓµ¿íxGBê€'–º˜Ë?ç6RȳIÝø“WÛí.n,^QêÆÕ"_Ú-ž©—ºñgêâïb| ‰¹3Ðv=ØuU!u׺Zži*á+›t ©ÃÓH]aRµÌÌ3ÿ0wKÌ~9¥IC¸ÍXY§9Xk»9»v—YÄ/ø 2Ÿ •ìØ;#e~b×ðË‘yM ?:'¿Çò$3cÁ óÃ/k³_ÆŸ*_âòºYàSú$H]{ŠØëÑ,Âf9!uxµŸŸ&G]½/·hò¦»s}ŽÌ÷xÅع̤NDHàŽó$í°ó´üÎ uLÀY­Ú_§À¤NDHàŽ#""OÛª!u¯õHm¸faÙ“YO‹^È­a»Ÿy Ô!upÇRçÛñ@ö%º†'Nå´‘ƒm*u‡÷¼ŸÄ²H@êDD„Ôý[XÒ ˜åæn‚šæ”8ïßY(O¯ ä ëŸývpδfQ׎´°žÄF…,øXPŒñ߉}ãJ[›DŽÔ¤NDDH݉n±åÅÇcyè]µcmÊÔ|ÁF÷½…ϼX[ݨ°p_f¦éÝ 98ü²kdf¹ÇoQh“åò:!uçºÅèE·ô2#µ.»‰…ï]ª¥÷õq©kîw·B>¥Ôé©Hˆˆº3ÞbGúÁ ëÀæß<·0Á*®µÂoäKÁ2—瑺L!¥.Ô·êô*Éú!u©R÷´=uÉîš®¹Fvë©›Uøí:Áz‹zHOÝÄN³Bß©#u u"""¤nΣk[KÝÏÔM—º®ÇÒ2«Õ¤î<…|J©óL@êDD„Ô]à›o²ÇS v;X{’ê60ûe¡ðÍaœs'–\›„³© ç)df¼èݦÊG=صköKR‡î8oookÿ!""Bêünúry)µP¤zêDDDH[,™Q BêàŽ#""Z5¤ND\þw!un±"Bê@êDDDHÝ)n±Á*pGÊÕ¦ܧl"¤ u""BêÎ{‹-Ì<9ÝŽ’3éR6R:!ug¹Å.®%ð8É{sùÇ7¬MŽ_XQ`Äß¶(›©HˆˆºóJÝ­géíµ· ¾›ZÛþÄ…­ÇË&Bê€Ì€3@êÿy[—¿M‘º­Ë&BꞀËKÝÝl"I :ÁN%u›–M„Ôºã¥n¼§ŽÔ‰:RGêOµDêL”"¤€Ô]Iꟻ3œÅ‰" â´8åÈì—;”M„Ôº³HÝ©Ò%u–M„Ô:R×^îðA©R@êHˆ:RGêD„Ô:­U!u¤NDH©#u"BêH©Rð*R\å©:©: uR u@ꤰÿ?Î[-vrµ 4IEND®B`‚jbosscache-core-2.2.2.GA/src/main/docbook/images/Interceptor.png0000644000175000017500000003115410660354341024405 0ustar twernertwerner‰PNG  IHDRýŒn8ö&23IDATxÚíÝÝ­ì¸Ñ…á†Cp†1àLˆ¯œ‚#10·¾2@l|óìÝ"«ŠÔÿSXÌéÝ’(v÷+ЬZúÑ„B¼)~è!„À}!„¸/„÷…BÜžû¿!„¸aLqÿU×½·¯Êp÷…¸¯³„÷u–B¼ˆû?þß_¼oý~F¸/„ÀýÏܾxðUaæ(¸/„Àý"÷ÿ÷??ßüø¿ØºcØu“þV¸/„ÀýÜÿr ø²ÉÇ¿~|Cp“þQ:Â}!î/ï÷_ü2ÿ²ÃÜïl’âþæ™ã¾÷÷ã~‡‘EcÜBÜÇ}!„¸ ÷ÛÆTOëýEÚï3?ã ¹ÿ믿þv¾ÿú׿|i„¸?æþÁ±¼ ¿íð/ùËßþö7ß!î·ƒñ'qÿ·½ýáøóŸÿüïÿÛ7FûÏ_ýõOúÓ?ÿùO_!î¿‚ûfx„¸ÿ"îÿýï7Ã#„Àýÿßø a†Gû/ïKÜBàþ»¸/„¸ûBûB!p_!î !„À}!„¸/„÷…Bà¾BÜBqiîÿ"„7ÜÏ·à%"º©p÷‰÷q÷‰÷q÷‰÷qŸÞ®ÿ ý@¸û„ûÇ]\{÷qŸNàþÖÿü åÿüßÿ|yCd“áQ:"ÜÇ}ܧ5Üߺô‘½5lïoÒ?ÊÇˉϋp÷iýx¿ÿâGp÷¹¿µI„ûD„û¸OÇA‹òè|èîã>]šûAÐ÷lSGÁ}Â}ܧ£¹ß™çÎêt6‰Üô— ˆp÷I­á>îã>á>á>îã>á>îá>îá>îá>îá>îS7›eí~.•“mÌ{ä&á>îÓÑŒ¸5÷¯pjóG~ÜÇ} âcÁê°luk¼1ÆiÛn9ÉÕ÷RÞzOëZrÆÏ"â´ß&©­p÷qŸBÐÿȾá{ú[}$QOýg­¦˜úY{îŠlÒ?Jê@Ðû¸OuîGÞŸE|p?‘ׇãÜ7#w-ýûGߺmm’âþ°sp÷qŸ.Êý8³‚Ãç ÷S·‘³¨×*îGŽN¸ûTçþüH¹0\Åýaû‡“?„û¸O¡uÝÓåýIóòn¿ ÷GV8;»*L²g™ú'Øé«øçùà÷qŸfaBùʉ¡> ÜÇ}ZÈþ@^OªŸ ÜÇ}"Â}ÜÇ}"Â}ÜÇ}"Â}Ü'"Â}Ü'"Â}Ü'"Âý{s_!n¸o¼ODÆû¸ûD„û¸ûD„û¸OD„û¸OIýøñÇû¶ü¾'ÜÇý5ÜïP`'@Ìï6»‡ÉsüþžþVÇ€µp”ß7~Âý—r¿3ôûþ§ÿ½òó‹_nûe«/›|ßÛpÁ–¤N6Îý­}oÞ°mK6n…û„û/å~‚ýW>²µƒ˜!à²{è·dæÄ#ÜïŸòÇ¿nµ?²Iÿ(ñA?áþ[¸¿|)ÕþTã~mŽûã˜úg¥ÿe'Aþö7Iq¿s Ü'Ü7Þ_3Þϲþ`îï1ÞOõCûK®²¸O¸ûuÎs?{yè kÜßu~÷‰pÿù<ñ…Äïð}‹Â©c•ç²'óy"÷1ýyžHß~Ÿù)t‚u]Â}ܧו à>á>îÓëÊÄÔmîã>îã>îãþ{fQ–ÛHÔ–Ž_8ÕÙ¡¹)ÜÇý+úóô3Iök@ÁšáÃ.75ü9¬y¸û¸!žï-»Í¬J®Oµöçö¤. “MåãÌ´¹‹áO|„^ËÁÝJ`%ÜÇý3ëu eD‡™ç¤ZÛyOçÍ‘Ô]Ë Rç/= –¿îãþ¾þ<‡o™0¨©Ñ¿cžSní÷ À°Zjî_¼ˆáOíSãÜ'Ü?m¼ŸµÈÎ{¬ï—[Û™ð¹÷³ ;?à>áþÃç÷‡˜áþòùý™Öö1wÀ<îÇç—÷q¯|ž­éŽˆ?OÔìaž“jmÜô&x²KV Ú ²W»`S¿_†q÷q_þþ‹òÙ¯ßHP&ÜÇ}èÿã{Zú„û¸OD„û¸OD„û¸ñ©%.=ñ˜-!Âýûùó´»Yô´C\z‚û¹µ?î?ÜŸçv=çºôÄÓû=se"ܸ?OÊÇ&[‰³‡EOÍxg¡KÏüyˆpÿiþ<ý¦_Тg¦ÁË]zÞàÏC„ûïï/)a=r¼yÛB—ž7ø4áþÓæ÷ûó«¬ ÎïÏ7x¡Kîáþ-óy>:¥\Ö¢§Ðà\zjëº3Ù÷ç!Âý‡çïßîÇJƒ/>FFpÂ}Ü• Ý(ã ¾rwá>á>îá>îá>î?Xµ©¡ýÞ¼÷ÍÞç;\I^nˆ\í÷UÇ}Ü_æÏs/sžUÐ9ÞyíÜ‹GÙn¨_í|”q÷åïWüyú¥³íbæ<ÃÖ‹Èúý°eY3¬ï튗±V4·äè ¹¬‹<¯‘Ïî«×-Öë¶mË—`&û‘æ<ÙÖf“2ûF7Ù,þ…Gœó.9ú×µy˜xÅYí³&ÜçÏÓv]Öœg¦µÃñxC‘SÞÔŽXžX[xôÂ× ÞþÔ8÷qßx¥;Û“†µãý™ÖÆïR5ºåªÝò Üßãèó}‚û„û§Íï׈ÐÎ0ç)@'x²Ïàþç{Mî›ßÇ}ÜÏq3¸ÒYsÛÛœ§ÐÚÖ}NKpM¸`n³ëƒ«¸{=rú3W‚ %Ñ÷Ñîã>îk]D¸û„k:‡p÷‰ˆp÷‰ˆpÿæÜ?Ø`?ß‚²—@Íàa˜8¿Ê‘&ò˜_s&„û¸¿Éýа+s#‰žñê³yîgOvÆ‘&ž&kbp÷£Üïx°²û,Þ:ú“Ÿx_äŸ)«™TÅo0¡p>¯?òaE:¿c¦D„û/åþw¬ Éÿðq¸©TëŸßüeÃaéf°¶³V*5ïÒSû°ZÒ)ô ÷_áÏóÑÖ¦ ô~IdÖ˜%[ƒÚç~ö‘èK¸騅Üï÷aÊr‡÷_=ÏSæ~+ùóìÄýì,ù<÷ºr.áþªep"ÜÇý„)ÊÜoö2ÙásùDfro‚~¸O¸û˸ßJ¦(‘·MRr÷àÊsd]·•\}Vy“œ#=¹€Ὰ-Eù·éÝK¸û¿¼y/‡¾¤LÂ}ÜçÓ@D¸ûDD¸û4S¼º‡;ÅÖ£ƒkG©µ0uèÉù%ÓS„ûÇqòqW‘\ÙßüäƒÂg\~¾ãò¶Ío8“-zŒçîîÁýÔSm; ‹ßËýw¶  µØM5¸“ üçÇ ƒo‹´§m[ñ û¶…{âÜ6¯UÒY8GÂ}Üïq? ÙB‰VªªÜ°T}Ö°D ö°õ¶Ïƒ×·u)€(;öÌXF¯ò;*Ÿ#á>î÷üyfÀZs¿ šY¦ñjf5Ã;€àVµtªØ8rî©O­ÆýÂÕ1ÒÂxçîãþAãý”—@¤$uïñþ’}–õ-ãí³ê&¦ì]q.÷SµÜ¸û¸¿Ëü~vª¡Àýµóû…/ä~›óò<‹ûýÕÜ'Ü]>ÏGƒš ?Oduy>O¶ÁçFœ«û;éwHÇäghŠ÷q19ëØ33+Õ¶9Y^×Xá>îãþùû·û™áÂd¿é@Âý7r¿V@t… ,ÌšïFý@¸¯^—ˆ÷qŸˆ÷qÿvœy–/Š,lÒ•Í!–φÍôîãþqþ<÷2çá̳êãÃýàŒû¸š?O»•9ϰµ3O챋ÁcíÔ¤TÏ´3L²5Ø©|hI ¸r½n¶”æ\sž`qgž`¶þ)M fn 4lðdîãþ¡þ<-ð<ñë˜ód[ñ]x­3Oùã^ؤcŠ„çMâ N ’p÷ÏïgçÄÏ5ç)·3OKº.Ó¤»˜Cà>î?m~¿ü‹=Þœ§6»Ò8ó\‰ûÁéì÷pßü>îÏS[% ®[.Ïçɶ–3Oj…i½UMН…L®ë–MÊ‹½ñgÜÇ}ùû$_P§á>îã>¡˜îÂ}ÜÇ}"Â}Ü'"Â}Üw«¾Îª%¾+óD¸cžv7‹ž%ÉÑ‘žr˹*¸öîËßϹ\ߢ§æÒÉì熦²à·:¤Ð‡K6‰ûÒáþóýy æ*çZôÌ»Á”Ýâcí‚ÏÌÐL-hªùÔŸpÿuþ<ýr§ ZôL68XP³–ûÁj¯xù[Á'[cL„û/ï/±j8r¼ŸmdÖ„g'î—fº÷ ÷ÍïçŒ\®cÑ3Ùàø÷‰pÿ±ù<[.%×´è)4xk¾¨¿Û¹ZÎ÷a‹=Ñ%nF$á‡p_þþ])pdƒ/>FFpÂ}ÜÏÕ"Ý(¯ãÄ_¹—pŸp÷‰ˆp÷‰ˆp÷Ÿ­…Î<{OªÜw¢fyËËO¼"ÜÇýeþ<÷2çÙÏ™gy'Äßvq?Ÿš‡û¸û»øóôKgÛÅÌy†­m«y:¢ñ½ó>ïåç¡§‚xß¼Âý›Õën%,^Óœ'ÛÚBéÖd¶~m¼S?ŸáiN>÷q÷—ùó|—]Öœg¦µñò¥÷S{[Uß{A?ŸøÄ]jû¸ûëÇûYÛ€sÍyfZÛGXü‡„ªëO¸§ùýò¼Çñæ<ó³4“ÎñÌË`S¿p÷q_þ> „ (îã>J¾èŒ@Ÿp÷‰ˆp÷‰ˆpÿ,îïä-S˸XâaPKZèİ·oDª‡ç[¾°7‚«Ü&‹p÷g¹°ÓKû±~&‡²ci°ª óI«‡Q2[óœÚÉÚ+“ îãþ¸H5’'þ%+±uíYúY•ö€­Í0¹°“ÇO%lß¶íQÓFN8å{”aÌwìxé ïâ` ¶yذþu7eE¸ûik”-Ž´íG™§n䃀.¼­l“ðó™na:ò<÷-¤¬gê¤úµ µ Øì€:U²ÐoXª&n¦Ÿ ÷ŸãϳU©”åoªŒhø§ ÛOûCÐÏs?Ø™Y;¼þ ;^J¶õ¶BÝï°¶6ÞælaZ§ ,5Í…þ¸o¼ÿ×øßV˜,–}&mÑ:Ü϶«[â<ŠÂý…ãýÔ*ˆñ>îãþ÷·n±/ÂýÂtÐð=©˜ì´C@óså{p?x¬ˆ»röb¼œûˆû¸?€Qßüdˆ¹È²j ûm‚†ëºñ SãLjOÑ-iqS8Ç {vö.ªü†àÂòÚ¥{Â}ùû¯³+Ø ͥР߸û¸¿¦” ¡Ré¼û¤÷qŸˆ÷qŸˆ÷q?;c°Ó<ÉÞž<3­5/A„ûwòçYBÀxòX„$ÂýççïOì߃¾:톞<© ÂxÁ*áþ…¸_.– æ¡ßË“g¦€ÙxŸ÷oæÏ)œiy›”[{òà>î?ÙŸ§•\n&Gô÷äÁ}"Ü)÷[Éüvš'zò¸o~Ÿ÷ïÇýv¹é{»ßÚ“'Ò-mälCD¸¯nëÌr“0D¸û¯C?èá>îá>îá>î_vÊ>>qŸäÙÏ\(8%õ±ØâÜÕ‘ý6œâukC6QêF8[x Ã}Âýó¹ßêi°|÷cÞdÿ-æ4lçDZpÄH§Ÿšù1µt˜µ9Y“¯>ûÞ{ÓLY$»bØ·“9²-f O¨­5˜pÿiÜÏB6^¬ß$ò´ñ`òΠîþ¥"ØCj—‡±C¶öK"Ê…xÃÒ¹ByDçSŽWê¥>‘xƒ ÷åÏSþwʲ‚éŽÿÌâ ÖÓ/KØÎk~æ'r¡*8µ‘…j¹“ÜŸ9\çË™½÷"Ü7Þ¯;ef'4âĺËmm²„û3³«¸?ïtôHîïip÷Íï‡ÕÃ[ãî׿÷S“6{Œ÷ Ü_2Ï3C´Ú¬îî¿.Ÿ'µp:\,xÀe¹ŸÁˆ<¡%ëÕSØÛäî¿×ŸçâùŒ}föƒû¸ûüyBõ™Ãf|´p:½|Oùvf¡·Ê؇±î«×}‹?Oèµ:µT™[ÖMhmÁcÆ>¸ÏŸç]þ<Ãò±ì h—p¿1öaìƒû¸ÏŸ'ëÏSÀbalxî3öað€ûæ÷_äÏsîgç7ûà>îã>ž„?OêòÓ¿ëo]§—à‚GÐ"†±cÂ}þ<2@ß7Â}þ<χN§31N‡î«×%"Â}Ü'"ÜÇ}þ<ÃÊ€ÂZh<ÇfíäGS@„ûüyõçÖ”-̯¿Î8êîËß¾?O!û£Nã¨C¸¯^÷¾þ<…¢$Ž:£á>ž»øótJ½ú â¨sñ [Ž:¸ûüyðî9êpV ÜçÏsKžyîsÔÁ}Â}þîá>îïìÏÓ_6,Ïw-ŸÖ8`JmXü|úÔÐBïlÁ~-ßã,†Y!“bíÄݪ®Æ}þÁ*î¬ïP¼å{»|ý_J¡Ê2Þ9óé¶©¯ îóçYéÏ)ÈŠœ`꺯- šdôë*†eÃKÚÌsáûW‹Ýά]ܨVeÝé®à½f¤åmµ7Q|˜w%ÜçÏSôç r¹‚ÙSŽOÎf;¿\Ô4M58U£·5øýHÏ ©ê$÷³`]ÕòåÜšMXV=c£„ûüyŠþ<î§Öñ†?¶ù Þ]¹_¨.Œïú7mÃUúq?ûë˜ç~j”}/; ÜçÏ3ëÏSø.á~íwøî·ÝL„â®J5îÏ´¼v…Á}Ü»?ÏÇõ¨B³ãS[#š`?Ïp¿p‘ëÏz ½qj½Z›XkaW¥2%S-~Á³~UR÷‘ý\%ÜçÏC,|ßոϟ‡€úßÕ¸¯^—ˆø4à>îîãþÜÏö”o$³³I4‰ˆpÿ Ü/øuÌ?»Š÷qÿêÜïdLž5Í"‰“÷qŸˆ÷qŸˆ÷qŸˆ÷qŸˆ÷qŸˆ÷qŸˆ÷wá¾BÜ:p_!î !„À}!„¸/„÷…Bà¾BÜBûB!p_!p÷…÷q_!p÷…÷…Bà¾BÜBûB!p_!î !„À}!„¸/„÷…Bà¾Bà>î !îã¾Bà>î !î !„À}!„¸/„÷…Bà¾BÜBûB!p_!î !„À}!„Àý³¸ÿ‹Bˆ|Ü›ûí%"¢¸pŸˆ÷qŸˆ÷qŸˆ÷qŸ.¡ÿÛúÇOáC$ÜÇ}º(÷WëË~Rû,´ÁÕ…pŸžÉýïÿÓ!ìÇáö—ûÿüx ýx”ÈÀÖ©}<îÖùº± ܧ‡s¿ƒþ¯ûƒâ!Á;;^ІMŠ\ÕâçK„ûôÀñþBîò§¸ß9bû÷n\Ìóîîç¸ßk_‡û‘õÜ'ܧq¿ÿÏã¹ß™…_Âýøåp÷é-Ü®ë~œH)¬ëßCg«áå÷‰ˆp÷‰ˆp÷‰ˆp÷‰ˆp÷‰ˆp÷‰ˆp÷‰ˆp÷i]FÿÚý\*ó=Ò©ú„ûú—ÛçÞ~Â}z#÷?Vä¶QíÖx¿SsÛ±cƒÜ7UÞzOëºJà>á>½ú >|O«“?¡#:¸EZE„ûôjîGÞŸE|p?‘×?Žî‡mÞ²ò• Ü'ÜßûqôGŽç¾)~Â}Âý÷#óû©•ÉWpŸp÷iåºîÇéòþ¤yy ·ß†”ôðÚÖŸ "Â}’Ç™¿<Ÿ•p÷éöÈëäAŸp÷‰ˆp÷‰ˆp÷‰ˆp÷‰ˆp÷‰ˆp÷‰ˆp¿×!„Ù0Þ'"2ÞÇ}""ÜÇ}""ÜÇ}""ÜÇý{êÇ?Þè ¿mø»ÎꮳM„û¦ìô‹ßmv Ïñ0ŠßKó›Ùä÷B?áþU¸ß‹}ÿÓ÷aãÇW†Û~ÙêË&ß÷6ÜC°%©“ íã!†ßzqøÏl·÷/uÁîú~Ü­ìì÷ ÷/Áý,û¯|dkç7Ÿ¢Iüè[-™9ñ>÷¿_úp0¾a¶ÛgöÐyqøY@?áþÉܯ/Îý,"ªq¿6é¼5ÈMa±Ö¼þæÅÚ÷ñ&£OsÜ'Üãx?K„ƒ¹ŸÚª±rææ²ÜOMèã>áþíç÷繟½Oœ¶‘uÝs)©uÝ­/pýã¦6}Â}ùûô®ZÜ'ÜÇ}zúAŸp÷‰ˆp÷‰ˆp÷Ÿ†^;£4Ɖ—\•O$nÑoíª®{|íñ•[õ]>7L!Ãý§ùóM–7`þ‡·÷ƒûß›b©3 6u'î?¯Ç.Îýc Œûôçùþ× µË*óœHíkëšÏ÷ÐF.:Á,Ïá&[~;‘öd ¶>šxŠªKõRgW©”ßÖ­0¸fÅGèµüéÓ½`qÇzÝxB÷Næ9k[[KB_Ru•­YëÐ$Ø€O¡ Ñóz,˜æ´ªRÍ.Oîef¼cžHAPü²¿Ä<ççÝFê¶‚­ÍR¬3~Œ×µÕF¾‘šälÕtgî¿§ÇÖ’´sÁëùS%Üý>Ü¿ñx¿ÌÊÔ¤ðªñþLkS‹hÅâC¼·Œ½¹ÿª;†û“·¶¸o~ü§Ïœ6r2X8¿_nírŠMâif&ºðÿY¯‹=¸ÿ˜Ãý»î½=Ÿ§¶Ü¼Ý^žÏ“míÇÉ¢š©Îðƒ«”ý£Ï2ì,Åæ×ußÖcïèªuÝHÇo¾ƒ/œq_þ>ݸ¬ãÂó}¦¸O”ºèã>îá>îá>î_톷S p£ðxËsÊD¸š?O»›EÏÕ–.wòº¹2p±žpÿþ<·³è ºôt² ûçÕF+Ãü¼~~aü …4í[Ÿ2îáϳªÒò0‹žZ!O¼~-e™-%ëôwX«!º×)áþŽþ<Ãr§`Ìa=…éYx¼x¤xma ç<÷ïrÊD¸Úx?R¯Ÿs8`¼oØp"%hl½Õ¸÷¯|ÊD¸¿ãü~Öð¶mÑShðrÎL1Ý”ûŸ2îï›Ï³e÷qM‹žZƒS“}«ú‚WLsñN^µ®{—S&Âý#ò÷o÷«{&¬|áþ2îßî‘ixÆÛÞu[D„ûDD„ûDD¸ûœy†3σWV|LÙ¤?mÜ?Ÿç^æîÜ„8ÜÇýÅþ<­[:Û.fÎ3lm?ñvÎ<¬Ù{Yî<ØM(òbꩊÒp÷zÝl9Ϲæ<µ¢vggžNíÅõÏ¥PevÇSžïä3Öq÷øótF”4çɶ6[¾T(N>Ò™g²I·Üy†›P|35œÂ}Ü_9Þþl ïÜc¼_kÃcœyâÍ»£åÎ3Ü„p÷ï1¿_ûU´3Ìy²­]”vª3ÏZî·‹Yî<ÃMhWî›ßÇý5ù<ÃU¯ÔbÔÞæ<…Ö¶§8óÄ/{·Üy°›Py±7¸®k¼ûò÷ßR… |X„û¸&äc"Ü'""Ü'""Üõmx¶Ú ~wÿ$“™ì¶:–pÿþ<ín=ÁÌ¢…xºz•“/u,áþÍüyngÑté ÖEœXj9£·óЧêØ¸{áþýy‚¥.×±è)4xˆ¹ìsÛ#6jÃú‰øpû0/ ™»1ë.÷/íÏÓ/~¹ EÏdƒSxš/U»¯ЮÜUÇîßl¼_¨&ÝÛ¢g²Áñ›ƒÂE{Pû:÷qÿóûÙ»ïv¶EO¡Á­ä{u:žÚ©^@…ÕW‹û¸›|ž-ÓkZôÔ™ ÚZiìïç‘^@Ùç¥èXÜÇý»æïßî›ê§µSêXÂý'sÿvÏoóÀ¹ºW?î«×%"Â}Ü'"Â}Ü¿ËÔ™…Ëì/ï7_¼¾xó;üò¢|žb®Þä'ºŸ9gžýö ßÖÒGv ¯à~ÍŸ§_:Û.fÎ3lmc “wæ™LÐl¯4Þá\´¼ƒ%8-ö|ÊïÇU¯›(iiW2ç©ì«oÞìÌ3SÅxǯÖÃ^©|òx¿<²ÎÖ7ÍL_NšóÌ´6õó›¯P»¯3ÏÚBÜöJã_¼lÆ{55 }Ë<ÏBw¶xyg°¤s~¼?ÓÚÆ@æ î¿ÓxÇ÷¯>¿_ûtÛæ<ÙÖ62ÓÜN 0Þ)ÌKèÀ#¹ÿüùý7ƒ+½ów óù<…Ö62gžÖõAÒo…Û\_¼ZƗЇëºïÊã$Z’Hǹî›P«n‹(1%͉ }Ü'"âÓ€ûDD¸ûϸ,ÌZ̸Žì1IRÎ…È&SÞD¸¦?O»›EO¼È{'TüÜ¥ò§ßðà$ÂýƒüyngÑSvéùò§à¶qC•lÞqgŸÿì‡Tî]¶,c¿$Âýãüy‚%×±è)4xyQR¼¼¥VcL—®ÕÚÌRì×D¸¿¯?O¿ˆã‚=“ ^RÔš-ý>:Žwà°ü²ß…2Ÿ,÷—¿H„ûgŽ÷ už{[ôL6ø‚ÜOÇ Ž -lÉ‚û„û¯˜ßÏzÀ¶³-z ¶!¶úÿ¿–ûÁ›­‚—ƉH„ûûæóô× #¿ö#-zj na’>˜â®#[YY·ÛþlØñ³ Þ±ÖD¸Dþþí~x‡=_ðy¥ :pÿ½Ü¿ ËÚ¿[:pßxŸˆ÷qŸˆ÷qÿ¾sœyÎÌ<µs.õ-º]áþ£üyîeÎÙg§||ç¬jùªãÞ«‹pÿ®þ<­[:Û.fÎ3lmãÌ3ÊmŸÝ:'U’²_G¥FèÁìêHáþKëu;•AÁ:#Íy²­”;½Ê™gæìž×9 ‹Ú¥î-²`¼ÿ^ž– yðeúwÌyfZ»¶¦ôÎ<ÇôÃ]:§Ìý]Ë•ãÓ¡©Áî¿}¼Ÿbe;Ûœg¦µD[ü¾íàÌsqîÜ9¸ûïšßOÍœkΓmmãÌ“qeØgWîœÚºî­¹o~ÿ½ù<}Ó•ìòÑÞæ<…Ö6Î<Õ¥ò·uNäžõH{¢òbop]×x_þþ{=jœµÎ¹æ·È§€û¸ïGu܉#Îéß"îã>îã>n1•ýþ:s.©üŠÉf˜F Üçϳ—³ÊÁ=ä&ïíúrÌÒ+îîóç¹EO<‹´mû¨KCƒ>3ÁÓ`_Om -xw¿‡9O¶µíþŽ=Ù“½Ú‰ ?ÍÓ-†Ê‹½ñgÜÇ}ùû·IüÆÑAGÿîÓ¸Èë"Ž=Ï8Ð'ÜÇ}""ÜÇ}""Ü?’ûñ9ò}qvÎá€&î?ûñ´ú™Mʳ¨Á=Ð$"ÂýgrXÊ8ÜdËfè¯2ùÁ½›DD¸ÿŠñ~¶Ž©%Åžª-J ù×6‰ˆpÿ!þ<[¥7ÖßV«·LÁw¿&î¿}¼gå‘Ü߯ID„û¸¿²5ÿ†]¹o~ŸˆÞÂý63‰dàý™[ÒÅ7’ŶID„ûê¶+ûÔŽƒ›DD¸û+ë¿RžÌg5‰ˆp÷‰ˆp÷‰ˆp÷¯6Aß*o¼"¬Ð€Z9Bç,ŽY«0ëE¸ÿÒçmMÖOÍÀe'õÂn nq{zÞÂ÷‰ÞËýÔSm·ìt>>i¯ÿÎöÉ!gí#vãé§…:²Èþ†§Ÿêáy £…Ï)¾ÙÚ8áþ¹Ÿ…lüÁÐñM:†-\¥²ƒâ;“Bñ£ü¼·­§×¶¹'˜ÇïZ þH©‡¹Gªðˆpÿdž°~|¾v¤*…`# ^C©‡ŽoÃÚàIî§šT.H•1÷‡ü¼.÷ß2Þ+¾È¹|¼Ÿj@²c‰UÜO]öæþðv÷ ÷Ÿ3¿¼‘Ÿáþ~óû©+AÊÞ'…Âw  #Ü'Âýb.Í–N02!°G>O vr]÷û¤ÇpÚ'¸ÔüýÎ î>Ô¿ .\×í÷îî?0ßïù"¥ >"Üß‘û¼k®†~î«×%"Â}Ü'"Â}êLaÝÚ±gf æ"^Ë'¯â¥Dòyf¹vqsž2…/îØswîeÜ'ùûižÖ-m0çIµvIfçE{:=¬]8ÝÃ'[O—Ê*–›@¯æþL½n¶fçDsžZR¿’ëâŽ=‘òºàûÏòðIMjŠÎpŸ^ÇýòÈz8½Ž9O¶µµ‘f»¤cO s—òð‰785ÔÀ}2Þ_æÎ¹ûþ°W÷kmx†cÏéÜ/ßâ>áþåæ÷SçšóÔ¦Gžá؃ûîÆ}’Ïåfa}/r§ßö1çɶ¶=±'¸†yeŸòbo|Á÷ ÷é %Î…÷qúîîî?|ÞcSx-h$fòãU©‘Ö.42ÉC¸ÿ4žv‹ž¸ÜÿÞÜO}Ù¦â>áþKýynaÑÏ"',vÒ Ž1_þ'e×SNŸßúRÙ«Ãr‡ÓM~ˆp±?O¶þ¨jÑ“­ØŠ r—[Ù¤êžâeeãtöP{€ûuL~ˆpÿ—…`íLj_Ç¢'eh“änÑ-XÅ–ºX/ ñS ÞsÄ;-þ%)4fÞä‡÷÷ïGêuSk’ ÇûÙÆìá¸ÐæLV÷oÄýÚ ßÏNJ´³-zÊ3?“ÜŸúÌÜ}áÿk®¸O¸ÿ–|ž-•ËZôtZøq^(þ¨™¶9ñ£s@‡Œ/&¸_[×½ˆÉî/Îß÷3{IŃ…SÂýWsß3êÞYææ'Ü7Þ'"Â}Ü'"Â}Üß{® üœ&"¢÷r'JŸDxü$5îê€:n q‹žB.`ÁåÆš3áþ÷w-¨IÙCj|pŸˆpÓŸgèiÓž3-cÑ“ªÀ*¸à>á~qž'’ÓÜ îî?û­dͶ7÷ÍïîÏr¿<ÒÂ=ñÌËa3ZÀ8ˆˆ÷OKÞ7 CD¸¯n‹ˆ÷ù4á>îáþ¡Ü_8?“ší9e^¨|PÃD¸ÿžU Ë®îÐùMî¡°¹« î¯ä~¿øöc2åVfgœûCû‡™‡ öŸS8y²ý‡áúfGOÖîïž¿ßOÉ<|æ ÝßQ{xwö¼²')^î¡ó¢²d"Ü?Ÿg†µG¨§Ž[®Þ=Ì_ZÊ»>÷‰pÿäñ~…×á~y¸}÷#ú¸O„û¸÷#+“Ü7ÏC„û—[×M½³¶®AdÜ;hëk'Û¶ «ãÝ•š†‚~"Ü¿AÝ—"Âý×ÕëÊ>$"ÜçÓ@D„û¸OD„û¸Ø4ÔZ?¢c–L.¾Ïýzº-ÒòãÆŸMD¸?ËýH]kö[[ްЀÚ#þ”à³;\ ’Þ…˜±E: Çp÷s¿3VŠçD~ÿkÐvæûæµáHBjëã̸ú´®Qp“/’²*4/˜>Ë©l‘"[HwnL^qîg9[®j™g ×è?ßÚ`qY°«PÉõø+Ö|‘]$Ý–-R+US/)âS«û»øóFÖÃb¥øÈe8~´°c4”mm¼2«|vÙÚ±T5rªyÙ2 ¶H“Ü/tîãþ%ÆûÙ9ñÚìùªñ~¹ K†cññþª›§UÔ`‹„ûd~?JÆyî/ŸßOµ÷ïÅýv%[¤]¹o~÷ÏÌ牻ßD–°âNmµ|žlk‡·ùÃI­ÈÂc¤Câ3Í«­4¶H¥uÝx×}\ÔÁ}Ü—¿›j™|>hÂ}ÜGpñîã>îã>îß’û3ƒÚQ†™×Kßüx "Âýúó¶ãþ*èÇ“üæÜ-îáþýž³X¨Ë &FŽž5“édš“ä>zÆŒeëèå\Ø‚} á~ú¹ê35/)'™xeS¶ý©ú¦TEqpêi¾R)X å–D¸òçùhkÚÏ¿Ãý>OS^=3ud“ÜÖ%)è'Âý5ãý„EŠrOç~¡+Ná~톌ˆp÷·æ.ʳóÜoá|Ü'"ÜO¬¬öá;,q÷›Úºnd±4µÎ<\yÎú®¬Z×t2îáþúüý»{Μþl?"ÂýWs¿û4WÐ'"Ü'""Ü'"¢Wq¿<•ÑpJüù®Áf¬Ý‘<Îeܯí0hì³Üᇈè¥Ü&8¶ªÑˆO!ùr+4kãó½‘Ù"Âýr¿Vu5t¤™,k*<˜7^zæËMDç~ߟ'^=”By™ûå²g/-DDoŸß/ûíì1Þ\opŸˆp_î·˜Cg–û)›LÜ'"Ü_Ïýöɦo)\­ýøÎøSm]7xq""R·uªî¿®^Wò%á>Ÿ""ÜÇ}""ܧà$Rv*©ð€°SV)>Öm\dFnΙ<ßr“–8”DZn¹ ÷‹Ü|« _ÜUܬ=k·üK[²Ÿ%íÜã—¼pŸû¼³:'uè¡¡Hð26Ÿ$6ó¨QÜr¿ó=.øç s=¿­¿o^6öŸÚip«>Êqët:gÝù©Ït`ämñâ»È‡µSÆm¡sâžú‚ ?‚þw)û¬ÍÂWq+Mýqÿ÷³-øçÄë³f6Yƒ–bDÙ¡¨ìz”-pösäñ¼Ùc-¯°ËvNÿ{ŸäÙ2üþßÈw)ØcµrÂàW ýqXƒþ9ÜDîÓ#Ìz ÅÜgÅ]Û0Û3ŽFZ¹›Rÿœ¹x§†·…+ÁÇaûGîÇ¿ü ¹¿uÐò½2ïÇû‘yƒà¤ê1ãýÔŒjjØ^¥ÎwàŒ³Egà¿°3ç¹iääÊpm¼ÿìæ¹Ÿ½•D|ÜŸ‚lÁ?'Ëýµóû Žä~m"å"Üo‹³k3?;ÍïO~ZÆl<îmŸe"ÜŸÊ¥ÉúçôßçB9%£Ðà62Ь ÷Ïzk†}kÅ/Ò©~Î>eg0–î?ÇŸgkª:kÌ2ã(,z¼¹MÐLt•‘Nû×÷¢‰×÷¥Üq÷Ÿ?ϳü×ËܦÌý=Ž59ÞwõÁ^4¸O¸¿žû[îG‘åÜo÷4·¹÷ÛݼhŽá¾ù}Üòüþ–M xõ4æ6Éc?‹øRü¤/^pÙ²Uží0 ‘}KXðÁŒ­ïa$LDDDN!aÏ?rë¹ñßÏõÆ7^^o+>írË$LDDDÎ+aE?»ü«fž–JW×H˜ˆˆˆœNÂ.¿zõ"ÙUi»¼Êt;&"""$,çdµ×Éjwƒ„‰ˆˆÈ©%lʨúJX~÷H˜ˆˆˆPÂnÌæï—Ÿºm?e¯™e¦#3wå?ýêÇŸ¼GùLˆˆˆÈq$lð<Ùç|õÕW>"""²c Û—‡=ÙÛ^xáþýû?ö‘½JØîòðáÃ;wîüðÃ> """BÂÖ‹‰H!akç믿6)"""û“°ÄD¤ˆˆˆìOÂöþþ­I!"""$LDDD„„‘0&"""BÂH˜ˆˆˆ !a""""BÂDDDDH˜ˆˆˆˆ0&"""BÂö#a/Šˆˆˆl—SKXúçgëCÂ| #a€„‘0FÂ\áÖÿ'þ-mÏŒ/€3óý=¸w÷õÛ/?‘W_yéþ»w¾ûÓ¯H Žf`ÏÿïÒÖëÉŽÊãï>ýí/~vçÛ_~üÞ¿þú›'<üö“?ÿþço¿ùÚ¯?øé£¿ÿ„‘0àhvyalêÁËÿ¸ñ´à•°ËmN}5þ¢Ï?Í!öÈ{ÿ·þý·ßÝxüÉ#îÝ}âa$Œ„Ç”°Ë¯N‰Ô”]U¢øf§ž|ѪyUÎBÞyãö¥=ó°·ß|íÙ¼$ ó‰{%¬Á–¦®KMIØÔe³ünd^”»æÁ½»_~ü^æ ß|þáýwï0:²ÖÀªîÊŸÒµˆŸg?ì‹×o¿üô>°)~ûÉ«¯¼DÂHp4 ›ºìT”°ûCÈ7le¾½J×Hp"jñì9$Ì'ؽ‡MÝ õ–ùâå«È”bñÊYÛ‹’0À•0àž0öÀ÷_|ô“לÿëÈüñ—$Œ„€ÎX'Œ„€ x¶bþ_>ýàÙŠùß|þ¡óIXc^òÁ½»¯¾òÒÓîÈ÷ßyKw$ C@Â| #a®Ñkõ¬©\ÇwÖH °WÖi‚;ÃÃFÂlæ(WûSiíû©+aS«ÞœÚþå—"ŒÔ\’0€„‘0[ØU*>'ÿ]Wç(3—Š|iªt|êi< a$ À y~­o·yüêu¯â>_½žGÂFœHÂâyŸ„¹- a$ Àþ$,rOXÕÄßÌGHF€Þ˜õ«üVÍ÷àç÷!óü©»õƒ›b` #a6“°™ß¸k!a #aÆõü%.€„‘0@ÂH #a€„‘0F #a$¬å ˆˆˆˆlWÂ\ #a€„‘0F #a%nÝúQñ ÏXá`÷§ãË­ùîºÆ¢ß^»Í¾Çe/ƒÓ}¯npÌÏ'vR [ç²þ‰êùg^ºÑ:'<¶ùËíHÂÖg€„ -aO¹*.™3Ò3õ{ÿò…®nçÆW‹vuõiù7rù]Sƒ´âå±üfó‡ 8\ÝiÄh§ž|S:3˜™‹‡~ðÁ‰<˜éâWI6º„]ž¯žø¯þÆÏüG~B0óüÚW,¾zæ\•›ñ s6ûìiyC­Ú½«ÌñªcÚðk=©ËÆœÈ4zí,ò/" aëIØóÿ2Î\‡h{0ra#s*¸©†=¯’°Ú)­æa î^äš_óãš[{Œ"2Qœ,ŽËzð³7ìàÌ”°ø?N€„m|%¬ál7Óx"“,—s7‘sɶÖ<ŒÍÖvÐLɘyŒ‚׺fÙ­ lþàk~Ú‘ a‡’°¶yÉÚóÍ”gE¤8S¼‰§¯„5o6øFªþF¡—^T£^Öegv18mV¼ j@€„(aUwåG¦‹w^§#«îÊNƒ6ÜÙ=‚²xW~ñ¶ñun<ÏŒyÃM~™ fžV»3ÃNƒ–vÜ•€„Y¬u7«3,tŠª]¼ãxgJç~£$Ìém, ;ÃršÜÂ( S[H $Œ„-7Ÿ’¹ºãÔŒ¦ÈŽs[Ê$ì Ý‘Ík\iŠÜµ„%eˆ6Nwdd±‰çw@Sä®›"•!HØÝ‘š"óS¥‡lŠT† aÛwGϲ‘«eš"k'g›T† aûîŽL±õß«N´š"W0°ÌÌi/k˜¿f`@ÂtGÖ]™#aISä>›"•!HØöÝ‘™;ŠŠæ—4Eî§)²AË”! ³NØîѹÇc a$ìDç~M‘ @ÂH a$ €„í½;2]¬I¶Üì•ÉŽCºÄ›ê[:gSê5€„±;r{í5H(aë¼ÜŽ$,©×@ÂHØå# uG¦Ê*I ’iÿ ’™m×x+îjÃ2%I½&°ótG^]\ª¶J2iÜmƒdÕQî¾ñÁG½&¦;rñîÈ6Ò Ùvmc¨ÉŒ7´Éwð(ìbpÔk aVÌïyrŠÌÙ5_«=O' ’3$#3sÚüg^þ îÌ.G½&Fªe%øoýÚ*É`ßQp"¦xÉ´|ƒdƒ„uÙ™] ŽzM$Œ„UßœbÝ‘Å϶{«îÊO$gßx^Û Y¼Gjjƒ™§Õḭ̂ƒÓ eê50ë„í ’;:gÆ( a$ì¼§7 ’ÜÂ( #a€„‘0¶ëîȶɗMîÖJú%·8F*€„Y1c¹iÞý’»–°¤BH K tG¦‹,¯nsªrñrW#íú%—;Ö£U¡ aÛwGNIXd•²9ëyê— Î–&ŠvìîÈ^VÜgý’{ì—T¡$ÌŠùÎÊS¶WûÒ‘ÉMý’mž¡B@ÂNÔ|fþf£*Ñ/YµÁ¤B@ÂÐYÜ~ä~¦üÕµàfõKªPôH˜ÅZ‡@¿ä€„‘°ûõK20 #a€„‘06lwäÔ*ƒÌ[i“ì8ÈÚ$$l îÈM–kÒ&¹k KÚ$€„鎼¼„S¼®S\Q3¾‚6Im’™µI ÓùY¤O0_,÷ƒÈIZ›dð 7˜ÿlh“°A»##ד2+Ý%L›dÒ&{ $ H؉º#3“hñýÑ&Ùl`m÷Hi“°CuG¶MA&m’%ÿÐ&œ×& $ìÝ‘ÁûЋIÚ$µIj“f°£¢MrÇ@ÂH؉ÎýÚ$€„‘0@ÂH ¤;2xŸøøSZ+ž¡y„/WƒÛ¤šÉ$#0¶”X´=m9¹!%ƒHXóFºŒp|…6wzHØV\ò>M;æ¾/n$S/Ø«ré÷Ø`œËð SÔ axçŸÜÐ8™Ñ¦ü Í\½vjlþ{œs5hÑVÔ a»éŽœº˜QÕB¯xÉcZ¦r¡÷X%aKpðÒà Kj‚$¬×ŠùSsXsÚóÏÉ¿Ð˽ÇZ [g„“¢F SÂR õ¯ên¡¶‰­â«¤ë¼Ç†?\z„»KXRÔ a}%¬Ø´¬P Þü>õBÅ Bm‹¾Ç†™ÇEGxN£eÕ4®»ò$Ìb­-×xÎŒ!0F2 €„‘0F #aí³Z[Ý©­MR›$€„éŽÜàL¬Mrj(’6I ;ywd*ÕGæLÚ$cž´IH˜îÈ+"¼´©3û©M2³Mm’vöîÈø >òrÚ$®Tõ’°¤Ë@ÂvÑ9ßÀš÷J›äœÎ€„í»;2…û#}>I›ä–´IHØ®»#çÜ•¯MR›$€„Y¬u.K¬/åôoˆ$Œ„m a$ÃàH $Œ„°ÃtGæoÆ_aúlÛÀǹå|ð›ß¼¥/²´Ç:o|ÁQ €„¤;rM [úµV;oÍÙÚàç¶}ý]Å9%,)@Â6¬-Š,‘²Ë+Týopå…ÈKgöyªþ(³þÅÌÈ©…ïó—©"•šëìI ¯R»{µ°È§"¸ÄIÄã‹«ûFš=›ap”‡ aÛHXÃz›Uß’yNíÆSÍjøS'†|+e|ÙÒüKç°ö{×Ü“¢rÕÖeæ{?ãßoíhW-¢6ノòP$lËîÈŽŽÁßÈS_êR ìo8w¶íd­„UÈÝ“ÈUÉæ ÆE|êâSó{ŒLgÇ[³"ÕFå¡HØfÝ‘} ƒ×ºR¶ŽpdkÉî¶ôžÌ¹ë®£ddfN›ßcÕ…±æÙÅà(@†°¶yÉæ™ÄâÕˆx)dPæ’°æ“P¤Èrµ=©šáª:gÏÔ‹¶OÚL ë²3»å¡HØ@VuW~üZBp¨­²øÕâ½MKÜ ßv+Û&{’¹+¿xÛø:7žg>mk§6X¬@ḭ̈ƒÓ eÊC0‹µýoëøŸæ¶'Ç;S:÷%$Œ„èä±ÎZ KìÉÁNÆÜÂ( a$ 0@ÂtGFþmæ2ªÅ=œ9ªœ®ÝWkPßwYp‰WÜöç}þqYs¸–û™êRàfn$ìÔÝ‘]n_¹\Ru㘃yf [s7ÿyïøN×9 Kü#¤ã/:v¢îÈ©ÍÞø}ЮjË%S}Ç¢êÆâ“U7ΜL™iü3…œÚ`ð¶ÚÏûÔáŽcþ@t®â»ŽÿoóQèÒ" °cvG¦y­‘{šW˜Tݨº±ïàdÊL›OŽÌýõZrv¹Ÿ÷ø„f~"m}‡«vé†kºSG!óááa agïŽlþ¥Ü½\²¹kEu£êÆ…gþ'¶êù™¯ºv»ÄÏ{ǺM†«í÷X¯£Ð½E aéŽìõyCkÒfk¾=®Ë¸ü½ßü»ÙýV7.m`W/4_^Ýöç=8¼ã W~#ùŸôªíg®ž200Ý‘ŸÍ™Ü)ÞéÕ aó+‡U7ªn\bpŠ ^;¹¹s¨ËŸ7®óóÞ¬ªq ë8\Áw=_ò‚ÿ1ö a‡êŽœÚHsS^P_j/Ô«nTݸÚàÿ²$xW~þZKäU~"Öüy¯º+?.a ÃüÐ<Ù|žÿ(º $Ìb­­w°Ð¯Õ~§58ë/`q¤Ôk‘?u> a$lƒóÜú¦ºÑ'sóWt€º×øC:N³-H  a$ 06çšö uŠJ$;Î5(‘ÜpJÛ ¦;²úô9Ž„)‘T"yH Ó6€„éŽ,ÿx¾¿¬ê¿•H*‘Ì|ã1J$“¶A$LwdscpÙÆ`GÞB‹nç¿K‰¤ÉÍ'i@ÂtGÆëÏÚVhl(÷P"©D²ê(ìqp’¶A$LwdÕŒÚÇ7Oh6œ§“É’‘97¿Çª c§*‘LÚ0Ý‘µÝ‘i^Ù_æú™w¤D²jƒI‰äÀƒ£m Óº+?âRU÷ÐÄKÓ”H*‘<^‰dÒ6€„Y'ì(‘ÜÑ!ðùÜ×î9²H ësžS"ÉÀ ÎÌO/0H $Œ„?CÑköP;ä8ób €„Y1ÿ˜¦RbR€$LwäÔ5¤ùÛOÚ!»ʈÑ*@°=uG^}rí"ûÚ!«vO¢D a§îŽŒ›JdO´C 2)@fÅü%NÛµ/‘býtÚ!ÛniR€˜  ;swdÕÜb|ûÚ!ãL   ;Owdmë_fþ(i‡Üs;dR€$Œ„í;:î F—í'4°¤H $Œ„°a$lf‡Lí ¨&à2wÙur@¬˜¿¬”¬cÃ6j7Þü·ÃÃ$lh ‹,(U\Ó!ã@S 1KýâU’‘׿̠Í¥m_6¢„EVò̬{™‘°æ"ÂæªÊ[&´aÏ™£¹pÕñ%x€„ÛYÕÛØ&aÅͦÞU•óßN­!µmvé— a6îŠùizÑöȦ9í UU¦úõÜ#7–u¥ª;Ûf¾°½JX ´%¶]ŽŠ_û)îR[uwG ›¹Ùå^‚„HØ$,c0q3ˆ—3FŠSM•d\ªDg‰Q NÝ.ô0‹µV°fëßÕ;ç6b0’!ÎÁÀ$Œ„0H  a$ 0H  a$ 0@ÂH a$ €„‘0@ÂH #a€„‘0@ÂH #a€„‘0F ۟„‰ˆˆˆl•óJ˜ˆˆˆÈCÂDDDDH˜ˆˆˆ #a""""$LDDD„„‘0&"""BÂDDDD„„‰ˆˆˆ0!a""""$LDDD„„‘0&"""BÂH˜ˆˆˆ !a""""BÂDDDDH˜ˆˆˆˆô‘0iK»„‰ˆˆˆÈ !a""""$LDDD„„‰ˆˆˆ !a""""BÂDDDDH˜ˆˆˆˆ0&""""$LDDD„„‰ˆˆˆ0!a""""‡ÊéGý‰Ú8èIEND®B`‚jbosscache-core-2.2.2.GA/src/main/docbook/images/MultipleCacheLoaders.doc0000644000175000017500000007600010660354341026120 0ustar twernertwernerÐÏࡱá>þÿ 9;þÿÿÿ8ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿì¥Áa ð¿[jbjb„,„, 0îNîNFÿÿÿÿÿÿˆ˜˜˜˜LLL‚J J J J V ‚¢.¶v v v v v +++.......,X/Rª1^G.L+÷"+++G.1+˜˜v v \.1+1+1++˜òv Lv .1+`t˜˜˜˜+.1+1+Í-ŠÂL.j ŒåÁJ !+å-.r.0¢.í-,21+2.1+L‚‚¤& $‚‚&  CACHE TCP DELEGATING CACHELOADER TCP DELEGATING CACHELOADER CACHE REPLICATION CACHELOADER DATABASE STORE CACHE TCP CACHE SERVER TCP TCP LOCAL FILE STORE LOCAL FILE STORE FILESYSTEM BASED CACHELOADER FILESYSTEM BASED CACHELOADER SERVER 1 SERVER 2 SERVER 3 EFLMhi„…‹Œ˜™¥¦µ¶¼½ÎÏÓÔØÙêëüý:;DENOX[íéÛéÛéÛéÛéËéÛéÛéÛéÛéÛéÛéÛéÛéÛéÛéÛéÛéÛéhï<»hï<»56CJOJQJhï<»hï<»5CJOJQJhï<»#jhï<»UaJmHnHsHtH&FLM\hix„…‹Œ˜™¥¦¯µ¶¼½ÎÏÓÔØÙäêýýýõõýõõýýýõýõýõõýýýõýýýýýõõ$a$gdï<»FZþþêëöüý.:;DENOXYZ[ýõõýõõýõõýõýõýõýýý$a$gdï<».:pï<»°|. °ÈA!°"°# $ %°°Ä°Ä Ä¥<@ñÿ< NormalCJaJmH sH tH j@j b}È Heading 1#$ & Fdh¤ð¤<@&gdÙ@L5CJKH \^J_HaJ DA@òÿ¡D Default Paragraph FontRi@óÿ³R  Table Normalö4Ö l4Öaö (k@ôÿÁ(No List#?FS`pw‰Ž“¥·Öõÿ [ÿÿÿÿÿÿÿÿ/ÿÿÿÿ'ÿÿÿÿ&ÿÿÿÿ)ÿÿÿÿBÿÿÿÿ+ÿÿÿÿAÿÿÿÿCÿÿÿÿFÿÿÿÿGÿÿÿÿIÿÿÿÿJÿÿÿÿKÿÿÿÿLÿÿÿÿVÿÿÿÿWÿÿÿÿXÿÿÿÿ#?FS`pw‰Ž“¥·Öõÿ    ÿÿ[ÿÿÿÿ ÿÿ+£[FLM\hix„…‹Œ˜™¥¦¯µ¶¼½ÎÏÓÔØÙäêëöüý.:;DENOXY\˜0€€ØÊ€˜0€€°Ê€˜0€€Ø@­˜0€€Ø@­€˜@0€€°Ê€˜0€€Ø@­€˜0€€Ø@­€˜@0€€°Ê€˜0€€Ø@­€˜0€€Ø@­€˜0€€Ø@­€˜0€€°Ê€˜0€€Ø@­˜0€€Ø@­€˜0€€Ø@­€˜0€€°Ê€˜0€€°Ê€˜0€€Ø@­€˜0€€Ø@­€˜0€€Ø@­€˜0€€Ø@­€˜0€€Ø@­€˜0€€Ø@­€˜0€€Ø@­€˜0€€Ø@­€˜0€€Ø@­€˜0€€Ø@­€˜@0€€°Ê€˜0€€Ø@­€˜0€€Ø@­€˜@0€€Ø@­€˜0€€Ø@­€˜0€€Ø@­€˜@0€€°Ê€˜0€€Ø@­€˜0€€Ø@­€˜0€€Ø@­€˜0€€Ø@­€˜0€€°Ê€˜0€€Ø@­€˜0€€°Ê€˜0€€Øʘ0€€°Ê€˜0€€ØÊ€˜0€€ØÊ€\š€€ÿ¿(|  ¯[ê[Zð8ðYUY@ñÿÿÿ€€€÷ð8ðFX@ñð¾ð( ð ððB ðU 3 ðËjJÎÿ ðððB ðT 3 ðËjJÎÿ ðððB ðS 3 ðËjJÎÿ ðððN¢ ðG 3 ð€ ŠGÿ ðð" ð ðN¢ ðF 3 ð€ ŠFÿ ð ð# ð ðH¢ ð) # ð €ÿ ðð ððN ð S ðÿÿ™¿ËjJÿˆðCð ðH2 ð C ðÿ™¿ÿˆðBððH2 ð C ðÿ™¿ÿˆðAððH2 ð C ðÿ™¿ÿˆð@ððH2 ð C ðÿ™¿ÿˆð?ððH2 ð C ðÿ™¿ÿˆð>ððH2 ð C ðÿ™¿ÿˆð=ððH2 ð C ðÿ™¿ÿˆð<ððNB ð  S ðD¿ÿˆð;ð ðNB ð @ S ðD¿ÿˆð:ð ðNB ð  S ðD¿ÿˆð9ð ðNB ð@ S ðD¿ÿˆð8ð ðNB ð S ðD¿ÿˆð7ð ðNB ð S ðD¿ÿˆð6ð ðT ð C ð€‚ÿ ˆð5ð ððN ð S ðÿÿ™¿ËjJÿˆðððH2 ð C ðÿ™¿ÿˆðððH2 ð C ðÿ™¿ÿˆðððH2 ð C ðÿ™¿ÿˆðððH2 ð C ðÿ™¿ÿˆðððH2 ð C ðÿ™¿ÿˆðððH2 ð C ðÿ™¿ÿˆðððH2 ð C ðÿ™¿ÿˆðððNB ð  S ðD¿ÿˆðððNB ð!@ S ðD¿ÿˆðððNB ð" S ðD¿ÿˆðððNB ð#@ S ðD¿ÿˆðððNB ð$ S ðD¿ÿˆðððNB ð% S ðD¿ÿˆðððZ ð& S ð€Š&‚ÿ ˆðð ððf ð' s ð*€Š'™Ì¿ËjJÿ ˆðð ðð`B ð( ƒ ð0D¿Ë8cÎÐÑÿðDððZB ð-@ s ð*D¿ÎÐÑÿð ð ðZB ð. s ð*D¿ÎÐÑÿðð ð` ð/ c ð$€Š/™Ì¿ËjJÿ ð ð ððfB ð+ s ð*€Š+™Ìÿ¿ËjJÿˆð4ð ððN ð3 S ðÿÿ™¿ËjJÿˆð3ððH2 ð4 C ðÿ™¿ÿˆð2ððH2 ð5 C ðÿ™¿ÿˆð1ððH2 ð6 C ðÿ™¿ÿˆð0ððH2 ð7 C ðÿ™¿ÿˆð/ððH2 ð8 C ðÿ™¿ÿˆð.ððH2 ð9 C ðÿ™¿ÿˆð-ððH2 ð: C ðÿ™¿ÿˆð,ððNB ð; S ðD¿ÿˆð+ððNB ð<@ S ðD¿ÿˆð*ððNB ð= S ðD¿ÿˆð)ððNB ð>@ S ðD¿ÿˆð(ððNB ð? S ðD¿ÿˆð'ððNB ð@ S ðD¿ÿˆð&ððZ ðA S ð€ŠA‚ÿ ˆð%ð ððf ðB s ð*€ŠB™Ì¿ËjJÿ ˆð$ð ððf ðC s ð*€ ŠCÌ™ÿ¿ËjJÿ ˆð#ð ð ðZB ðEÀ s ð*D¿ÐÑÿˆð"ðð`B ðI c ð$€ ŠI™Ìÿ¿ËjJÿð!ð ð ð`B ðJ c ð$€ ŠJ™Ìÿ¿ËjJÿðð ð ð` ðK c ð$€ŠK™Ì¿ËjJÿ ðð ðð` ðL c ð$€ŠL™Ì¿ËjJÿ ð ð ððTB ðMÀ c ð$D¿ÐÑÿð ððTB ðR€ c ð$D¿ÐÑÿð ððH¢ ðV # ð €ÿ ðð ððH¢ ðW # ð €ÿ ðð ððH¢ ðX # ð €ÿ ðð ððB ðS ð¿Ëÿ ?ð  !"#$%&'()*+,-./0123456789:;<=>?@ABCD[XÝ X3™À4tW9Ìûÿÿõ4ýÿÿtVÿÿÿÌûÿÿY4ýÿÿtUñT9X3tJU0-$htT™4ýÿÿá$„tSýùÿÿ4ýÿÿE „t)‘ ˆMðtG18 tFÝ 8­  tRüÿÿÜ ýÿÿ0tM]!Ü Å"0t-ÉÈ9tL( ]!ø t'ø ]!Èt&M ( t%و٤t$Ùˆ]!¤t#UˆÙ¤t"U¸Ù t!ѸU t U¸U t© ¤" t%¤ t¡¤  t% ˆt¡  ˆt …ˆt¡Pÿÿÿ ¸tMœþÿÿ-$( t.ñÈtKýÿÿ( Áø t/ýÿÿø ÁÈtI±úÿÿ0‰htEÌ(l.tCu µ$tBu °&µÌ(tA¥”$å°&t@1!1,#t?1!µ,#t>­ !1,#t=­ @1¨t<) @­ ¨t;­ @­ ¨t:,#i”$t9},#å”$t8ù ,#a”$t7}¨å!t6ù ¨a!t5u ¨Ý !t4ù Øa@t3¥$…°&t+Yl.…¤2t±úÿÿ ñ( t=ˆ=¤t=ˆÁ¤t¹ˆ=¤t ¹¸= t 5þÿÿ¸¹ t ¹¸¹ t ¤u t‰¤ñ t¤m t‰ ñˆt mˆtýÿÿ éþÿÿˆtPÿÿÿm¸t±úÿÿœþÿÿ‘ ( t(u ˆµˆtF\F\Ÿ4v5°2Bÿÿÿÿÿÿÿÿ"„Є˜þÆÐ^„Ð`„˜þ56CJOJQJo(†*‡hˆH.0€ „ „˜þÆ ^„ `„˜þ‡hˆH.‚ „p„LÿÆp^„p`„Lÿ‡hˆH.€ „@ „˜þÆ@ ^„@ `„˜þ‡hˆH.€ „„˜þÆ^„`„˜þ‡hˆH.‚ „à„LÿÆà^„à`„Lÿ‡hˆH.€ „°„˜þư^„°`„˜þ‡hˆH.€ „€„˜þÆ€^„€`„˜þ‡hˆH.‚ „P„LÿÆP^„P`„Lÿ‡hˆH.Ÿ4v5ÿÿÿÿÿÿÿÿt¬        ÿ@€à<ìÿ%¼[ð @ÿÿUnknownÿÿÿÿÿÿÿÿÿÿÿÿGTimes New Roman5€Symbol3 Arial ðˆðÐh%ó±Fõó±F!ð¥À´´€~4µððÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿjV‰ÿÿ Manik Surtani Manik Surtani þÿ à…ŸòùOh«‘+'³Ù0˜¤°ÈÔä ü $ 0 <HPX`h'Manik SurtaniNormalManik Surtani4Microsoft Word 11.2@NZ@¾VkDÇ@Vš·†DÇG”þÿÿÿPICTŒ€Zÿ ÿþHH€Z €€ÿÿšÿ€´€ZHH¤“˜¤€”€Z€Z§ ©ÿ ©ÿ/òÿZÖJRNsZÖJRNsg9ÙÿZÖJRNsZÖJRNsÿZÖïÿ ©ÿÿÝöÿÝýÿÿßÿöÿßÿýÿÿßÿöÿßÿýÿÿßÿöÿßÿýÿ9ÿöóþðóÿöÿõóðóÿýÿ=ÿöó~`ðóÿöÿõóðóÿýÿ9ÿöóþðóÿöÿõóðóÿýÿ7ÿôóðóÿöÿôóðóÿýÿ7ÿöóüòóÿöÿöóüòóÿýÿTÿùóþóóóóôóÿöÿùóþóóóóþõóÿýÿQÿüóþþóþýóþ÷óÿöÿüóþýóýóþ÷óÿýÿcÿüóýó~`ýó~`÷óÿöÿüó~`ýóýó~`÷óÿýÿGÿüóýóþýóþùóîýóþýóýóþ÷óÿýÿ+ÿîóúóêðó÷óÿýÿ1ÿïóýøóóÿïóýøóÿýÿOÿñóóóóùóÿöÿðóóóóúóÿýÿSÿòóþóþóûóÿöÿòóþóþóûóÿýÿOÿöóýþóþýóþþóÿöÿõóþþóþýóþþóÿýÿeÿöó~`ýó~`ýó~`þóÿöÿõóýó~`ýó~`þóÿýÿQÿöóþýóþýóþþóÿöÿõóýóþýóþþóÿýÿ?ÿZÐV°ZÐRJOçóÿöÿZÐV°ZÐRJOçóÿýÿ+ÿâóÿöÿâóÿýÿ+ÿâóÿöÿâóÿýÿÿßÿöÿßÿýÿyÿûÿO BgFÄF†> BgF¥BgF‡> BI=íBhBg> B*þO úÿöÿûÿO O BgBh> BgF¥BgF‡> BI=íBhBgO > B*O O úÿýÿ9ÿûÿîO úÿöÿûÿîO ûÿýÿuÿüÿþO BgBhBgBI> F†BIBhO B*> B*ýO úÿöÿûÿþO BgBhBgBIO > F†BIBhB*> B*ýO ÿüÿýÿ=ÿüÿîO úÿöÿûÿîO ÿüÿýÿ0ÿüÿëúÿöÿûÿìÿüÿýÿ7ÿýÿÿìúÿöÿûÿìÿÿýÿýÿ{ÿýÿÿO O F‡BgBIO B*> F†> BhBhF‡> BhýO úÿöÿûÿþO F‡> O B*> F†> BhBhF‡> BhýO ÿÿýÿýÿyÿýÿÿþO BgBhBgBI> F†BIBhO B*> B*ýO úÿöÿûÿþO BgBhBgBIO > F†BIBhB*> B*ýO ÿÿýÿýÿAÿýÿÿîO úÿöÿûÿîO ÿÿýÿýÿ7ÿýÿÿìúÿöÿûÿìÿÿýÿýÿ7ÿþÿïÿöÿöÿ÷ÿîÿþÿýÿ;ÿþÿïÿ÷ÿöÿøÿîÿþÿýÿ7ÿþÿïÿþøÿöÿùÿþîÿþÿýÿ;ÿÿÿñûÿøÿöÿúÿûÿðÿÿýÿ<ÿüõO?ýýÿùÿöÿúÿüÿýõO?üýÿ0ÿìüÿúÿöÿûÿûÿìýÿqÿýO? Æ%)ç!Æç„Æ1ŒýO?ûÿûÿöÿüÿúÿþO? F˜%)ç!Æç!1ŒýO?ýÿ?ÿïO?úÿüÿöÿýÿùÿïO?ýÿYÿûO?BvF—BTB3>úO?ùÿüÿöÿýÿùÿüO?BvF—BTB3>ùO?ýÿ?ÿïO?ùÿýÿöÿþÿøÿïO?ýÿ>ÿýóO?þøÿþÿöÿÿÿ÷ÿòO?ýýÿ9ÿÿïöÿÿÿöÿÿÿõÿðÿÿýÿ+ÿâÿÿöÿÿâÿýÿ#ÿáÿÿöÿþáÿýÿ!ÿáÿþöÿàÿýÿÿÝöÿÝýÿÝÿøÿÚÿ!ßÿ^÷ZÖR”úÿR”ÚÿÛÿûÿØÿÛÿüÿ×ÿÚÿþÿ×ÿéÿÝåÿéÿüÿëúÿåÿCéÿüÿffR7NJNNNfJAòNAòFNAòFþfúÿåÿ!éÿüÿífúÿåÿéÿüÿëúÿåÿéÿßÿåÿéÿâóÿåÿ!éÿöóþðóÿåÿ!éÿöóþðóÿåÿ!éÿôóðóÿåÿ!éÿöóüòóÿåÿ0éÿùóþóóóóôóÿåÿ-éÿüóþþóþýóþ÷óÿåÿ7éÿüóýó~`ýó~`÷óÿåÿ/éÿüóýóþýóþ÷óÿåÿ!éÿîóöóÿåÿ!éÿïóýøóÿåÿ-éÿñóóóóùóÿåÿ/éÿòóþóþóûóÿåÿ-éÿöóýþóþýóþþóÿåÿ9éÿöó~`ýó~`ýó~`þóÿåÿ-éÿöóþýóþýóþþóÿåÿ%éÿZÐV°ZÐRJOçóÿåÿéÿâóÿåÿéÿâóÿåÿéÿßÿåÿ=éÿüÿýO BgBhBgBI> F†BIBhO B*> B*ýO úÿåÿ!éÿüÿíO úÿåÿ!éÿüÿíO úÿåÿéÿüÿëúÿåÿéÿñÿðÿåÿéÿñÿðÿåÿéÿñÿðÿåÿéÿñÿðÿåÿéÿñÿðÿåÿéÿñÿðÿåÿéÿñÿðÿåÿéÿñÿðÿåÿéÿñÿðÿåÿéÿñÿðÿåÿéÿùÿîúÿåÿéÿÿóýO?óÿåÿéÿÿûìO?üÿåÿ=éÿÿO?O?ú)J!ç!5­!ç1ŒøO?O?ÿåÿéÿÿäO?ÿåÿ-éÿÿöO?BvF—BTB3>ôO?ÿåÿéÿÿäO?ÿåÿéÿÿäO?ÿåÿéÿÿÿâÿåÿéÿøÿðùÿåÿéÿøòÿõåÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ§ÿþÿ ÕÍÕœ.“—+,ù®0Ô `hpx€ ˆ˜  ¨ µ'  Title þÿÿÿ  !"#þÿÿÿ%&'()*+,-./þÿÿÿ1234567þÿÿÿýÿÿÿ:þÿÿÿþÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿRoot Entryÿÿÿÿÿÿÿÿ ÀFŽ6Ô†DÇ<€1Tableÿÿÿÿÿÿÿÿ 2WordDocumentÿÿÿÿÿÿÿÿ0SummaryInformation(ÿÿÿÿ$4DocumentSummaryInformation8ÿÿÿÿÿÿÿÿÿÿÿÿ0CompObjÿÿÿÿÿÿÿÿÿÿÿÿXÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿ ÀFMicrosoft Word DocumentþÿÿÿNB6WWord.Document.8jbosscache-core-2.2.2.GA/src/main/docbook/images/TreeNodeExample.gif0000644000175000017500000000442010660354341025105 0ustar twernertwernerGIF89aów1!þSoftware: Microsoft Office!ù,ñ„          ÿÿÿÿ Žd)hª®lë¾p,Ïtmßx®ïµéÿ¿pH,ȤrÉl:ŸP“ЉŠZ¯Ø¬vË풪ˀwL.›Ïè`œn»ßðx‰=Ëïø¼Þjö÷€‚ƒsƒ‡ˆ‰p…#ŒŠ‘OŽŽ’–—˜>…•™ž‘œŸ£¤€}¢¥©ª‹'«®¯qv¨°´µ|­¶¹ºW²»¾¿a³ÀÄÄtÅÈÉÂÃÊͯÇÎѶÐÒÕ®ÔÖÙ¤ØÚÝ™ÜÞá ÌâåàcSæëEèZ*ññ*ìõRäQòûüøöÒÜñêGp@{¡ (È0áÁf›ècHÑßÃrÃTÜøï⯌J&nlèÑH$"Gÿ’,™í¤‘”*W² ØñL‚6fVsIä&Î}>ýYÔéŒg‘)ƒê«ItUF)øA ´àÒ¦ñ ¢€A¨ÊS:+2‡€ `TMHi”5k¬&oç¨LjÕ ÝºI  0ÐÀ-\±óúÎý»ËaÛ·s¬ús1ã\p ânÞ/{åZ¾\˱a <\‘oe¦¤1Eà«€¯¬Rîw5vcØG'í Ü·$£S9Ê-nò°ÂÿdÞ\ÑóÜŠ¥ó]]ÕuìÛ‡ã¤ÞÑw)Ñ…Ž/_šN÷gøánØôâ×þF%‹Ú¼¬ä“£æ—@n ¢>s>ÿÊÂèÜççÔl9Ê¢O®z<{[ƒ­ED챟Øz•stFì < œ)ÀÁC/­l»§Ñ{…%(AjÛÒÆÕ𨖽TÖò þðÅOÐ ×OWᇯ»GbÖ°˜ñ|O¾ýjiv@î@ úØB¼ÿ ïxßÛFóȦ©Jýá~ í¾B¾ B€6 `¿‡ÁõAÐvž@Ý‚(*|P3õS‹0<Áˆ  X¿*Pk$| ‰Ãý±‚ij`Ž,Ïþ£ýŠ(<Úp"Ü·•ªPéptVܲBòu/&à³Î·>ÿöð†Îð XVEjýË7QÄc•ÃpÅ&ŽW­hGÒàÑ Æa;vÕ¨dÝqIУ®êH+DΑ¸ZÀ©*ŒåÑP£÷$@Åt‘¬b ƒ#$‘0ÅEª«¡ÄäC.v‰‡„²äê¼6J1Òbª\%UK´õÒ–ŸDå<~É5bn¡K91ιöºê,SϳOsž MÓ9Ó˜,Ц?°YÌq M>Ô¡ƒÀÉÍ9n˜SºO8‘8u–³mv‚§IÞ™9­óôô%{ò M~ÖsŸþüçé*Ði´ ¯´ç@#tP5”Ê|(DsÉЄ‚H¢UÂh,ü¨Ñÿ­£%HCz9!!.ó4’’˜†Î{*d¤`ëP°Ó˜‚fs;©©M‚&—®éK²¦2| mþ©D}F3›à?ï=±P#²WÆ©f°{“VT9öÓ,5OP8c øç½ã€+SÀY79€ûI2’,PCAv•J_=ªëJ>¨T # k@àn’1ŒÀj8C&$õeßZŽ>ØBºò‚ùŒ`¿'€ P¯+²[Í\ë'IÞU>—½øB' …«ëð–´ØÎ¶E¤kÛJ<BÅŠ}©€RëIŠ S=³¸Ÿ /Û§ú wÄM-mAÛÁ#Nwÿ:åqF+…I'¹Ç deYÈ/u·KAz庭ë–¬ë-{hc ”—»³bmü X/à¯%⢺š¨¢à.mŽè[¼€©;šl nTë/Y–󱘣&‹k\ï2Š›Dt﫹]0ÕÑI¬—[ò ÄÙˆízS¿©1k\¹EãF„fU³„bŽ¡áL˜Ã•9îD.;jöcŸ»}pï=ßZydãÕíýî¿5ýôD?£ dŽ~è§Zñl½óúÊCƒ¥ûî9óÄï¿ú­«ÿþƒ›¿:;üà¥Ñ¿»ï=wéõ'Ï>ùðè·Ëú—¿Ïaì¼ýwƒ{çß}çm I?×ë@ôƒ2{FîÙ}ï¹î9ˆÑ²ƒû¾xá‚ôè§ûúqˆ~P»¿¹¼pÏçó{ÆÆ@ _œ¿zõª&¥ŸÎëÇu úAaþ°¿òðW6ž].èžÿ¿ôü§{L£ÒOôcŽ~Pˆ­û§ƒù[ÿù£Rô3Šÿò~SpôÓý˜…£ú<4¸ôú“e¹g—7¾cD?ýÑÑò°·óÁ`á®L·YÇÄÒàK»»»š—~z¢×è™yë•g×NÿU¹îÅ‹ßÿÖÆÆ†æ¥ŸþèÇu úA6žúúÿÕÔ)]?—~ræôéÓš—~ºTñ:žáï÷ôjúA K‹_¸vþLéúÙýù?,..j^úééP ôƒTî¼ã³µDËÑ:;¦yé‡~@?Hþ3”힃ð7¢úý 0úù\%£ŸíWŒ~è‡~@?Hdiq¾’k?¿xŵŸ|ú™;BÏ­ Ù«©»‘uß*:g'úAO}ã±Jî|{ûÇá;ß^{íµÀC£ßöjÙ1ý@Ðè§û¼õÆË•<÷ó÷ßõÜOîÑÏÔ³äÑñÐäðhì=ÁSê²áØØ‚co‹ßÕ±Ÿ“v;¼á5§®mò‡Àé‡~è§dö~·;X¸»äªÿõKK÷«zP¢~bNß“ ÞŸ\6ðž$aäXUê‚Sßh“T¤z½Üñ³ý –•Ç*¹æÛ{ož:uJÃæÖOÒ‡ô˜skøÜs^.¨ŸL› ¿ðAd#¤ê'<¼£ú¡ŸjÙúå/N|å/JýúÕ“?¬âué“oýÔO¾É=ú)Å@ÎNôƒ ,?v²´ïûyãåÑÐg_«¶]?áÁAxÞ,rWcô“úJAý”n g'úAvwwæï.áÛNßsaaÁ·VªŸðõüÔ 'á{&¯$¥^¥™àšz À¤Š&w,p˜©75Äßz0v/ƒÑýÐO­\üÙÏ÷Îï¾÷\þg}.½1|Ù´[Aý4üþæRîxnìãDn¼¦ú™ çúÓÁ}_Ì7{Fî9wîœfl»~’îºNš”búýô~ tñâüîÙXûF†;~ýê[o¼¼°°pþüy رѠú¡ŸúØÝÝ]yüñ_l¿ùÔç{.ÿÇ~ík§Nr½‡~ýÐJàý÷ßIhð¥ûÖž^Ýzóû×~ö£ƒÊ¤7·_ÙýÅ+Ûï¼úâÚw—î¿$žÑ Ç}nô#è‡~P&{{{›››O=õÔ`08vìØ¨ÙGÿ...ž>}zccC]úôC?ýúýìôŸRââùV˜uÓ%®¡à ã?úΪ[‰~ýÐO£õ“ûTXé™4¼Â²ôs°’ܦA? ŸÒô3ùÃäX'u1ùs`C“+OÚVüNNn+^?#-rP¹[‰~ýÐOô>ËÇë'i^.üÛÔQBx'c†51£Ÿ°ŸrT¼ò‹ÈèGÐý´ìÚO¤E2é'ÓÇÿxý$m+¼Û‡KÅõÂc¬øƒ*eÏéGÐýtgôS~ÂC‡˜LVýD^’‰Ù\ä.ÓâÅ÷œ~ýÐOǯýdÒOøF²†ègl©"úÉwPô#èô3 Ï8%Í/%Í,…/á¶x=ÇÔ\̤ÙQýlŠL“oEö¼ É€.áüN?-pRëÖôC?ôô è@?ú¡ýè‡~ô úЀ~ ýè‡~ô úЀ~è@?ú€~ô úЀ~è@?ú¡ýèúÐô ýЀ~ôC?úÐô è@?ú¡ýè‡~ô Ÿ|{ýx¤Ÿá/ !D‚~èG!è‡~tJ!ýÐý!ýÐè]Ìý‘£ÿ-¸¶¬ï,¸QÑϸüÏsúk÷ÏßuÇè´v÷ÇVO¶ßø6ýÐh¥~ÆTD?¢™±¿ýâú_?4X¸ksíñÝ÷ž½rýç?|û…o,ÝwÏ™'¸ù«³ôC?¢Ýú™;˜'&ß3ù†ÀzúIÝ¢#÷¬<øåO~ùüØë£WFã¡‘è‡~D›ô3©IU¤ÎÔ…WVNàîGçÜFãžI÷h4:œ…£ú­ÔÏÜŸ2i”È1ÍäzâßÉ=b,Fã›ÍµÇo¸øão®žÐýˆ¦»'IYF1ói‘îÉq%Iô'æïºãàzOR\ÿùï¾óýÐh~â'Ħ.›iñ˜ŸéG$ÞKýú¡Ñhý$î禑uæ-uJ-0N¢aôC?BáÚýТÇw¾-ÎÿyøÎ·K¯?I?ô#„žû¡úBt¨êÁùŸ8¬zpñÇßTõ€~„¢ŽY¸ÑXçî;Ô|‡Ô|£!„hÌ]Ú !„ úBú¡ŸzõýxŒ~„Âè‡~„‚~è‡~„‚~èG!è‡~¦Ÿ¹¹?‹|1fU“¿H»4¶•FeQÁì§éjkŠzŽ%¾g–{\³íTôC?uë'²#ﯓkȱéÔóNî]ʽ{O:•¶|=MWÏzj;–L+Ïý l†¯'¢Ÿ¶ê'é3fàƒçäÉôèJ¦~¾›šx©Ÿm_)w—­Éí†[,æðóµ|ŽWªøkfúS†×3ÃcIÝP`ÿcö¤ ~è§¹ú™Úѧöæ˜âÏ)IÛMݽRv)¬‡¤sJ¦Ý‹Ÿ2Šoù|ÃÍ*þš™þ”™¤æn¯Ÿ¤N’é¨kîxý1ý4W? 01 3µÇGj#Ó¤GîÓAñ] œ•¦®^U7]|ƒÔO¥ÇØPêtb‘£®§ãÑý´@?ùî;ˆ¹"šé¾ƒÀëåîRøÎˆÈ[$Æ.h‡WRõ}u6]üvc®ÍðXbúaxL“õ¨ëïxn¼¦Ÿ.ëgØÚšo¥ì^õ£<šòh‚~z¤ŸÖÕ|kZ)¶ßÿ¦<ÚPy4A?žûq{kC_y4=MÐý(Z5ƒÓ¢òhzš úBú¡úBúéƒ~J¬Äxä(f‘*v)õ–¦Mjez¨¨3uÕj;–Šj¶«ãÕ9×Z¼Á'¤ŸVV=¨¨·å~D£žâ`-WP¨54]=ë©íX2­¼x‘Y.•uÈc+¡Ÿ¶ê§êšo9î·Ž¼)¹¬2t3)—¯ås?MUi]µðŸ2¼žK¦;ãS;ɰÁÕù,Ðb‘=d- Î…Ôçè§;ú©¡æ[êÓŽùª ‹UèiTɸøšoù ÛTZW-õO™µ8^Ý HYŠvUç‹ÜD|’ø¢s—_‰òŒ~š«ŸÙÖ|ËQk§þ]šaɸ|%vJyÄuXF]µr¤Îc)¨ŸÜG=“ê|ñ›È-§ø¾šû,D?Ý©zPCÍ·Lî5kHɸ*NÙµÕU+ý@ê<–|¶°:_ÖZ•º'w2ùÖ)ý «¯ù–ïcæ°–â`3,—¯åã5Pg]µ"uêf{, ¥N'¶«:_ü&rOÇÑOñ¢‹ôÓVýTZó-þ¾ƒa]ÅÁšP2®ÜûêlºøíÆ\›y¸p?¶¹:_îr‹9î;(>3çÆëž>vÚÀÛC{R¢¦ž¯*× º×÷Š?ÕØ}¦Ÿ¾è§™Ïf¶îi‰;\Åa–û¬_o»A+Úmþ¸ùv{ê{è§Ë£!„Pt‡~èG!è‡~„‚~è‡~„ôC?ô#„ôC?BA?ôSµ~ ?OSôTÇúúúÂÂÂÚÚš¦è¨‰«W¯.--ݼyóøñã;;;  VWW·¶¶F?loo///ïïïk€~€jSΡŠÐP#ëŒM¸LÄݺuKãôTÅæææäíëëëçÎÓ8ývk÷¼ô´¦‹ €~¹I? ôÐ@?ý @/è ô]ýôÈMúAWyíµ×4@?Ѐ~ôC?úÐýè@?Ѐ~€ ê@?@Ó»8úè›ôú@?ýôÐúôR€~ú@?ÐÅÐ@?€Ü¤tUú€~ô úЀ~è@?ú€~ôÌUúA/úÚ‹ úA›ûÓ/‰6†tý€~ý€~èôC?ý€~ý€~èêgîS’þ[OÌý‘Rö!~ñ7J? ÐO§þr qtÆö‡~úAõsôŒ?vŽ1ÄØ;§þvlñ1Rõ“ôæÃÍ ^O@?©[¤Ðè§ýLzeìÌ8YO¾'¼xÀgµF-áU…•ø¡¬±‘tý ïú Ÿ…Ãç߀&Ç7Eô“º¶È} ŒºRßYtý ×ú™:–*•˜K&9Ü“¤‡¬£˜ù´"¢¥Ðô§ÛÊúD^b‰ÔO’É"õ?!–zí*uñ¬GD?  ¨~’©÷Ä_× ÜªUNI;–iV0~¨WéÌý€~Ð÷ÑðØ)è‡~@?ôÐèGÐè‡~@?ôÐèG”¬ŸýýýÍÍM9úýˆúô³½½}âĉµµ59úýˆ:ôsõêÕÕÕÕååå9úA‹õSÖ³)IãÌ6f¾3ñ%´Çž‡ªŸõõõ¥¥¥­­­ýý}9úA‹õSÅÙ™~Šëgê‚£¿Ýüüü7Æþ (‹Ñ˜Ò)‚~0ýL-óíIŸßõ¤S«L=kÇWUˆÜ™ÔJÞ1µR¿s!~=©úY[[~.]º$Çg~¶Õ ôƒüú™zúK*hx¸ vxýñc…˜_¥ø«zbš(µJPê.½ÿðÚÏÊÊÊèsúoû[9N?ôƒÎê'0TŠq@êY5Þ@‘ã¡ÀR9Æp«‘û8ðxOOÞù6?~ÜoôC? Ÿœú‰7PUĺ²G«ŠoØÜú~úÜÏÆÆ†§úAô9nˆ<«¥iúɱxý€~èmÕOä­I—F'ÐÜw„÷!õ;å“c™æ¾R'ߦ^JÝÿÔõ„o=Ðé‡~ÐAýÿâO†VzǶt¦úAwô“Iáa aTý´t¦úA§ô#T¼†&ý€~ýÐý€~ý8ÛjúýÐL?ôúôC?„~@?ôú¡4–7n(ªßjôaú¡´’õõõ………µµ5MÐý &®^½º´´tóæÍãÇ|O3'LúA嬮®nmm~ØÞÞ^^^>ü¶fN˜ôƒªSΡŠ8aÒªbd± ·ƒ‰¸[·niÀ “~P›››“·¬¯¯Ÿ;wNãN˜ôƒ ûyú¡H0@vÐô'²ƒ~ ÁÙA?`€ìÐ ô Èúd‡¡H0@vÐ$ ;èG B—dý@‚²ƒ~ ?ô ÈúþÈ B?`€ì H0@vhúdý@‚²ƒ~´&t @vÐ$ ;èúÙA?`€ì èO€ìÐ ô Èúd‡¡H0@vÐ$ ;èGkB‚²ƒ~ ÁÙA?ПÈúdý@dè ô È B?`€ì H0@vÐÖ„dý@‚²ƒ~ ?ô ÈúþÈôL?·¡ÍÈÕnŸ†ÐÃÜì—~†¼$ÚôÓýOÁúyÿr“~ý@n úÑÅýРúô¹)è§ü.>÷)“/æû3L][ ËæXÁÍÅ/~ôE6J?ôîc™zW¾Ü–Ä™Úñ/ÒO}]|²{ÍäD=ú9ØDñ>G?hˆ~òåoÖܢęºÔØ!^¤ŸFèg¬«ýS%}p˜|ä{êë“ïËû65‹r¬0ЛSÛ$²yé‡~òÍýpjN%¥IÛ‡~ÚªŸ¤nþ<ó7NêôñúIzCd|ˆ‹?ØÀ±¤fNLóÒýL¤vÑpol{âÐO;ºxà3Kü§¤L㱟#{LÒÇ¥À½±­Ä,ÿ‘0Ó˜ojÎÄ'ýÐO¼~R{Wd·luâ„?ËÒOÓõî辕U?Iý>0ù–šSûbä¦lüâñÓôC?%N¾Å÷·ðY¸¥‰“û“1ýÌ ‹§žÁýŠÑOîN_Eå› ÔëA£ô3“ÄÉw]€~š¢ŸÃêÜ£é¬3¶SLá|ŠYa¾9„ø1ý Ü¯“úaÖnÙÆÄ‰ß7^·¦‹ Bn úÑÅuqР]\Ðä¦Ü¤]\ýúÑÅý@nÊMúÑÅéÍ`óSö÷÷å&ýРÔÇ7Μ9sâĉ?üPnÒOõSV‘餧f3ß™øâ¾cøÑOOøè£Nž<ùÌ3ÏÈÍ~æfõSEÐÅ‹wñ© Þ†®#7Û››ôSH?SŽ©‹žô!ßw=¥Ö¯ |¥ÂÔ÷䨕©~bj5øøõT×ÅÛz)¾£ŸG}ôé§Ÿ–›ýÌÍžê'é+ª’ªŽ†¿Ò*\Ì-üçŒù<ó«Ôú=ñïûNL…KÛÅìÒØû{¥Ÿ½½½ÅÅÅÎ_ûY[[{ðÁ¯\¹"7{››ôÕã»xä‹™zyäg®pÅõ¬Ÿ“ ^ÅgiàÀãÏ=ÔÏîîî`0èüoSï|“›ýÉMú™Yïå9ºcü'»¬y«ŠoXúé³~ä¦Ü¤ŸÌ]<ò³Iä_®ø+Mëâ9§Ÿ1>þøã“'OÒÜì|nºõ åšÛ0ø­t‘Ãçøb쑃âpù‚ãëÔ~àË¿cf3’ÖãÖƒ®\¹²¼¼Ü[ýÈÍþä&ý”pÏb£îéì@ÐýÈÍ>ä¦ÇNs~¥Õ0îk:E)O$ôJ?/^|úé§û¬¹Ù“ÜTtG(ºÓ,vvv¶¶¶z®¡èýúÜô£‹ëâ A?ô#èrSnÒ.®‹ƒ~ýèâ‚~ 7å&ýî-Z\Öz˜›=Ò Y£^úЀ~託óçÏïîîjÐýµ²¼¼|ð ýÐPKKK×®]Ó úje0˜|ýÐ@?ý=`qqqooO;€~訕Ûo¿}_;€~èhnNô€~úÚÉÞÞÞââ¢výÐP+Ÿ|òÉææ¦výЀ~ôC?úÐô è@?ú¡ n¶¶¶vvv´è‡~€ZùÞ÷¾wþüyíú¡ VVVV._¾¬@?ôÔÊòòò•+W´è‡~úèè:§Núðõè‡~€ZYZZºvíšvýÐP+ƒÁ`wwW;€~è €~€®sûí·ïïïW½•íK45è˜M¿ï9gŸÿnúfÑïo¼ÓÛ80ÐÚ³Ïè  €~jÕÏÙçVôÐOÝúýûò O0è Ÿºõ3Šƒ1ë@ €~jÕ1è ŸÙèçÐ@gŸ?£c€~ú©O?G dôÐOú1 úèg6ú9¼@?ýÔªc Ð@?³Ñë@ €~f£Ï~ú™~\ýô3ý˜…ýôSIÅëx†¿ßÓg@?ýÔ>Tè úýÐè‡~úè‡~@?@[ô3w„ð‰8õ ôÐA?‘‚)®ŸÜk˜¡ù¤9è¨\?“6J’Óähirþox%á#‡kôúÚ§Ÿ|?L©„ß6õ·ÑOüp~@?@+õ39|IýÕÔARÒÛ¦kèôC?0úy'믊 ’¯Óè‡~Ðeýä˜C›:0*¢Ÿx½Ñèh·~’®äÇLMÞGža OÍn(pëè‡~Ð)ý”{¸¿ÑÜ5ëR_šƒ~€ºõxb4\k'i¼2U?™¶’£¾\d©:úýM™|+XêmrŽkR?‘u¯S‹îd-ËM? `ÆúI=ã—¥ŸÜ’È÷­ ôúš®ŸÀפ~ƒ\ÖïYHýæ…˜ïšË´žL;F?  VýúýôC? úýÐ@?ýÐè‡~Ðý$U{‹±¬j:ôú¡ôtôysséUD›ü…uÒô4W?›¶c ¬Ý0®ð–uCYÇpôújÕO|eøGPc ¬Å<”õáÖIåZIsÐP¡~bêª×Oäl[Ì0+kµ·Ü“{ÒôT¥ŸÕtÊÕOÌâ¥TôÉa iú*ÑO|‰ÏJõ‘©ýÄHšƒ~€ªô3£•zÑ>S­¶˜;¼Ãß÷“©Ú[êqÑè˜Áä› Ð@?ôú¡ÐýHsÐ@?ôú¡tP?EªÝ4¶Ú[Á“æ  ýä¨Pä>fúèô“^`mê{’ê¶Å,SY'þ{µkHªäM?  ‰£ŸÈ¢jáo[È)æqѬ¯¦>èC?  5“o‘ƒ˜|ËÍW‰'r˜E?  5úÉQÃ&uÙH=DV߉÷ý€~úÉ ŸðØ(æËèô4K?1—L³[¥,¸û`²’[L1º©_öC?  Aúôúè‡~@?ôú¡iúè‡~@?ôú)­X\üWÛ•RÈ€~@?€ÑO†ïÛ.ñiúý³ÔOd¶Ézná*©‹Ó@? Ÿ¨gE#« $­0üÈjÖ`éô£_¢õ“o1EØÂ™–2j‰©©C? úAÇõ“õ ~ ê'G­kúý=ÕOü7Tê6úý­ÔOL¶˜kñ¿ Üx^OêÊéô´I?‚~@?ýÐè‡~@?ô#ÍA?ýÐè‡~@?ôбß#ô úЀ~ ý@?úÐýè@?ô ý@?ú€~ô úЀ~Ž, @>ò뀠ýèúÐô è@?Ѐ~ôý:ÅÿôÁ?|²Ü¸IEND®B`‚jbosscache-core-2.2.2.GA/src/main/docbook/images/DataVersions.png0000644000175000017500000001576710660354341024525 0ustar twernertwerner‰PNG  IHDR„Ñ?M¾IDATxÚíÝ]«$Õ¹Àq?F>‚ŸC(f ÂÉ&s$Þ J¶h Ä á€'ƒ;”ƒ"HÄ‹}΀d@/2AÈM®æJr6ÌiÏŽ;;ÝUÏzÖªªî®êߟ™Ý]µê¥ÛõïõöÔyÂ-P€*TØ#U<8 ©‚?Ý*7ª  *7ª    ª€ýRÅÿOç‹oU¾«Ø±ŠÂÅuQªÈª"~1®¦óïŽ[Ý·U ŠÑTqùϵ¦Füg ŠÎÍâ’7[<Áî}[Rª˜\òvïTEç?âöG±À p­ T±mU¿÷ãŸü›ÛÕzЬI¾Û‚*PÅTªˆ¼/& ŸTØ‚*PŘªHv"®Š!‡K^ U ŠAªèŽ»:w ék ÄÃã™Áóx²ïêõ‡®nÈ£G|«PE‹*Ïêª_{íµ[·nùJ Š”*Í«ëýÞ÷¾w|||vvæ+€*Úw^0><::zðà[€*¨¢]O¨‚*"nß¾­ë UDªÀ ]O¨B«"ÂìXTA@T  U¨@ؼ·‹O  ¤?˜/ß;Àð¥¨TAU€*¨  ª  ª¨‚*öQÿö4À\o|ùݪbƒí‹åÜûä×~ñ“ç¯<³ººçž}úøÅ£»ø UTT±V­òã½VUqv÷Ý“_ýüèê•Ó›/ÿãO¿]½òðó·ÿøû_^ûÑßzõ§þòUTÑT±ÙÎXÛ¦ó•ÍI_«bsã¾W6O¬s¯ó?Wžxåg?þúÏ¿[»ŠÕ+«vÆÊTPFPEg¥¼éƒ@0±ŠíƒÎcÅ 9ÿǽOÞXµ'6=qa‹UÛâ¢'Ê—   ´¨¢¯ ÑWe­ŠŒ*:ÿ,ê!8âªÝpzóå Gë³÷_?~ñˆ*ªÀ U o@´ybs÷¼'.JxþÊ3çã}ñðó·Ÿ{öiª¨ªR¿÷+Ô6)jËY+$3ñ÷b_*€*ÐÒªèkFdzŸzâ¨Î­ €*°¥¨ù†± €*@…¸÷É/<ÿƒxÔýš*ªÀáªÂº €*@å¸X­ý黯^¬Öþìý×­Ö¨TÑ‘ê¹gŸ>ϵjgÈP¨BfY€*ü_MTPUÌHk釧žmx‹*ª ŠýUŤÕ÷6çKP&WE_ðµ•Õ}ëÀ;eй6›*ª Š9©"®Ä7;¦ú¶‰ÝS,„*ª Šù©¢³aÑ—8–*PÅÁ©¢ïñÆT€*¨‚*PÅAª"¨ÙƒÁí¾|憵PÅ©ÂdYTAïõ=Îh¦ž  €*0­*$ö@TA¨TAU€*¨  PUTATAUPÅþ|0‹O  hUhUTª  €*¨‚*¨  ª  ª@TA¨‚*¨UPU   U  ÄŒ%x¨ûÈÉÉÉÕ«WoÞ¼éV  tðÕW_]»víÑ£Gׯ_¿ÿ¾€*°Îñññ;wVÿ¸{÷î7ÎÎÎÜT±¦‡ mUà[V†Xët:ïŒúæ›oÜTo9==ÝÊ>99ùøãÝT~”¦®  Lò Z  ß   PªU  PªU  PªU  PÕUøNP-P|¬¨T€*@¨T€*@¨T€*@¨Bâ;@µ@¾nªU  PªU  PªU  PªP§øNP-P…ïP¨U€*P¨U€*P¨UŒt¢À"Q *F=Ñ/ßbaA  ª‚*@T!U€*¨Bª¨bß㉠µöÊXE‰Í¸÷ɯýâ'Ï_yfõí}îÙ§_<ºû‡ßP¨‚*¦RÅš6ÚªïU!â8»ûîɯ~~tõÊéÍ—ÿñ§ß®^yøùÛüý/¯ýè‡o½úÓGy‡*@T1•*.ÿ¹ÖΈÿì+ªÓ@— éST§À:K8L­<ñÊÏ~üõŸ·öúê•U;ce ªUPÅ6TÑ'b£¤Ó +íTËÚâÞ'o¬Ú›ž¸°ÅªmqÑE  ªØR«¢³Ö~ìwVë}•{g¯WpÐÀO«vÃéÍ—ƒ >{ÿõã¨TA“7)’õ{gŸìYJêŠ'Öâù+ÏœOôÅÃÏß~îÙ§©TAÛhRTÕÚI7tŽgE듚ÒÛP¨‚*Fž)¿U쀊åÑÐûikUhU€*¨Bc  ª¢uÔ Ïÿ žõÅG¿¦ PUˆƒë*@T!Dvµö§ï¾z±Zû³÷_·ZTABtä€zîÙ§Ïs@­Úr@*¨B™eAT!U€*¨¢¸ÊaÜröjÍÁvN&y”›Íh1U€*¥Š)jŸQŸ%+ª¨bªú®stqs_«"“~#ÈÉÑY's=ung®-%¾ñµ÷%HïÌSÛÙ, Þ¢ €*và‰¾G>·öê«þŠ…Tý Ž N&>¶+Ú´Eí…Çb˜…-¨TqXªÈl_k…d9™×‹?ºã§5TÙÉk/œ*ª Šl-YUEi%TÝ“)TÑpÿ© Š9©"3V‘¯1‡¿2SU4tÐQ@{:¬Ý÷œ¸ rlÁŽÏ!ؾ8àœ.Î%oˆÌs¿ã] kT1×ɲU#Ò xÀ†3¡ PÅ᪢ªö‰› Ë~“Ó  PÅA«BªUP…TP…TP…TP…TPUª¨¢ùDE¢UhU¡Uª  !¨TABP¨‚*„  €*ÆL.ôäfœ¿¾ý3™îÒF<«æÒ¶°ãÀ{8ÊG@ Šm¨bûutçq‡W:™.o3…*’…W½»“(yPªW?ó7묋WúªÅÎ}ƒ‹ÛdŠê«tÖvßü³x™UGìÛ xÁÙ¼Á‰u^`íŒïgÕW¥øb|uɦ PÅÎT±YÝlÖ°}wî’©X;븠¨`ûâ.ÉËÌ·W2­ü Îè;±Ìá6½R%† 1×w3ó/¶}ôTª˜V}cA Õ9´Ð÷;½¶&Ê´X{f rÄ6U¯¢ïÝäf}JËß´!ªr¸øe>zªUl£UÿØ/ÖÅ›ÂHŽÊf<4S¦óÄX¼É«Ø|·¶Å0°i²'žè+U¬â¿~ÿꤶ Š}lUÈ®ŠdÉIUÄc±r¨‚*¨¢A¶˜¨'Š*öhX{ˆ*’ýûyµ$G ò=Zµ²¤ ª Š*U\ôDMa ªØ¯É²UPµ-Œü¡û ,nç;²bVÙTPU¬Ùbôž(ª‚*¨b9ª˜hÜ‚*„  ªX”*¦·  !¨‚*–¦ŠÑ{¢¨Bª ŠªbÜž(ª‚*¨b™ªÑT!UPÅbU1Ö¸UAT±dUŒ2nACWHŒ[Î^-ØÎÉ$2p³Ã\A’Y¶âYëÿû€*–P“ˆ*Æ-y\ÙPU,¹ ¢U±Ûš´ssqr_«"“7)H¦?Ô(_`2™Gñ”—­Ç¹iã»ÝWZüUPUPÅ<ÑYé·‰÷ê«þŠ…Tý Ž N&>¶+Ú´Eí…Çb84[P… Š¨"³}­’åd^/þ莟\ÔPe'_¬½pª  ª  ªÈÖ’UUäVBÕ=™B ÷Ÿ*VOtqþúfeÚùbÃ6C¶§ ªˆ’r·©bø+3UECUx«b­Êž´¿\8UÖ.kwvýÇ•cóv|ÁöÅçüpqæ(yCdžÝïbX›*bU\42:«øµw“Û¬½Øy ÎW¨ÂdÙ÷zL)^ðDdªØU¬ÕæÿZ ™m†ˆ*±Œ› nOPÅ; :+î¾J|È6—Ô×j¡ !UÌFÉ¡ïâ6™Q…‚*檊Q^¡ ª‚*–©Š w(³M¦SK”‚*,Á³*„  ª  ª‚*¨‚*¦R€½‚*„V…B«¢Nñ0rí8srãíÏwê<"U!¨b¨**ôQ&¿N!ªBPEµ*:WG¯¥ÜþŒ'×§½ù â³ÚÜ¥/uUp¶T!„ Š²*ŠK(2뮓…UïÛ·Â#Î(•låPÅ‚(=yÉ« xÄ|ÉÍ¥ ÙqÈ%PÅ!¨¢3lœ v¸*Š/Ï*³ oƪþ¿èéáÅÖ–0ðã6.¡áb/vé;Ê#æË™ô£?p[PEs†ßþc©bà"ó«"ø}·ùÖæOÚÎWŠû®íµ¶KP9ö•<“ª‹­ý1¾y2'ÖwžÕhçêÜxø7oBß ìÓXþ›@T‘ìÿi¨…©bdUÔ֛ɟÕUIqßdÕ™ùi\C›0òªÈ\~±ÝPlUħ4ʃÏ%Y`ñV²-¨b³‹?ßiÓÙ´VHm{¥8Nž9«x¯àq°û¨Š)êÊÚ“Ú·ÚTÑÖ]Þù;zsœ ø?Ø2ømÞ¦Šª#^þÉ „4رêEª ŠíÇþÑ(ß•ìMÊw[åŸe4'UÈ%Ôžä€ÚíÉ QEÃ!¨bÈdÙ}ÎÉ!”Pr@M›j·'CTAr@É5ƒP»=™‰T‘¡ !”Pr@ÕÍÝÚÕÉ ÖFÝ kS…ÕÚB,²ìè×Hµªè[_¯ynèÈj.Mb!ØbÌ%™–àÕª"^&=0;S¾­ !„Äû¢Š8½RñAÖUÉšŠsa«²B ÌûDBª¨[W‘O¾Tõ”ìæ…u`ÆÊû4cUÌ¥g *ÒbR' ª ŠªõwÅŸüɬPÅfMÛ íÙ¨bFbȯI’òhŸS' ª ŠdêŽdÝOõ1$ÿÇ2UÑ——)žÂäYÚÌïÔY`>ÕO>}ErÝÆ\R'‰e«âìììÁƒÿøŽ¯¿þ:ø¿}µÁ_/qïÞ½Û·oú÷ïßö]mÿá%>øàƒ·Þzë?¾ãÎ;Á¾«ÂolðÒK/]»víèèèôô4Øwu¬§.1P£dgjPÅÀdPËTE2Or9Rœ °9»NÃ’ˆ9¦N‹TÅe¾ÿýï¿ð GßW»kUö+¯¼òæ›o&«û5U|ôÑG·nÝJjfMQçüíoûûßÿ^Ô[~X;PE0âs.º¡¡kHÞ§y䀊ó5d׈‚%—† IÒ Š¹¤N: ,Á³o; TÑ–wa”¼RÍCûŸ:IPUPUlO3M$¨‚*¨b6ª(öØ ÉÈÝ{®6YÓþ§NTATa ^ËÜY5©  ªÈ<Õnÿs˜SÅî—à A‡¦ŠÙ=è‚*„TQ·¯oúi>×ìåLñ»ñŒXªBPÅlVk'¾åWóÕH»ÉÒ|y8wxWRm £w^-&ÍTUf­m~dTq€­ŠªEvm©©b¯s@5Lx^éL:³hIi¦î'UPÅ誨J¼A»™,gjŠgvf2uª"ù(ÖàÁ«ñÖ8uU2›Ó!¤™ªÊ¬UÌ–Ÿ1|hy±¨"Nú]Õ%E;PEqIDðgò‘ôIµ¥¥J.mˬüÈ×M K3•\Ø8dQa2ÃØ‚óbQÅæÈAüt£` {UÖ^WE&TçOãNI¬ý·¸F¯˜”©juÞÀ,™Ë¨Š9¦™j^ߦŠÎ›iÈR…ɲ–àíxµvæç^ßk{3~uN¤Š±Æxçžfj˪˜ †*¨‚*¨b~ªX@š)ª  ª ŠjU4 ÀŽÛמ™aí±2\Bš©™µš‡µ>zª  ª°Ï´K÷ÓdÙƒ˜5|09ÿ »!9B&Ý—*Ônî¤ Š–uUñn§ºjU!¨bKªæËÆç¿ÙŸ2OZ-&>ï–*„TQ­ŠÚñªº¾õzÉÃuî^{ª§Š|²£!™ˆªöÝÂ) A[VEð{?þÉŸÌUlÖÔ®èÒ 6ãPS§´+¢9ªX@T[ݯÍò|T^e‹9©¢¸höqîq§›s‹ù‚>uêS‚*¶<¬½MU 9ÜX±–³®"ŸÄ¢!}S°b`”¼CNIªØÉdÙbâñxp{H/Víày<&(9 ªÍ ÌDÔœŸ|ºS‚*,Á³Z»¥ÂMV¯ÛTÅt§$UPUì^qÊŠ¨ÂX…  ª ŠŠ¥ÅîL› øL›ÃÚSŸ’TAVkïW>‰=yˆ Uª˜£*j×Áe6«]$a ÞÈuqœu¯NIª˜—*¶ãoÄ$TT!„ ŠHñŒØ ùRðbœ *˜¤[\ÛÏëSE3h©BAÙÕÚUÉ—ú^l[ø\y—<íäÑ©¢1MÈNÆâñO ù ¬Z¨bÙ­ŠÓlMªE×ùuyùeçÁÒ¼ùÍ€Úš*VbOqÂU×~ù͉­¨‚*UìvX;Ù¿4®*‚öUTT”m«šãBòK:âZ/þÇð¬´ùcÕ&¸M&KòYT^æqâ‚*¦«†ãÞ›8 T1›Ó¦ªR£×>ë"Î 5¿PÉD¦éÌ"U¥Š 5ÓPßÕ®B¯:è@UŒ’¥/woòcT±'“e3º%x»Iì1ú/ÙLïS›*F=®½Æª›¹U´5UPU4öÝ'Ó7UÕS»UÅãʼëT!¨Bbªˆò>%Ó7­ í6ÌFM&tJö¨$ëîÚÇ.=nzTCb«±†µ3UPUPÅë*Æ­GæX+M4ƒVP…ɲµÏ¥ªöyJT±›jnFõæ(sIyBPE¼¯X/XqwÎ€ŠŸÀJBªØ¯Ä›™”2ÏO3DQUAKSE2}S>mUŒ£Šæ’`džÌÞ™äQ[˧$Uì°U1î‹™ÅÕÆ*Z&ˬŽÛ L&ÅD"¨â0U/öÞòêYª"ùÔbv¦LS#3?5ù0Ô†´Nd$¨bo‡µwÕª Šìtþ†tÅœE[Xži…äORªØídÙâÓ‡2ÃÚÅœNU¹Kq¨¶¼°UÇ®RE¼¼®Võb A£/Á[RÞŽ%U4çtš¢UQ•v*UPUì‹*ª25U…*UP…Ä3SEgÖ 8éPr°ºï Eɾ¯¶am9òUPU, ž  ª  ªØRF&!¨bWªÀ&T!„  LÙJ£ !¨ Š½ë×jëÝÚB–”qûåôàQ¨¢NqŽæj®ùÁ¥ ç0J½®ÉOl™¯Ýšg@ L*ÕÐÏ(08JqHM»ûrM…¤½Sxýt Aè€ÛØÒØ£¾~3Ôõ tu¦[¯Œ},8ýÃé¯@uÀmlQÖ´·oÆ"Ý€ÀñBŒ}, *A 댗Sp[Ú{Ô×oÆ"^:D›çxÅAŒ},ÌXªnc‹:°¦½}3 TqT”1^zjì‹`hP¡aÄË0‚/·±¥=°G}ýf,¨—Aè:ëL·^û"X [¶ÔË+¯\-‹•ë¯?ÃùÃgìÃ1bò ÑmlQÖ´·oÆ"ùz¶o¡¯£Nû"XÜad÷Ü3Aް›ìѧ—Œ>e[¹äK=?|ƾ#ëëœCÀH¶Aº-í=êë7c‘l *èë¨KZߌ},|‹M›jeØúKå½åáK+ä“:õˆÓ…ÿ°ÇpÎýè£[|—C6·Ñ¾ÛØ¢¬ioߌE´:‘›¤¯£¹uÙØÁÂ×€@@ç~}`OÙRß&ráßqξ»ôpÊÀH݃ÄÏÉ0Z·±¥=°G}ýf,’¡[Qúú:êP®þû"XøÌ‘Dö à…öDP–Ó"É3V·±EXÓÞ¾‹äéY®“ò;}õÇK¿Œ},|‹»ï/G«©b2¹2˜Áº ¯ã¾ø²ÛØÒØ£¾~3ñÕ'|}õÇK},| ä x,ÆÄº‰\h(ö;Ê¢f-’e´nc‹:°¦½}3ÉÒ1/'Ô>ú:êN>Ý2öE°ð,pû(îôðZ¨Y,X ,ê@]ùŽûãgÔncK{`úúÍXÄOl±}ú:êN>]4öE°ð%ˆßqÇXuò¶ª¿ËÏX ,nEÅs.ò ÷ÇÏ¨ÝÆ9æAßôIDATu`M{ûf,â§G¶Ø>}u'Ÿ.û"XøÄçÌ9S&±§êïîžuiå8îŸQ»-í=êë7c?=²Åöéë¨;ùtÑØÁ— Nc£±clQÖ´·oõ5Ÿ¾vµŸ¾Žº“OGŒ},| ¦ilÅ[Ú{Ô×oõ5Ÿ¾vµŸ¾Žº“OGŒ},| .h¢±clQÖ´·oõ5Ÿ¾vµŸ¾Žº“OGŒ},| Ü‚…÷‚t÷vSÔÁÛM“e¸ncK{`úúÍX$KÇò9ú öÓ×Qwò镱/‚…/`ŽæCchp^ç6¶¨kÚÛ7cA]õÒÕb÷Ñ×Q¼tÅØÁÂ7°ÉŸ~Ú¡2\=ž»Ô»Cðp>Ò;™Æê6¶´ö¨¯ßŒE2uÍËÙ±¾Žúã¥Wƾ¾:/CçöS PÌ£½qÞ‚2|Ãi2ÕmlQÖ´·oÆ"™ºæåìƒÚG_GÊÕ-c_ _Á @@æï Á𠝧qêצãd*øfÓä©ÛØÒØ£¾~3ÉÕ·\gäwú:ê‘[¿Œ},| t4R…˜‡ÄbL<¦OåÄ´ð‡Ï؇÷‚àœë~N–±º-êÀšööÍX$KÇ¢ôôuÔ%­ƾunϺòʯÈg>³¿ì¶ÛrÒI˯ý]…0­IÞº-í=êë7c‘Ý Ó®èë¨Sƾ·––¹rãß’#ŽØG ØKæÎ=ËYK1oÞYrøá{˧>µ·àó›oÎ L†0 Ûòv,nc‹:°¦½}3ÞcE.¯_èëÊ뷤雱/‚…ïAýœ"çw‚ô鳌u¼<òÈ¥ím ãµ2á¼Ñ£ÍyMM—µÓçpƒu[Ú{Ô×oÆ"þzeƒo ¯£¹õÐØÁ—`î&vd"ðÊD¸ÁBˆ.«³ùÊêó¹—1»-êÀšööÍXÄK‡l²yí¯‰¥¯£¹uÓØÁ¢[`QˆØÝ®?{…>†m©õ¹Ëò³Fî6¶´ö¨¯ßŒ…ºb³ —ê›èëÒ§cƾ%ƒ…&ör2 ]›v,º®~èó¹µ×ˆÝÆu`M{ûf,ìÕ›lYû!ú:êK1ziì‹`Q4Xä{9k"Š ÷ êvwÞy[9÷ÜŽk6Üçñ³Æï6¶´ö¨¯ßŒ…ºb‹ kŸ£×‰Ñ×Q_ŠÑMc_‹‚`Ñb÷ˆrÀB×óÖ[ó¤¶vdû]&7Üp¦ç:}>·v8·±EXÓÞ¾ ;tÃ&¥¯£NtW},<ÁÄÄ«=sóôIDATÝ ÷ ?úè9ÿü…Y ûÛØÒØ£¾~3öëÛÞƒüL_G]ðK¿Œ},ÚÁ"—؃xÆ„_`¡Á+‹ñÆ7´_“>Ûèœ‡ÛØ¢¬ioߌEtú`ƒ-Ò×¥{üƒÒAc_ çNŒ ²^ƒç7X¸ÛÐY Ì‹b-ÆÃ›çg¸ÏãçpŠÛØÒØ£¾~3áê€-6TvÂëúèëÒ§cƾR  ö(ž€¤±iãÖYŒ#ÜG;lOÁZ f1¢3r·±EXÓÞ¾‹èôAÛiX[úºôŒuX:•¯c_) 7±#KQÎjç|ZÌþ0ÀÂ-Çc]î¬Å@ãœsŽc#‚÷“¸-í=êë7c‘ü`C_ÇŒ­;„ñÙØW À"*b÷ȰÁBË€,ÆM7Ř3‡Y Ý7AoÝÆu`M{ûf,’ ôu …¾.:Ý6ö•`°ˆšØ½VT`á–YŒ1c>ëÜQ‚,ÆC]ÂÅžf2ÜÆ–öÀõõ›±ˆÎùºmѯÏôuÞãI_çÝ/~é]n=ƾx?Gk'r;8ßwÀBËööÛ7:YŒ£ŽÚWú÷ßS˜ÅÆÝÆu`M{ûf,‚km[aléëŠCúºâûª;ºkì+!`¡‰}—]¶sž?öÚ‰bÃ&°pËü§?™,ÆÙgbÃÇ †ÛØÒØ£¾~3á8Z·ùõ™¾®{cG_×½þ+¤Çƾb[¶ÔË+¯\-‹•ë¯?C®¹æÎm•‡²‡ °—“©ðz£h¡ŽúX®ÌèxÈëÀ± Û/µ~Mö¸›d¯½v–¯~õÓrõÕUVË\ê5†}¾ÛØ¢¬ioߌEpÎÕýÊõôuþ}ÿ}jì+`#»çž rÔ€Ýd>½dô)ÛÊ%_ê)§ôï!í±•ôÙ¡§yX_ç[‚u>™?{HG~\‡í2¹²·±o§ŸÑç6Ê쇺·±¥=°G}ýf,üwª~èQ>¿A_çïxåö3}?ýkìËr°Ø´©V†}¡¿TØ[¾´B>©«PöÛñûp çàÜ>º%ÒLeöGIýpÔ6Ôá6¶¨kÚÛ7caŸŽÒo„3&qìgüX12û²,ôë{Ê–úŽ0‘ øŽsöÝ¥‡SÊSLGø}NZdîq?û=nAÖç6¶´ö¨¯ßŒE8A¬X½J‹ß ¶KïŠÕÏbÏ3öe)X U…ìÑ " 툠,ê(¶Cü8/®2Ÿ~Ú¡P(Ô§^Ç¢êg?Æ*Ì:ÜÆu`M{ûf,ìqðqõôÏöèP˜þ¬P[ƾ,‹»ï/G«©bã§ôIDAT2¹Ae0-‚u…:Áïc”™†æ¥SncK{`úúÍXØ£«ôáŒEûÙËŸØ¼ÏØ—…`‚ÇÂF¬›È…†b¿£,ê+kA™ÃÍÙl\¹²¹-êÀšööÍX„Ìru!÷;ýF8~#Žýœ«+qønìËB°À­˜¸Ák¡f±`²¨u…1 ”9œ~c,ýnÃmliìQ_¿ ;À‚~#¿Ç~öÛ…QŸ±/ ÁâŽ;ÆÊ¨“·UýP~Æeq‹$žF‡Ræpú9Œ±ô» ·±EXÓÞ¾ ;À‚~#¿Ç~öÛ…QŸ±/ Á–žüÅžªºxÖ¤F‡Ræpú9Œ±ô» ·±¥=°G}ýf,ì úpüFûÙo?F}ƾ¾€G7Ž2‡a~·á6¶¨kÚÛ7cA°(WÏãè7â(s¹ãe9c_‚EÓV”9œ”f”FSnÛncK{`úúÍXØôáø8ös¹þ&Êrƾ,‹8.´¡Ìá,ŠÒhÊmÛmlQÖ´·oÆÂ° ßÇoıŸËõ7Q–3öe!XàÖ ¼¤»·›¢ÔFGSæpú9Œ±ô» ·±¥=°G}ýf,ì úpüFûÙo?F}ƾ, t@fB™ípÖAÐk¯]'«WO/ RÝÆu`M{ûf,¼uõé§§Ëë¯ßPÖ8—«wôÞcQnæ+Ç~Îw-¶î7öe)X€0ñ¨éáêñÜ¥Þ‚Ç€GõHï8ÊŒ¾Âã¹ãÒÏaÕÃ_*gõéÕk+¹êª¯•pÜÆ–öÀõõ›±ðfãí¶ÛZ¾÷½!ò⋳ÊïRõ“¾Î{,JíÇ®Îc?wuM¶7öe)X Ãð"1Š÷Xóhoœƒ‰2Q½á”2‡ã$‚4¨ÖÖZ¹ùæsäÈ#÷qtI ÁbK¨Á »íë±Ì§?c}ÎV[õo~s üéO—ôáø8ös>]µq¿¶Ø©ú\á“‚À{C°æÂëiœúµé8¿¾¡ù¤.0ßB¿Žßˆc?ûí낪OÛŒõ`@ ócXŒ‰Çt㩜x€þðûð^œƒsƒê´Rê¥Ìá8‰RÆÄëܶ¶[ä7¿'§žz˜'LhCqƒ…û3ê,æ;ÎÑ¿¶3çÏø ¿g@%Ìþ@[™öÌ´–û»;c¡Ïuoû÷ßSêêΕ÷ߟˆŸ¡ßÇoıŸ½ü—mû´­Ä,tçApÛîIÆOð‡Ï؇cú<›¶q–ù¶ÛΗ/zýôIDATùÙ}÷åŒ3Ž‘^˜im?—3æÿüçl9∎ÓÚ0¼¶:ccú3Ú-æ;ÎÑÿ2ç_¥¿fËó»î ûugê/,tùƒÚMÖ¯¿F‰L Œ³ßˆ£Nº¯ JOsëÕö+°È½~Æ©¡_ï»ïrÈ!{ȈÇ8ŽsÍøÞÐðƒÀœiãùÖ[óHèÓg;ç:µaxmÝ0QЬº.8¹®ÌXxÛNW ,â0ásòŸÿ\Ÿ(;(EŸ“vnZ|]ãfìËò5atÛ0N¿ÂªªŽ–CÝCþð‡IŽó„² €‹o|c` ¿Ö¢bƒ`øÕ²q|FçÝúV,×ûÛLÇÜeøÙ»/mï—´úº ÇÅØÁ‚ŽBAÃ,Y³†Kß¾ÛËÕWWɇÞÜÞ/, Ø_]]åœ÷ÓŸwÊ©¨a×]0~…÷èê1ŽÏ;zn>öXðw†„­ëim¾Î[÷ýÐc_‹öêGÇÆ±w*ðÿ˜Ý©?Ü`¡¯ç%uz×è‘‚ÅæÒÜØ(MMMÎ_cc“¬Yß’‰Ð­ë¥Éu çàøº–6‘ÇP~µlÜœ ìmªÎ†FY³¡5»#³ikY'jÿÚª¼óo³¬[Ý(K-’¥Ëe}ÇÓE6«ö¤yíÆìù"×6;2hy325Ëksën“õªîeKÉ¢¥+U›Z8U•#_ƒ¬no°MÖ5«kpµÓÞ`‘Œãóv®n°0`/çN­óÜz÷Yœú…¾.Ø14öE°P.)ØÎ¶µ~¯T —¬^`¡ÏÃtIR§GpnÀˆ,ZWgžé¢ Wo—­Û,-Í5žëCVnhËsl–¬s¬ÉÔ9«Éâó†ÆiN}õk@¥¶*³àQ·YQ1NœC8YýkœV™i¿²V2ÌÑ*µÙE’¦ ꨔßÝÛ±îúѹuWÈÒµÙZ²òUŒ^*n´­•Ѩ·¦9Ópÿky´þæn1Æ{íµ“sçîÊ=Îïñô•ôuጛ±/‚EêœR˜ÆðšöðrœP¯ýz_Ò§GpŒgžùqÁ~Ðý‘»5Æ¦Š—ùoí¢ÑN`^¹®E6oV„† L\¼l¬©¯RdžJ£úe¿¹µUZÕ_KK« ßÐ~lƒ Í›7È¢qäUÒ¬b÷Ú¥™:;r…4× U猖5ªÈÆÆYê³*3´FÖ©v›ëQ¦B.nØà\IÛúe™ã8§bš¬Ë&99ZÖÈÅØ_U/ S«*ïª{ýÊ‹3eG/Rø"²¾!ÛÖ¸eŽì™kF½êÚœÙÐàœ_ÓœÍÔ8”öŸ oGûç?_ùsprõ‡ßŠ{‡øôIDAT½Çª˜~¡¯+¿ïŠéßÜsŒ},”g ·ó£lO§1á5íá%”ÅkOä^o±ß±•ÍÙ›e™˺ìΖ5‹œ ;nÙs²ÔùÕ?NV·lv A}ófDøÍÙc³$ƒ"MN`ªÀB×9MÖ›†Ô§©ª‚ùÐzõI}v²£¥=–o^+µÓ¦É¢F”j•ENÛ£¥¶vœ–ö-M2TéNUýšì.¯ºUö£}ö£Ej+uÛ/› QuŒ[ºNZœ¬M¥ä‚P{{E|0c‘›/VO“v}]ø:nì‹`¡ÜQøv›Å¦½ä*,tÙ¤Oèë,vkŒ­ˆÈçyÊz™†_þ꯲²R†ª?§Î¡ÓdZqqö˜nÛÑKת5 kÛUíy«ªmRl*;Ð×Ñöæ5ÎtÃPL7´®–*uþÐÚÕžRµ4ë©•Aذԑ)3}’9]OßÔj*ñ¨»ªCÝ­Y¨©UH“‘¯rÜ,™¸:K–Ö"3“ Bž¢åÝ©û¨Ø±ãyñóôuÑ™±/‚…rBÑ DÐm—š ô’Ê⵿о4Lº~÷1clyã]á¥~ÖJY·¶!³Î@M/`B mÃJçØèE«,´e2jÊù }lèÅõ²²a¥úÃËlî"['2î›×eaV£:/ &ã ¦+š–JýÒÙжAf!» äª¬-UC3°3m¥É¬q¦o†JSvæÂ«îqj*§ýŸšê@†£ââµn3s͘riiÊN‘8Çr@¨½pqÌX$×æÝº—¦ÏôuÑë´±/‚…òHÑH2”“ ô’Ê⵿˜}œYèßLürÏÒ™ÚÕ™y½î¡¦y£llʬµW¯îžhn–fõ‡;0pGÇÆl@^´¶CN©^שË9wo4¯“¿g×=¬ÄêN•5˜…`®Ö[,Uw“4/Ë´…i’ÕË.v®kÚ¢•êù& Ò°rQ&ÓÑžè< £×T8u·éLK¦îÕKìÚB‚CË·ÔY´¡ÏU™˜E*ÓÆñ%Óæ‹±É$žC_g‡>û"X(7eÇ ø%Rxˆ•û!WÝ©»;`¡ÛMóôˆ1¶ò"bfffÁ¥Sƒ ÊÎÔHÕ"irnf2ºl¼W;Ǫ$Ë#_“]„é.S¡êktÖ`Œ“µzæúF—ÍLàܪiKeƒZg1ÀÑae³,”…ªÃÁ-#²Ù–3 7ó×]9ºFVgoqÍ\³:7ËDk—b G…¸§Z:\P‘_ôõj½ä6Þ¾¾Î®ñ3öE°P.É®Á)Ww*±r?äªÜ:QÊÒ†§=ôIDATòºlZ§GŒ±ý¬;­Í¹Ó¤ÕYê¿pÎ$Õ+­‹dؼ¶­´méëìÔ_c_ å{ì¤RäÒÛ.ånbë÷ ,t{îé¤0õþ¤n±å†9~»ÌXÄßæ“j/]]}½ºkì‹`ëÀ¦SxH•~·GW†Yêq¿ÁB·y1]ƒw“à:ôþ¤m±…FÙ^n˜±°×9'Mÿýºú:ûuÖØÁ"–-¨T — ,ЦGðn<¬+‰ïÁ5cË süv˜±°ßI{Ùb÷Ñ×ÅGW},bA¦½W`¡ÛKòôˆ1¶°Ã(ÛËí3ñqÖÚFÒ¸¥¯‹—žû"XÄ,ÂHz9¯0ÀB·›Äécl¹aŽßÃî3ñrØÚ>Ò²¥¯‹§~û"XXa¦½W˜`öõôÈ®»nï¼Ê×ï%W\öc ;Œ²½Ü0cOÇ/WNúº¤ø:‚…ÕA+ìT —C,´ ÿüçl1â9øàÝ%Îw˜`–æø=ì0cA°ÐvfË–¾.I¾Ž`a%XD• ôr2Q…–åÿ÷‡±¾{ij°Ã(ÛËí3 m_QoéëŒ.&Ç×,¬‹¨S^N&j°€Lqž1Á,7Ìñ{Ø=`ÆÂ8s/ç¾àû‡¾Î»“áëÖ€… ©@/‡jXh¹â8=b‚YØa”íåö€ o§®õŒÛ`û‡¾®ëþ·¯#XD6¥½ªM`¡å‹SÊгÜ0Çïa÷€‹®»Ö5nýë+úºÒû2ž¾Ž`Xؘ ôr¢6‚äŒKÊг°Ã(ÛËí3¥;x/Ûà¾âú‘¾®¸~ʧOñóu‹HÀ©@<Î:ˆw{äSÎr÷Û úzlOš`–æø=ì0cÑ=G¯uÛ®û‘¾®ë>*Vâãë‚Å–-õòÊ+WËâÅcåúëÏpžËpì±ûËþû÷µöÊ\™áŒ!?®ÇŠ5‚0σ\¿üå²çž;É‘Gîãô³-2›`ve{¹=`ÆÂ?g¦žÛÜV®ß˜5k¸Ð×ù¯gñðu‹@%ÿž{&ÈQv“=úô’ѧl+—|©§ îßCŽ> —ì¾sO9ò°¾Î98ׇ‘OæÏÒÑ×a»Ì眴œ|h9Eõ3úÜ™M0Ë süv˜±ðßáÛ`ÃQÈÏoÐ×ù«c¹ýl·¯#XøÔ7mª•a_è/•ö–‡/­Oê*”½wüÃ>Ã98÷£nñ]ŽRœ eö× ¸ûÞ³°Ã(ÛËí3Á·{ì“þ™~#=ŠK?û"XøÐsåÚ#ôIDATèܯì)[ê;ÂD.\à;ÎÙw—N(Oލ™ûQæ¢ÇÊ[n˜ã÷°{ÀŒE8! {«ÍrüF}e.ÞVŒ},Š],RUÈ>@½ ¢Ð>€Ê¢Ž®Úñó8Ú;ý´C PH>¯c”¹8ƒ3Æve{¹=`Æ¢¸±óÓÖ’TW\}]ýsœd6öE°ð-ß}÷x9ZMm“©È Ô(ƒi¬ËÓQæàŒ1¶Ü0Çïa÷€‹àÇ=L;»-úpô'nýlì‹`áK Áca#ÖMäBC±ßQu„•µ Ìád‡Œ±…FÙ^n˜±'0„ðÃh~#¿Ç~6öE°ð,p+&î@ðZ¨Y,X ,ê@]a8ÊN?cË süv˜± X”ëcè7Âñqìgc_ _‚øwŒ•Q'o«lµüŒÊâI<¡\£/¥e§Ÿ±…FÙ^n˜± X”â+ÜçÒo„ã7âØÏƾ¾ñ9sΔÉ_ì©ì¯{`g]àAZnCê3e§Ÿ±å†9~»ÌX,Êõ+ôáø8ö³±/‚…/A<ŽJ@™ÃqÆØÂ£l/·ÌX,¥ÿä¿Âvcì‹`á XÄ1mE™ÃIicË süv˜±(ì Ë ºi(G¿Žßˆc?û"Xøq\hC™ÃY„eŒ-ì0Êör{ÀŒÁ¢\¢ßÇoıŸ},| Ü„÷‚t÷vSÔºÊ5úRÊQæpúÙ[n˜ã÷°{ÀŒÁ¢_á>—~#¿Ç~6öE°ð-ˆÇía&p”9øcŒ-ì0Êör{ÀŒEðãîÆIûL¿ŽþÄ­Ÿ},| &=\=ž»Ô»Cðð¨évñxnÊŒ³0Æ–æø=ì0cÌX' ò]O\}]ýsœd6öE°ð ,`„x‘:ïÞ(æÑÞ8Ae¢zÃ)e6Èc ;Œ²½Ü0cì˜ç ÈIÚO¿ŽÅ©Ÿ},| 8(ï Á𠝧qêצãd  cÞ ‚spnØÁ«=ÊŒ“0Æve{¹=`Æ"˜±ö²«¤ï£ßG—âÐÏÆ¾u(nÂ=Éx þðûpÌF§C™ýuÆØrÿ‡Ýf,ücí8l™è7ÂÑ)›ûÙØÁÂÊà¶S`{Á9cla‡Q¶—Ûf,‚oÚû6­:`ì‹`A°PëBÒja\·1¶Ü0œêxÜôIDATÇïa÷€ ê|ºÏ6Ò¥gƾ ª‹@uÀ[Øa”íåö€‹t9|xŽw:`ì«°X¿z²,]4F-$k7ÖupÆm-ÕÒØ0YÖlè|wƒW9}þº–l=mó¤¹q’4¯›'αÆÉÒÔ4¥ý¯±qªll£r„¡lÃ_=3Æ–æø=ì0cQxŒ½|–¶ í»èë ÷¡î/nÓÓOƾŠ‹Ò0k€ó¬]Û¥k D4Íê—9^u¡lnÿœ¿\ëê‘Îù5Íó”oQß:U†ª:+k¦Š>æn Ÿ›ZÓ3@4ÆäŒµÖã°ƒ(ÛëÜf,òéW~Ÿ¥m’¾._ßq¿Ö‘´n}mëÇg ¡r¸¬WYƒõ+‡e¾O›œ‚Í3d´ ü™JûIÆL¢P¹Ö5£œókWgádót©Ru ­.úج†jÙÜ:OZZ2míÀBN«âÆñº±utÜn˜±ðö!…|–£{ôuŸO_Ì~ðÐc_E€ÅšE'8°hí‚lgΓ•õ#eYÓLçû†ÆáÎñ¡Um•‚a¡rjgKÛæÒºaŠ(ÛñX­´¶âO·ííâp(s:ÆÒ›2 þ‹´ÌXxë^!Ÿ{¥¯óî7ú2ö tÀØW`±Ö‹²f³WçÕÊ¢*d+†É™'µ•ø|š¬S™Bå4‹¾.vvGÈȯãAõ±¯"Á"#H]f!åæŽÏ°èZÈrË…ß1]_ eb•¦ÆØ¢¬i—ÀŒEWcX®Ï*·\Wòð8ýŽý:`ì«$°°ÿ¨|#ÛtÀ[ÚÃzô×oÆ‚vb›Pžøë¤±/‚…òvñP^ƒ½chŒ-úÀšv ÌXØ«/´eŽM\uÀØÁ‚`A° Tç9luôIDATŒ±¥=¬Gýf,¼â¼(·½ºkì‹`hP¡Øka1¶èkÚ%0cA½ KÿÙNztÍØÁ‚`ÁŒE :`Œ-ía=úë7c‘gÏÀαKŒ}, *a (Û±×yc‹>°¦]3öê m™cW0öE° X0c¨cK{XþúÍX0xÅ5xQn{uרÁ"РB#°×ÂclÑÖ´K`Æ‚z–þ³ôèš±/‚Á‚‹@uÀ[ÚÃzô×oÆ"=Ξc–û"XTÂP¶c¯ó0Æ}`M»f,ìÕÚ2Ç&®:`ì‹`A°`Æ"P0Æ–ö°ýõ›±`ðŠkð¢Üöê®±/‚E A…F`¯„56ÆØ¢¬i—ÀŒõ2,ýg;éÑ5c_ ‚3ê€1¶´‡õè¯ßŒEzœ=;Ç:,0öå ½“[õ*x¼žìu ú°J hÓôkÔàu@„`Áàécð¤Ñæ7Zÿ¢íêg~ýdß°oüÒ,lsvúâ>U!üc”ªZlÓkÊ-TØØÿZWKÕqžO¿Ðúc¥nÛ&”î,§Ðúc›^S‚E®h]-GÏY†þQëO®^Ùð½Â!Ü2è΢áÐpÊÑ­?nâguu@ëj9zÎ2ôZ¬ÔmÛ„ÒEáᔣZlÓkÊC¸ÉÕ­«åè9ËÐ?jýÉÕ+¾§>cQ}{…L¾µB¦¸þð}^vÇì%ráU2¦ºBfÞŸQæºU™ºLg«ê˜óXgEÏ=oªªkÞªÎuè¶Q×u¼®©B¦*f«­Û¸ëË-£Ï«Y^!㕬c”Ì“U{u9kUœ:æWÈŒSw¾>¸שÎudÊ~ž©¶º­éJÞé+Ì÷®ÚÖå‚ÜÚll6ÏSu÷fƒ´Ú?[Ãì¹ÙïcÕùwe>S¿æ¨sGª` '1Q•qsîycF˜rúØpÔ¡‚4ÚtÚ@dë©Æ«>¯2íò*9¦"³¢þNPŸ!‡S½û'e³ùú@Ë1]•ןQÎÕ'Õç fŠj; 1îë â3äÃ%}¤ôIDAT¡¿Ž‚ýéj] ¼êÌgçíöK_'ôuþèyêÁBàõ `Q› €3ÔT ÌrÜ'^¡¦Ô…²£²Çæ¨)œÛ)ÄQT€vû¹rcöØU¶NüZï,èúó…W >î2óTã¸èkªÉÊ9תäèëÇ6·´¹Æ†²5Ùk ê(¦mw;A~†løc ôÇA°ƒëG­«AÚƒWݹvN_g|:}?úN°È‚D®±Í̂ŠôsS\€È Ù '1# ú|}ž;Øëv®Í‚…v.z‹ùN¯r¨Sï×çê-ÊLWßyU6aäˆ 6AÁŽú›œÍ¤8m«sjÔù#Õ¯œïž¾Ñ²iÑí¹mä¤L¹Áj HœÓ¶î“ ·ºOýqìÇàúQëjÐ6‘[®Ó×ßJ_ç¾,T…áåÛ •€áO´BöœÉ 6&-1J8Píñ5j Deð ^Ÿ§·:0R¡÷@@W0rÚ‡úu–£NÁþpžW9÷~¯23«3õ_‘©c’ú>Jɇ66ujÚk"ð]g,œöUFË–ÛZ·±\.–©å]µ­ëc ™ðÇ€èƒ`?×ZWð w¹vN_g|.}?úN°ÈBÃ(d)Ë~oÄjÿd“² ÑORpÝÀà6\ýYŸ7X•™z» ôj Îd¸k:EÃ]¸+k<ÚË©6qwˆsL•×Ó'^eªrUÝ \0]3s‰‚•µ@{#ÔgU9WÕ§Ú™¢`È9_A‚^Ä™ÛZ·±MW`¶ -õçL§tѶî0¶  ˆþ8öcpý¨u5 »p·‘kçôuÆçÒ×ù£ï‹,HŒÁ¯ðA,`ˆó–w\ìˆ …{ª¢X°ÐÛ5*{ êÖAÛ} Ÿ‘]ð<¦€!ßô‰žjY ¦k¹¢6,¶]ÀŽKßk]ÊVÜíæóYú/‚cùÊiÿB_œ_Ôúc£¾,”qhãqo =?ß9OÝ%â<ž[U °~ïF¡rúöÍ‘* ¢ë¨Êâ±Øú˜×{@ô±örY™ëV˜» ðÔO´­Ÿ/ÑåûA²èÒï;)õ=&N9ÕÕ*ÛëÇ5¸û/ªÏ6›€2E@ZW£²Ýn!ŸåœC_' èëz”â+yÀbÆU™€éõ®[¾÷n*§¡ý] ™‡Tá!SŽ)PèêÝ!ú‰ynk²Ï—Ð’ONÝ^·ßc¢¦FGú-§ÚYEµÕκ#à¹Ñ×4÷½ÖÕ¨lE·[Ègáœ|>¤P9í_èë‚ûÁ¥õÇF"Xä‹BÏχ±É>ˆªF}v¿w£P9mlZ!ôÖ zŸÞºÈ¥a!×!´g'”,º }n>9sÏk¿¦AùÄå–EËçlû«)™åÁ¾Þb¶Z.2`Ü: uµ½òœB> íæó!…Êiÿ¢¯Qoéëüó“ºOÝ:eËg‚…2/£ÕÙ¯w…´?S®']ŽP  •ÓÆ6\M µV땱¸=#O1ïÑév˨ç6Gªz É©eq?=´”÷˜ ›«Ön¨§nB¹ÇzçéG¯¾ rŸÍÆf‹ÑS;GëjöPLÝ…|V!R¨œö/ôuÞñ¥˜qéê­?6Ú3Á"O@l7(“sÞ¢º×{7nR°à¼ìË£œ66MX¨àb àÄ5âõ]npλCnVk*åRY†‰ $¦+`A}؇E˜…ä¼!»ÆB·Wê{L4L›ik.º2„0Ž;ý¡®ßFc£Lvt[ÆAëjvQ¨ ú:µà]ù`¼ÏÉëMôu¥Û-Á"XÀ=ŸŸ_Ä{7<Ë©ú4 hEX`è@ ú˜v6z ñ<Ö?óÞ§-õYŸ_¡€f:ÞýÑ…œßÿ™«Œ’åKyIû5¨v†eÛÇsö 9°0Žé~°%xPŽÒRZúLëjvÑUž>« ‚w y–£¯ Åjý±Ñ^ÀBc¹ÏÏ/·œn·”í×bÏRÊ~³£pôIDAT%í\›ÍF@™¢­«6Ù`¹>«Ürå\;}]æœÖm˜`QX”£ü,MöÂfc³ÑP&‚}U4¾ª»ýn³¯#X,BIÛu׈Š-o³±1ˆGÄmì{­«Åê6Ï‹'5nZ¬ÔmÛ„ÒÔ`°Þd§ÖÛôšò*ru@ë*}R²}RPã«õ'W¯løÎŒ3ÌXð%d%=UÏÇ•t`*ð°Þd‹Öm`A° X,è€ €d@Pã«õ‡`Q„ñêÎ j0Xo²XëÆF™8âÖ­«ôIÉöIA¯Ö·NÙò™ f,˜±(xm1XÊ‘8Ñ!¨ÀÃz“ ,Zlô  ‚Á‚`Á©t@@² ¨ñÕúC°(Âxug5¬7ÙF¬õÇFc£LÉÉ6ø1–ZWé“’í“‚_­?~è¢ßuX›±ÐÆmçGo³Oºî¿ …õ üÖÚq×vÌ>êºüÖK?ê#Xdß“AîZãÔG~ë L©q²'Êj¯ RGË­Û:°(÷B’TnôèQwä3äÞ½·þðßÿ~mï$]/¯…œ:N€/ƒOóòw'ôÙÇ>þ¸­'u#~ºA°(bÝGØŠýá‡ô<ø”&/c5êÜ%aËÃöâgØ3ŽY\t>-××í¼óNï¼òÊËÆå(gG{#XXPÒ7Þx}·þý}1×àV­j>~ݺ—¡"wTdöûƒ:/Ð~lÒ¤ܘëç–,¹cÇ3^ãé/‚…¥`AzñÅúï¶[ß7´Ñ 4è ìŸ?ÿ¦‹°}àûOs&?Ç×9v»´è€ö[ÚmÞüþ¶ÇÜ*í瘕¿-,, 8š¦¦Gë9È\Š_¹rÅpœ³xñ/Ïûä“·Â_Zœ¯3þ·c˜Ž1Ô¾ ~ c®ý–{üáÛxÀ+ï¼óöÎîcü?=!XX0ª;îX¶@°ˆXÀ žþ¹Ã»2,òÆÿÝ©Fd:ZZÞÜ5ŒtUÇLj9V+[tþ~þ~þ¨Ø:/½ôâ¡¶\åèžM,b¥*úë¯oÜý_ÿzµ_]Ý-ðkáå—ÿ~P©uðüîûý—€Ÿ¿ßÿI˵ó:;Û9Á"¡`¡•ýƒ6oƒ{ÁaðØ×ØøÀçõ1n;û„}B(^´?¹å–›¿?œlß ôIDATÃþ+¾ÿ’ÚW‹„ƒE®â®Xñÿª°oÑ¢Û.À– >éru„ß©ùt`Ë–OœÖiÿ¡ýI¾ó¹?ºD°HXhCöÙgŽÆç¹so˜ŒUØo¿ýV}ŒÛt:Ž;Ç=ŸÀ?ÀOÀ_àœ?ÿùÙÊ|çr?õˆ`‘R°ÐƇÅU÷Üs÷7ÿùÏ¿÷Â\©>Î-u :?¿ÿ?Á[AÓ© ¥ú‚EÊÁ­0o½Õ²Ë /üí°_ýêγÿøÇúýÝÇù™N…:|€ÝÃþáàà8îÉw?ǘ`A°pæLÝJ…[ÆÞ{ïÝío½õgßÁþ‡zðT÷q~¦“¡$O´ã¹°Þªž¼1Ën ‹N`‘«|z–^°ÅŸt8¹:ÂïñÓ .ČߘÅÅ΋.ÁB+ó3Ï<=Ÿkj®½ó¯o¾ùF_}Œ[:)ê@Î-uÀ.€}ÂNa¯°[Ø/×NØ5FI²‚Á¢d°pÀ¦M­;®^ýÔ±÷Ý÷ûa¯½¶a ¤­`+M¾#a°ð㮵 ú ½†~_ýõN­÷]—Žû^}Æ}I°%‚ÁÂZ°ÐnsË–-òñÇË}÷Ý'Ï=÷œ¬Y³FÞ|óM}8[:Ë$8ËÒ¯!¿jB¡ÇÐgè5ô;­¡­µôIDAT}ÿh¥ëT<úŒ`A°°,Üøå—_–ÆÆFgûä“Oº[ú9Î ©N.ºëꬎÐW·þ¦(t¿Ð.¢ÓÍ`ûž`A°ˆXh—„-ÒÇ÷ß¿´µµÉÒ¥KCÈnØ÷/X#NªsŠÿuIûÔôz }…Þòz€v‘Ô> XP¹‹6Y×Ü$MMÒäúklZ--YϺ~u£,]´H-mµÛœ½m-뜌Ê 3ÑÔ¼F6vዟ~úi§luuµ³µkžš4©4ßuebŠh}Ôú™Qûðì"Óž­ÿÓ.òéOÜ÷,‚E‹ÔTTHE§¿JinÝ, ³†v:¶tm«´®®í´¿¢âbY—ᎂ^òõ×_wŽ_sÍ5ÎóØÑg1è@ãî(‹‘·‰þå/kŽÂ¹×\sõ4( 7à†oŽ1X÷í¢½Šã9 ‚E€`!ÒÚÒ¢þÖɬJCkd]K«´¨¿¶õË2ðP9KÖ+`X¿rZæû´Fi]Sï|žÖ°N6·¬•ÚÑ€“JY¹¾‹´…Ëqj˜X¾|¹lذAV­Z%ï¼óŽëŒ0?ÒÆÑ9+ó;ï¼½óªUÍÇoØð¯}—/_v†)—_Ç¢²‹üEq„vat%Y}A° X wÕ*‹ªõÒšõ_kvàaÑZ -²²¾V–5­m‹úì±³œskšõJiNPßÒ÷ÄOȳÏ>+ýë_CÎb$Ëi$Õ–r]ÈNüõ¯9òÙgŸ9ú‰'V÷üóÏÞùn]éi´vÑ•tÁ§]”¢sq:—`A°ˆ,Ö:`Q%k4W¸¼˜ÎXT ­’ÑUzºdœ¬ÑTâ:·Ô›6m’G}T0e²lÙ²R‹—y>hœœb>Yõ1—-ûÍ™xØ£þñäM›ZwÌw~×ÊÒ,¢²‹®e â ÚE~݉wß,‘€Åšú*gz£a£vX›¥Q-âlX½¾=cQ5«Þ þ+šeƒP¡[ÂS%È^àßìÙ³­÷|¸s¨›ÿÅÛI$Õù{]ú‰˜™u[z<óÌÓ5d®£+µé QÛEWû{œvQXâÛ? ‚E(`QŸ3Ò¶¡A*±¨³j–4ª– õãœéŽÊÚæv°¨÷JgøëÙœÚô‚O½‚_‡MÅ×A$Õñs]˜æÀyúÛÈRSΜӕµŠÍvÑ•ôÝ?N»0º’¬¾ X,B‹EX€éZc§Ô²v¥ŒvÝ1R5k™sªž ©÷cî£ï§~çwÊ»ï¾+=ô¼ÿþû%ÔïÔd9¤:C\×ûï¿·ÝC=xê»ïnÚáÎ;—žƒ}Åe'¼Æ8Ÿ>èý*¨L*<ôIDATc»ÐÒú¿õê3îK‚},!€E!—Ô&­­êÓÍEÜKZ¨ŸaªdõêÕò·¿ýÍY“±nݺnD7öé¾6‚Á‚`€߸q£¬]»Vžzê)yðÁÆ¡}ôáÖtrÁ9[ô/úù©§žüÌÚµÏjãÆÿî} X‰ª28}ˆ~ìÓ}m ‚Á"@gýñÇ«[V×xë­–]n¼qÞáðü¿û NL÷#úý‹~Æ‚L{‚J€Š•ˆªÓ©·öègpýO° X,wÒÖ õ“õ‚Â48?¯Q÷›îGݯ~¶áO]+VÌ.°ù3~”¯Ü~$X,»çŽJ/ \´è¶ `¸¿ûݽ_ÕûÊ5䤗Cÿ Ÿpºßìï³À+æ t´‹¤ëpš®`A° Xîž ;Ц¦GãÒ¿þõ]߯o®ÇÈôúý~Aÿ ŸâåœW¬˜7PØ.â5Ö¼÷x,‹ÀÝsqNgÆíûÏþãøüO¬:î½÷ÞÝÞm¬iùŒëÆõ£Ðè—x^{àŠóг‹xŽ}º¯`A° XîžKs2X€øÊ+/ˆ Z__7O†|ûí·ú$ÙÁbñ%®׋ëÆõÛµ³´1ÌŒUàŠóÊéS–‰ƒ X,»çòáo¼¾î5×\= E¿Ê;Î¥õõÌž}ÍT\'®·˜rñ8'pÅŠyåÛE<Æ?½×G° X,wÏÝw0z¡âm·ýb,œê½÷þökqv®Z~}=úúâ|Me\±bÞ@÷í¢sŸ³Nú„`ª@Ç6ôIDATA° Xîžýwv=ôà©p Ì­­ïì„ÇWÛàPòÉù § -¾ó“±?pÅŠyþÛE2ô&þýB° X,wÏÁ9Šücýþ˜>¸ûîå#ž{î¯G¼óÎÛ;Ûä\!ä‚|òÚ$_°²®X1o 8»v\)wWýK° X,wÏÁ;"Üš‰»'þú׿y×]¿ùþûïmÕ‚O´‹ö!ä\é¼…6pÅŠyÁÛEWǃ‚Á‚`¸{Æxó9E<ê·lΙsý¥8çÏ~¶2ß¹ØÿÒK/Zè¸ûØóÏ?w¸û»û³ní¢}ýÈm÷9éú¸bżpí"]ºmß,‹ÀÝs4F®Dêu +W®žë\±ö¡_¿}ÿõØcž”{,÷ûw,½Ï>{¿–»žC׫ÛÑíæ–Oß÷À+æ DcéÓÃðû™`A° XîžÃ7l/ç‰NaÿÂ…õã05@X²äŽQrà¼Rh}žzÙ»÷ÖâÜÛo_t>Ê£Ô÷À÷ŸæÕ÷®X1oÀ» žú? ‚Á"p÷ì¿ávǾüòßÂdŽ?þ¸U€üuî¯z_|ñ…þ»íÖ÷ }ÞAôw”G=^çsŸïÀ+æ è~â6i6C° X,wÏv:ÎU«š×° ·È`¸îäèßÿÐõq½EY÷yüì5Æ+VÌðê3îK‚-,© ¹Íd*ØåõC× æq?pñ ]ëP<ûˆ`A° Xd§`Ë °ií·®ƒBà‘9æ Ä3hv=î¼.‚Á"µ`ñÔBþ±JÕ R]˜˜ÇýÀÅgîZ‡âÙG ‚Á‚€AÀ*A~G<ƒfRaÀÏë"X,%•RÝòüäeD ?ƒpë"X, f,JЂÁ"‰0àç5,‹‚Šw¢MVLm’º)Mr«ë¯nÊjy0[÷½Ó¥zÌ"¹êÂY>§Í 䫿­“ºÉNg;uÜ¿ ÿ/|¯:´-2ÉóÎ’J¹½v³Ì>Ôy•HØVÏl•¦éµöWT\,+:ðüuh°¸uxe¦®KÛƒûS ó—ÓmOšª:®¿¶Y)¹úhÎ#W…ÜZÛ1È®š½hø.nôIDATRúç\wÿa ²JÕ§ëw_3>£Ž|Ç&LZíÇ‹åÞ올ª^ê\×Qkò–+\çz¤t”_÷]©[}M]¿pRëá‹®u(ž}D° X,º *XÎkQëdB?uËæ€Y1¯UT«f/Ëü~³ä^ ÷Nœ–ù>¬QšfÔ;ŸÇNZ'Í[+—€Û=+eîìÍ‚a¡:œ ¸` kð•2¿&›)¢í˦g3 VËUÇ ‘«Ûåš¹j[Ôudþ î üàÔ ]5ònÌ\{ÅPSª?²×æU‡>æ\÷‚VY19SÚ¾wüÅNŸ\¥À mÝ5j´Ó'usʯÓ-³Ÿ ~N<ƒfRaÀÏë"X,9³üàÓ*W XÔKS¶Î»Æ 0VH&ø"0·ÈÜQµR£¦ t€½Â Ì"÷OžåœÛžE(¢ÈÚ-7h`•S~ˆ ÐØ_LÛ“&¯—U 6KSM“(«åÊS™•Züu„Ô¯å;¦AVÔl”jºgþäÕΔI¡:ô±v¨™×ì´lÉSóšœÌÉ |V}5  –ÍÂèr^récžufû±üqíT ‚…ŸA8‰u, ßOg°Xî€E•Üå±vBÊU2l ž.'w©_üî X¨Ž§fÛ¬˜& *_†@œN)T®½íöLÊUˆ,tÍlkÚ׋Ù6J͈qN¹Ì9•2öÂæÌTH6cáU‡gÛýFË\'K±YjœÌÊúÌnp¦ZÆNʬ¹ð,W‘‘ËóX{ûÓÈ_Þ~}M]¿pRëaÆ¢kŠg,‹Áâ®QÈ"¨é •ÊÏ´ÍR§:Ο¾¾=30dx½ÔŒ_&s'5KCT L¡:VÕ˜uƒÚá¤B&MÙX°œÄTÆâ1'cÑØ9c1u£’¹MVÕáOËo¶÷Ïl–ùSÔÚuÎý³×JÍðLƤºZe@²`1É£}lìÄÕrë¨ ˜ ‰ E¦n 4ëFªdI¶Ot¹rêÔuû±%Xø:ñ šI…?¯‹`A° XøWäL…¬ªÉüê®8Kêf¬‘ùÙ@Ú_R(¯˜ÑyšÁ Õ±dd\.›¨î.Á&“9€PqÂR5½ÑuÛíSk,FŒ\)·«»]pgHÝä&µn${I¶¿–ɀĹceEõ¹jX&ë‚5úÚ¼êÐÇôuÏ–Yx:vRv¡¥{ÍÈi+ úC—+«N߯XÚ34];b¿pRë!Xt­Cñì#‚Á‚`á[ÐQÓHã»ÖX >8s¥kq¥šn¾Ì™VÐòŠouƒEÞ:êÖɹ•îKôIDAT˜Ê8a‘ëN‘[»P†Jº­µè¶X`èQf…þe®·—MÏÞA¢û«nƒTËÀ…>gÂø5NæA_›Þ¯·¨Ck¿nuc)œÑÙìD›Ôe魯Lßµ¢ÚÔåt]z[\&Ó’Û·¥~×ív’ ~]W<ƒf×ãÎë"X,:PºU¿â±rAÇ_ý¥µrë(·\qÁx•º³×–{×Hi×V\[6ÔI° X. ÃÁ‚`A°(â0mÚq`A° X,|œ…;‘Jæ—£ §âÄ(£}`¦õ§k»GŸãÛ ýj×:Ï>bÆ‚ À+^®Mmû‚vÆDëO×A!^v¾´ñ š];¯‹`A° Xp*¤ýVÏ8ö¨e$Xø… ÀI…‚Á‚`A° X”  ‚ERÀ¯ë"X,%•¨-³ýè§o ¿pRë!X, f,JЂÁ"©@à×u,‹‚ 3Ñg ¢‚Á¯œÔz ‚Á‚‹t€`A°H*øu] ‚Á¢„ õ¯e¶}Æ„`A°ð+'µ‚Á‚`A°`Æ¢ X,’ ~]Á‚`A°(!¨0c}Æ ê1 X,ü ÀI­‡`A°,´#æV½‰o#åû ð+܇WE°`ö!'Õ »"RÂu x(l…6Mª?äu1cA`ò˜ ;-TDÔœÿØÔ_uÀØWa;´ï(03 À>à¤Ra—eÁ‚`Eð[Œ}¶CûŽ&Õòº˜± 0ùL…]–q| *~ÖG2öUØí;ÊÌŒ°8©†TØeÇÇ @ ø­ƾ Û¡}G“êy]ÌX˜|¦Â.Ë8>¿ƒ ë£Nû*l‡öefÆ‚؇œTC*첌ãc PüÖc_…íо£Iõ‡¼.f,L>Sa—eƒŠßA…õQ§Œ}¶CûŽ23cÁìCNª!vYÆñ1ø [¶ÔË+¯\-‹•ë¯?ÃùÃgìÃ1¿Ûc}öé°±¯ÂvhßѤúC^3¦ÀÉ8>ûœr\% áž{&ÈQv“=úô’ѧl+—|©§ó‡ÏØwäa}sÉÖ;c_ hIÍÄíº ‚EÌØ´iS­ ûB©<°·<|i…|R§ÌxaÇ?ìÃ1œƒs?úèf/b6ÎÅB/Á‚@ex,‹É×ö”-õa".ðçôÛ¥‡S@Rl°âyñÉr,®¬žôIDAT òÀ¹uJ–}áƒU÷‚¦4N?íP¼ ¢Ð>€2œéÞبà ‚…u>ß6(OòŒÄ8¾ä9õ0ÍÝw—£ÕÔF1™Š\È@L‹`]F˜2³­àuÞØWò|ãA<Ç”S!Ì žA1Ž/x'›Ô@†LcbÝD.4ûeQ³ÉÒCc_ñ B„‡äÁ‚`A°ˆÁ Ü>Š;=¼j (‹:PWR,×E°H^`Ž;l,‹€ÅwŒ•Q'o[v¶BÃnEÅs.Ò€“zÍ ‚…m B° X,bsæœ)“¿Ø³Û`g]àAZI ²i¼.‚Á‚`Á@x ·NÉxWH·9Á"Yë"ü ‚ÁÂ:Ÿo›@”'yFbƒC¹…S!Ô|ºcì+y¾ƒñ žcÊ©fPÏ ÇÇà/8tµŸ‹7©;ùtÄØW<ƒá!yãF° X,b°Æ·ˆâ½ ݽÝuðvÓdA Á"y9î°E° X,bøµÊd% òe JÝO° XØ" ‚Á"&`LËÇsëÛG‹Ýî«ÞÂGz'L ‚yàÜ:%ã]!ݾ+DÿŠÅ‹ÄHðb±bís"(Ã7œ,ló ”'™PÄŒA'pÐ1¿¨’éØuÐk @ÀËÈðÞ¬¹ðz§~m:ÎA¦‚o6M®îûJf"|Äo\  ‚EL¦BÜà‚i¬¹ÀbL<¦OåÄ´ð‡Ï؇÷‚à.ÖL.T@'ñ ¼I‡%‚Á‚`C°ÐhÀ­¨W^ùùÌgö—ÝvÛAN:é`ùõ¯¿K ˆñ¸êñ-fK° XØ* ‚Á"¦¨¥e®Üxã·äˆ#ö‘ö’¹sÏr~½Î›w–~øÞò©Oí-øüæ›s}[ãQL ã9áfH ‚yàÜ:%ãâM_ûÿ8EÎ;ïéÓg;5êxyä‘KÛëGÑçmÎkjº¬ý˜>‡Ûp! ˆþ&X,¬óù¶ Dy’g$ÆñÅ߉Š©Ó@&Ù ¯L„,t½º¬Îbä+«Ïç6^zjì+y¾ƒñ žcÊ©fPÏ Ç/‡mC€-”ð’Ï ,Üç•ZŸ»,?Û©¿Æ¾â„É7‚Á‚`aÙ‹îdº º ¬Ï(”Ñçsk'T`\É Ìq‡-‚Á‚`a XäfÊYQ,X¸AA·»óÎÛʹçv\³á>Ÿí„ ‚ÁÂ6!X,‚…Îøµþ¡°ÐÀðÖ[ó¤¶vdû]&7Üp¦ç:}>·v€Áà-)zôIDAT‚`A°` <[§d¼+¤ÓÝÈqÇFwÀ >:EÎ?ÿDaÃxpMîg‚ÁÂ:Ÿo›@”'yFbŸýN:×iûù=7;Ä3&ü }Ý^YŒ7Þ¸¡(éó¹ _Ç}%Ïw0ÄsL9 JàãøÂwº6º ²^׿7X¸ÛÐY ê’»Ÿ }&X,l‚Á‚`ÑÍ`UvÂ+Ø„ Zd1nºi¤è,Æœ9Ìbè¾ zK° X,ÈäÖ)YBï ‰:;á°¢ ·,ÈbŒóYçŽd1zè.öì&¼ºû7÷3Á‚`aÏ·M Ê“<#1Ž/ºyè\g\îw¼Ÿ#еÅÊkXhYß~ûF'‹qÔQûJÿþ{ ³Á迱¯äùƃxŽ)§B˜A <ƒb_0ŽU² ·:;±Ë.Û9ÏŸ{íD±×fX¸eþÓŸLãì³1‹ácÃØW<ƒá!yãF° X,²N~Ë–zyå•«eñâ±rýõgÈ5×|ù­òCöör2^ouа?çÊŒ ùq8¶<]µ§³¸›d¯½v–¯~õÓrõÕUVËÜÕ5E}œ`‘¼ÀwØ"X,RÀ÷Ü3Aް›ìѧ—Œ>e[¹äK=å”þ=ä =¶’>;ô”#ëëœcK°Î'ógéáÈë°]æ/Wö–#öíáô3úÜF™£††bÚ'X,l‚Á"Õ`±iS­ ûB©<°·<|i…|R§LbaÇ?ìÃ1œƒs?úè–H3”9¾SjÅ€B©ç, òÀ¹uJfé]!8å¯ì)[ê;ÂD.\à;Îé·K§ ‚{©ÀóÓ"ó¾÷³cV ‚…u>ß6(OòŒÄ8>{~ib*áôÓu@Á " íˆ sö´H\eF_ õ©×±¨ú9, ð«c_ÉóŒñSN…0ƒxÅ8>{Àâî»ÇËÑjj£˜LEnÐCL‹`]†_Á¡˜z(³=úSÌx…uޱ¯x!ÂCòÆ`A°HXà—?6bÝD.4ûeQGXY Êlß.aCWí,’˜ã[ ‚EêÀ·b⯅šÅ‚Ê¢ÔÕ•ã÷ã8e§Ÿý«°ë X,l‚Á"u`qÇceÔÉÛß+{ôIDAT*ÿ_~Æeq‹$žF ¡Ìáôscéw ‚Á‚<ð@n’YvW-=ù‹=» xÖ¤åw ðª2‡ÓÏ^}oû>‚ÁÂ:Ÿo›@”'yFbŸ‹ï¤Ã ÒqìgÛ!ÂK>c_ÉóŒñSN…0ƒxÅ8>;À‚Ó áL+ıŸ½·íûŒ}Å3’7n ‚EêÀ‚ !ÃYÇ~¶"¼ä#X$/0Ƕ‹ÔnÝÄ{Aº{»)êóvSÊlGÆË+¸G¹`A°° D‹Ô‚6NŽc?G å´M° X,ÈäÖ)™ew…Ày#Ó€GMã±Ñ¥ÞvŠÇSGõHï8ÊŒG§Q?—Ü£,C° XXçómˆò$ÏHŒã çWr±N/ƒlx±X1öÆ9(ÕNÓ"3€/Ê~.V‡l8ÏØWò|ãA<Ç”S!Ì žA1ŽÏ.°@P à5Þ‚5^OãÔ¯MÇ987ª7›ê –™‘™‰ºŸuÛ¾5öÏ DxHÞ¸,©  L‹`-Gâ1Ýx*' …?|Æ>¼焵X³«`F™íƒÔ®Æ,¨ã‹äæ¸ÃÁ‚`‘z°ÐÁ·Hâù x¸þðûl -«ÞÆYæÛn;_¾üå#d÷Ýw”3Î8F^xa¦µý¬ûÛÆ-Á‚`aˆ, •µ°1`$Y¦ûîûrÈ2bÄ1ÎZŠo~s ó½¡á‹õ‘`A° X0È­S2 ï IrжùÚÖ¯¿FªªŽ–CÝCþð‡ID 0Bf@`ãß(8Ïæë°I6‚ÁÂ:Ÿo›@”'yFb36¤0eùàƒ2kÖpéÛw{¹úê*ùðÛÛÁAƒäÁþêê*缟þt¸ \˜rƱ-c_ÉóŒñSN…0ƒxÅ8>‚EWwevO{üã³;‚,t[8Ó#ÅÙ‹±¯x!ÂCòÆ`A° X”8§­ƒ·…Ÿ×´‡WŸy…>Ó%œ)ÜÏ‹äæ¸ÃÁ‚`A° XtÊ"èÀ^ÎÓ˜Æðšöðª¯Xà|N,âhÓ&?Á‚`A° XøzÚÓ^Ó倅.ÃéoÀ`Æ‚ ÛÀ…`A° X,º ÅN{hHpo»ÊX¸ÏÅgNt ‚Á‚`Á@x ·NÉx»i·wnpµå{©Ó^r— ¨ƒÓ#. ë|¾mQžä‰q|Æzî‹Wÿ”3íá5Æå€…®‡Ó# Œeú0y¾ƒñ žcÊ©fPÏ ,â :hçDpì­ôIDATÛbÚ±r?ä*ß¹ÅìïXèúÓ<=bì+žAˆð¼q#X,\cQÔT{Ú±r?äJør¶~€ÚMëôÁ"y9î°E° X,]‚…~Üv)w{ ~…nÏ==‚é½?©[‚ÁÂ6!X,‹¼ÁWO{à!UúÝ~h¿ÁBËy1]ƒw“à:ôþ¤m  ‚yàÜ:%ã]!± jAM{xõ Àmazï&Áú’úî‚ÁÂ:Ÿo›@”'yFb_²1zÉ$ì rÚë‚ Ý^’§GŒ}%Ïw0ÄsL9 Jàãø:ÐÙ¸ cÚÃëºÃ Ýn§GŒ}Å3’7n ‚Á"åk,œöÐÞ½ ,ЮžÙu×íW¹ãúÝòÄí3Á"y9î°E° X,R aO{xí°ÁBËðÏΖ#Ž‘ƒÞ]â|÷Á‚`aˆ,‹‚ETÓ:¨»·Q…–áÿ÷‡±¾{„`A° X0È­S2ÞbMª=êiÌÝÛ¨Á²Äyz„`A°°ÎçÛ&åIž‘ÇÇÅ›î€ög¦=¼®Ù°ÐrÅqzÄØWò|ãA<Ç”S!Ì žA1Ž`¡X˜[›¦=¼®Û&°ÐòÅizÄØW<ƒá!yãF° X,ºÆÂÆi¸Ý[ÁòÅez„`‘¼ÀwØ"X,  L{àqÖA¼Ûà ~|¶,ôµÙ>=B° XØ" ‚Á"Æ`±eK½¼òÊÕ²xñX¹þú3œç2{ìþ²ÿþ}­½…2WfFÈëÀ1ÐmÚB®_þòÙsÏäÈ#÷qúÙ™  ‚yàÜ:%ã]!¾Kº{î™ G ØMöèÓKFŸ²­\ò¥ž2¸9ú€^²ûÎ=åÈÃú:çØ¬óÉüÙCz8òã:l—ùœ“¶‘“í!§¨~FŸÛ 3Á‚`aÏ·M Ê“<#1Ž‹7ýø¾iS­ ûB©<°·<|i…|R§ ;þaŽáœûÑG·ø7¥\ eN÷}%Ïw0ÄsL9 Jàãø‚s®¥¹8Ÿ @@~}`OÙRß&ráßqN¿]z8eÜ£¸ördÞ—2=Vƾâ„É7‚Á‚`“5˜J8ý´CPð‚ˆBû"È\„=-‚öÐ.@¡|^Ç(sq N°H^`Ž;l,‹˜€ÅÝw—£ÕÔF1™ŠÜ@2˜ÁºŒ0³”¹88èΘ,¶Á‚`A°ˆXà—?6bÝD.4ûeQGXY ÊîTÞ/ôIDATÎ. ‚Á‚<ð@n’ñ®ng p+&î@ðZ¨Y,X ,ê@]Ýù…\lYÊN?,Öù|Û¢<É3ãø‚O ôâvÞwŒ•Q'o«Ä.?c²¸EÏ_ãú)s8ýlì+y¾ƒñ žcÊ©fPÏ ÇG°(7 Ï™s¦LþbÏnƒžui•+G)å(s8ýlì+žAˆð¼q#X,1XcÁ NŽc?,’˜ã[ ‚Á"`Ái…p¦âØÏ ‚…m B° X,b\ÎBÈ8ö3Á‚`A°` <[§d¼+¤Ûkpë&Þ ÒÝÛMQG˜·›Ræà×,Öù|Û¢<É3ãø‚w²¥,.ŒÛ¹|ØT8ú·~6ö•<ßÁxÏ1åT3(gPŒã '0Ä Š•™<º.õ¶ScÞ ‚sÂZ¬©!"ß–2 ùú/ß~f,˜±° ` ‚E ÁBkÜ"‰ç/àáNøÃgì³(´¬zK™ý ‚Á‚`Á@x ·NÉx»i` ÷t°æÖß`§þ$X,¬óù¶ Dy’g$Æñ¥×ùÇ)PQÖx驱¯äùƃxŽ)§B˜A <ƒb_¼6,Ç+:`ì+žAˆð¼q#X,¬‹õ«'ËÒEcdÑÒI²vc]‡éƒ¶–jil˜,k6ÔvØÇïUNŸ¿®%[OÛ°4}œt\e%X$/0Ƕ ‚Á‚`c X,l‚Á"4°Ð[õ*øì‚VnÙ~é€mÁ…ò¤x ‚ƒ,Àm à¶ÊA° X,"‹ö ¬ídh'eƒh?Ìu@‰àª aŠš~¨|Ű ¹IC‡G9ÌÛ3 .PνÆb°ªª è¸w_à]n°’w‡8ÇÔ97ß›‹ŠA*c ¾OWç;r¨:±³œ7dáA·7JM (Wõ··W@ 9“Ôš”Ú‹¨`ÂÝ.dÁŸ­Nr|¢Ö‚Á‚`X XÍ[ÞqæÀ ™[2OCðRá¾Dßy1ùþ<åT}:`ë ¼P¬k¨€AÓQo!žÇTvSŽŒê³>‡éJ†…*ÛQHÎïÿÌU&ŒGÔ¨,‡KÎö:³Çݲ´_ƒjgX¶ýéÙ5!î@ög-sÔΛí lÕ‚Á‚`¡]ØÁ)·=,¢¬U»¿«ïå–ëª^¯ã¸“D/öô:ž–} t[º-r, À"-A9 ×I° XØÀm•ƒ`A° X,JΔ$ʽ‚ÁÂÖ€n‹\ ‚Á‚`A°(A [¸­r,‹‚J¹¿rY®ôõD>¿ ôIDAT#¶öÁ‚`ak@·E.‚Á‚`A°`Æ¢ X,l à¶ÊA° X,J*¶þЦ\áeD [º-r, ‚3%èÁ‚`aK·U‚Á‚`QBPaf ¼Ì€­}M° XØÐm‘‹`A° X,˜±(A [¸­r,‹‚Š­¿¢)Wx™‚ÁÂÖ€n‹\ ‚Á‚`ÁŒE :@° XØÀm•ƒ`A° X”T˜/3`k_,¶t[ä"X,R :@pÛù-¤ì“®ûÄ'N9:¶éÁ‚`A°È¾²›Á´ë`Ê>2}d›3§< [t€`A°HXØb|¶Ê±eË'=Ž9fàÓo½Õ²‹­2R.Qꀽ:@° X,¨tࡇ<™‰«®šñ:o{7džcc«,T:[•r…çDÇŒ9ÿv€EŸ>;¿Í¬ExýNg_'E ‚u ]Þ}wÓ;î¸Ã&€³ tI t¼Žpu™`Á ÒTh|áŸý½hÑmh¨À–Y ê„zJ™ìÖK‚Á‚`Ah×!C>÷°,ð™k-ìvâ ²Ût€`Á ÒTlSNÊ®Ã|ùå¿Ô£GÅ–\°`Ö"Üq Þ³¿ã® ‚Á‚:àèÀ“O>1Ù üéL…þþÌ3OŒ»³£ü ØÔpt€`Á B° tÒ€p8N˜ýÌ~Nš,T:•¤)9¯§tÇM°(½Ï¨gì3ê@F ‚u “,$$©åêÁ‚A¥SP)W™X.9Žˆ`‘œ±¤]r,ÃÖ‚Á‚`Aè¤ £°ƒÛKŽÎ,T:xr ¼Ü±$XPÊÕ–£î, ê@' X08¨åêÁ‚A¥SP)W™X.9Žˆ`‘œ±¤]r,ÃÖ‚Á‚`Aè¤ £°ƒÛKŽÎ,T:xr ¼Ü±$XPÊÕ–£î, ê@' X08¨åêÁ‚A¥SP)W™X.9Žˆ`‘œ±¤]r,ÃÖ‚Á‚`Aè¤ £°ƒÛKŽÎ,T:xr ¼Ü±$XPÊÕ–£î, ê@' X08¨åêÁ‚A¥SP)W™X.9Žˆ`‘œ±¤]r,ÃÖ‚Á‚`Aè¤ £°ƒÛKŽÎ,T:xr ¼Ü±$XPÊÕ–£î, ê@' X08¨åêÁ‚A¥SP)W™X.9Žˆ`‘œ±¤]r,ÃÖ‚Á‚`Aè¤ £˜ËÓôIDAT°ƒÛKŽÎ,T:xr ¼Ü±$XPÊÕ–£î, ê@' X08¨åêÁ‚A¥SP)W™X.9Žˆ`‘œ±¤]r,ÃÖ‚Á‚`Aè¤ £°ƒÛKŽÎ,T:xr ¼Ü±$XPÊÕ–£î, ê@' X08¨åêÁ‚A¥SP)W™X.9Žˆ`‘œ±¤]r,ÃÖ‚Á‚`Aè¤ £°ƒÛKŽÎ,T:xr ¼Ü±$XPÊÕ–£î, ê@' X08¨åêÁ‚A¥SP)W™X.9Žˆ`‘œ±¤]r,ÃÖ‚Á‚`Aè¤ £°ƒÛKŽÎ,T:xr ¼Ü±$XPÊÕ–£î, ê@' X08¨åêÁ‚A¥SP)W™X.9Žˆ`‘œ±¤]r,ÃÖ‚Á‚`Aè¤ £°ƒÛKŽÎ,T:xr ¼Ü±$XPÊÕ–£î, ê@' X08¨åêÁ‚A¥SP)W™X.9Žˆ`‘œ±¤]r,ÃÖ‚Á‚`Aè¤ £°ƒÛKŽÎ,T:xr ¼Ü±$XPÊÕ–£î, ê@' X08¨åêÁ‚A¥SP)W™X.9Žˆ`‘œ±¤]r,ÃÖ‚Á‚`Aè¤ £°ƒÛKŽÎ,T:xr ¼Ü±$XPÊÕ–£î, ê@' X08¨åêÁ‚A¥SP)W™X.9Žˆ`‘œ±¤]r,ÃÖ‚Á‚`Aè¤ £°ƒÛKŽÎ,T:xr ¼Ü±$XPÊÕ–£î, ê@' X08¨åêÁ‚A¥SP)W™X.9Žˆ`‘œ±¤]r,ÃÖ‚Á‚`Aè¤ £°ƒÛKŽÎ,T:xr ¼Ü±$XPÊÕ–£î, ê@' X08¨åêÁ‚A¥SP)W™X.9Žˆ`‘œ±¤]r,ÃÖ‚Á‚`Apt`õê§Ž½êª?Áß!CÒŸ±]³æÏŸÛ9±=Dê@¶éÁ‚®‡£3ôIDAT`A° tÐL},0-b›Ã¢< ¢Ôûu€`Á Ò!¨Ðh»o´mmõzç·wÆbÈuë^:ä/YsÔO¬:îá‡ÒÐpßé÷Üs÷7ï¼sé9·Ýö‹±·Üró÷æÎ½aòìÙ×Lý‰ºcÚ´©×\rÉä¾ÿý‰ ¾ûÝïüì‚ Æ,:÷Üs–žyæˆÿúÊaÃN¿ï _8íþ!C>÷ðI'}ö1dŽ=ö˜Õ••ŸþóGþÜa‡õáàƒúûðÊÿüÏ~ÿì×oßí½÷^ÿÞcÝ7î¶[ß7vÙ¥Ï[;ï¼Ó;;ì°ý»Ûm·íûÛn»Íæm¶éýmn½u¯zõêÙÖ³çVm ,¶¨ŒÅ'ÈZè©|Þj«Ÿ¨ãã<ü¡ Ê¢Ô…:Q7Ú@[hmCÈ™ d„¬²ãŽ;nи&\®׊k>ãŒËÏ9çì;ÇŒ9ÿvô úfòä‹çNú£Ù¸cåšk®žvà s.¹ùæñ‹Ÿ{éÒ%çÞ}÷ò¿ÿýï¾òàƒC‘uùóŸŸ­|ñÅúÿë_¯ökiys×?ü 7u½ûºÎ>dzéÁ‚`A°ÈêÀ»ïnÚáŸÿüÇÿàÖʦ¦Gÿö·+¿¾xñ/Ï›?ÿ¦‹®¾ºúŠË/ŸR3aÂø:ü’GÀ:ôÔ úÌ“‡þ©ç0wÝu—¯»*t`æ¶ãÝ&Q÷ ´ï¾ûlèßÿÐ9fàÓƒŸÒô•¯ ûý·¾uÖ¯/¼ðÛ?À^?ÿù­.[ö›3ï¿ÿÿ¾ðä“O zé¥}ã×wûøã¶ž^ΕûtÓª ‚EbÁYƒ¿ýmíd ~ý뻾@¸òÊÏ?~\=~ã—ñ‘GñWüšözlÕS¶ÝagÙy·½e÷~˾‡%qœô?æsrĉ_–§~C}élùì×.Ï1AN;çb9ý‚©òµï^%ßøþ5ræÅsäì)óeÔ eÌU‹äÂê¥2îÚe2ñ†•òƒÚûäâ›ï—K>,S~þ¨L½}•L_²Z®üÕŸåªß<'³î~AªWü]®þí+2ûwÿ”kïû—\÷‡Ëõÿ÷_¹á7dÞCoÉ¿#7ýñ]™ÿèû²à±Í²àOÈÍ(·4¤þÚœ¿ºUKÝŸHý“[dáSâüá3öá˜>ePu .Ô‰ºÑÚB›sîßèÈPÓ°Á‘ ²AFÈ ™!;®×rù/s® ׈kÅ5¯Y®úàN¹à'·Ëèé?“³/_ úèùæE³åkã~¢únš|aÔ%2ä̉rÒðoËñ§Ÿ+Ç !Gž4LûÌ©àL¾ôIDATrÐQ'H¿C+eÏÿé/»ìÑO¶ÛqéÙ«w‡[j» 9[úôÙùíƒ:ðed]N?ýË £Gºãâ‹8Ù  cÿñõû3[BàH:p,± Ü©ðê«ÿÜ©n¤¿g̸ræùçŸ÷ËÏ~h#RìÛo¿Ý{¥‹­·ÙÎ <Cž"Ÿ>åkr°ÑrêYßw‚×7/ºVÎz‹Œu‡|oÎ ™\×(Ó~ù„üdÙóNÀœÛø¦du æ6$¶ö鯇ß gú’§å²[›ä¢/ß¹æ.9ïÇ·:à;íÜÉ´óù3äSÇ&ûê3²Ç~‡Èö;÷U‹\{´O«s˜":úèÊg‘èb* Óc?þ§_}ãîI<¼¾dÃÁ‚`a=X¼ùæ}ÿïÿþ÷‹×^;ûG˜†8ðèg0—ß•( cpÈÑ' Â3¿çüÂŒ»ö7rIýƒ2ã®5ê×ôkίm[ å²PÉ™û`‹Ê¬SY—fL.øÉ/ÌӗοÜÉL!{ò?Ž‘>»ï#=¶ÚªKÁúÜêûíoýÅ7Îûá#<ü¹M›Zwd@Nv@NÊø,ÖÅG}¸õ}÷ý~ØE}>÷)€ðtÄøµˆ)L+ ~îÔ:'}X€£g@¶; §u|)Á¦~Æ_w·œ5y®3vô*5eóié½íöžÓ4[mµÕÇXr饗ÌyàûOãÚB†­ B° XXHcdß¾}ßpg#zõÞfËAGèÌ¡šV¯ÖüQÍÝ¿NpÈ®}Hk€Nêu#ríï_uÖÒ|ëÒåäª lGÏ^½:ö{ìñßéÓ¯¨FFÏÖC¹Ò ? ‚…`ñÇ?6²ë®»¾© Qõ½jùÑ¢ÇE…I "¼.fUŠÕ,ŽÅ¢Ý¯~g†Zãql{Vc·Ýv{ý±Ç=‰Aã¼Í}XŒ>v÷­—6Ê”NØ!X,R¥:rL½ (c`ä¢'ÈN #‚§ƒâESxý9æÎñFTÀžŒˆ×tã+²$Å¥ÅÍ"=,¸õ§Oðþ @î0Â+Ô±Æ@÷™/|K­ÅùŽ+xûY—ÌsÖäL¸þç%uXß(Âkàm]¬u„AØ,xô›ÔÜ~lDÝz©SÐQÛ„?@ 3AôÁ"ÁÛfh#X,‰xeü¤qd‚P¥ «nVS!:Ð{€ÅU‹LVCC,tPs¶ýGËÄå "²ÐÑáXEMv݆n“[ݯÝÝê~¶9ÐP¶tÁÁ‚`A°h°É vs–7ˤ[רlD›Ì¹w­ŒŸPå@Æ…+t¡3XL¿ çTʤûMŸèµ#o7`1lîj™rEZ^Öìd%4XŒ¸}£úÞ&u«ðgêén eùŽ}I°HWÐŽ¤,‹„ƒÅÌ«2 qÚUêùkdÌØ¡XLjhËNO´Ê¨œ©,øtÖ ‹eâíê)™u‹œ©ŽŠŠq2ó1µ6#›•uWN&Í,ô†Å›Ùcƒ/[©ž+¡ï iRë7t{#A¡{ýA° XØ ‚Á"á`±pÕ¹pl.t^“¹»#ÔUÆbXÇ5Ø?oùJ9ÁY虹¸bÈ,™~4<Œº+»ÆbÕ¦m –ôIDAT:æœ;Z¦ÜžYØ©ÛÒ[¬Í Dt"¼úO÷¯mÁ…ò¤x ‚EÒÁ"{}z!¥Y[Q\[ÀÅ—VÁ"½ÜVx#X,) ¯_»ÜW\ÙÜO ‚…m€A° X,Vÿ"·9¨Û Á‚`A°` ·"[§ˆÙ'@Úà¨)Cüŧi  ëü¹mQžt‰vŽi ¼VŒ: m‡¾3¾ÓÆqçT3(VdP´sôÃѲì4逶 eJ'ì, ®±à‹ëÁ"ÁÛfh#X,1*iúeÎkõÎD,¶AÁ‚`A° X0cc X, äVrë‘w…0¸Ç8¸G™M!X,¬óç¶ DyÒi$Ú9Fé Ù¶wªýbw¿hÛ¡ïL§ï´qÜ9 ŠíÄìbûÆGÛŽ†2¥v ‚S𜆉±,Ò¼m†6‚Á‚`ã Â ‚}„°Ç„`A°° 2 ‚Á‚‹ëÁ‚`A°` ·"[§ˆ¼+„Á=ÆÁ=ì,…»=‚ÁÂ:n›@”'F¢£Ûaò3ÓüÔ®u@Û}g:}§ãΩfP¬È hçÈ@Òu a±Ü: mÇÆC™Ò ; ‚…`qÐA¾ ùù³'ÉuxÓœ t¡×þþU9õ[ ìæàƒú;ƒx:ƒ¸ãN° XXMM Þe—]Zà$·êÙsË‘' “QW,”Ù¿ûLÆý땟“͘uÏ‹2ò²›äSÇ&=¶Új ì¥oß¾o<úèO¶1ÀP¦tÂÁ‚`aXÀýç?ÿÞë?¸è¦í·ßþ]8Lý·{¿ƒå„a£åì)óåò_<&7ýñ]Âa#ñ:pÃoÈäºF9ã‡×˱§)}vß·Ý&`;í´Ó;—\2ù†×_߸;x:¸­ãN° XXÚH6mjÝñŽ;>ë¬3Ó§OŸ·4`¸¶[•ƒ¿._:oŠœ÷ãŸË¥?{D®½ï_Rÿä–Äf%’“•¸ùñeÖÝ/Ènjp2§žõ}'±ón{w€­û{î¹ÇGuÇo~óë³Þÿ½í´ÍpK°°I ëÀÂm Ÿ|òñVO?½ú˜ æÿüóÏûå‘Gñמ={¶iG›»íÕ{[Ùë€rĉ_’S¾ñ]>a–Œ¹j‘\|óýrÕož“y½Eð`¶#¸¥¹ÍÝi‹Ÿ”ïÍY!g_¾@N¿`ªú¹rÈÑ'Ë.{î'=zôp¦3rõß·ÛnÛ÷ úÌ“ãÆ}wáÏ~ ˆãôIDATë…Ï?ÿÜánÛàg„­:@° XX ^†óÁ›·yöÙgŽþÕ¯î<{ÆŒ+gž}öÈ_wÜ 'vÝ5³FÃËI»÷m½Ív²Û¾ÉAG(Oý† 1^¾ráÕ/ÆZ¹°úNùá‚ÿ“éKžvÖwpÚ%9Ùîfz ×ÿßå'Ëž—)?ÿ£ çýøVùÆ÷¯‘Óά€a”~¥ߡŸ–vݳ 4¸ôqK¿~ûþkÈÏ=üíoýŵ×ÎþÑŠÿ¯ê…þv ÚKÿ¹@a»,±‹BFõöÛoõùóŸŸ­\¹rÅpd9~ô£Ë¯5êÜ%§ž:ä¡ÃëÿÂŽ;î°ÉåÔ=Ó͹Ç{öê-HMï}àárpåIrÔÉ_q~u9s¢ {…|ó¢kåÜ©·È·ºD¾?ï^¹táà LV;)nÜáRûH«Ô=ñI(¿’»<“XÓcó}Ï‚êëäÇw>ë€Ájï“ï\s—Œžþ39óâ9òÕïÌPw%ýP>ûµ1pö™Se¿þGKß½÷—m¶ß©(]qëŽÊF|²÷Þ{ýû˜c>ý•¯ û=2³fͼò¶Û~1öî?í¥—^<ôÃ?è]HŸyŒG X,Å!Öp¬[÷Ò!XI÷ÝËGÔÕÝ2aæÌŸÌ¸è¢ïÏ9ò[wvÚç8ðègöۯ߫HG»ƒEw>#S²ã.»«@u€ìsÐrÀფÿ1Ÿ“#?{º dßt`åäª óì_}™“E>á§Îâ=Ü €»d.øÉíNVeܵËdâ +Áñâ[pÖ˜ühÑãÐ̸kó«s÷¤×Ü»^pk" gÎýenã›rãÃo;‹`pç?ú¾,øÓ‚ùþ[š?RmR·êc†ÜkVð€„c8ç¢ Ê¢Ô… êFhëº?üÛi2T¯ø»à®ü⇌€/È|Ù­MÎ5`® ׆Ì®A×>bÒuδ@î £.@ÝIÿíd Ž:BpQÿc‡ÈG'ûr”“‘BÖ ÷¶;òN7”2ž={nõñ{ì¾ñSŸ°öä“Oztøð¯¯D–ð:gÎõ—.^üËóþð‡†/?óÌÓÿýï×öþøã¶žÅè#Ï!<$M‹ÔE©FŒ©—×^Û°ÏsÏýõÀÈï~wïW—,¹c2"ÕÕ?~ùåSj&L_wî¹ç,ýÚ×¾zïç>7ø‘c=fuÿþ‡¾ˆ_¬È’lµUOJ b<×ÜäG_l¿ývï ð¼‡ÊÊOÿ`púé_nøÖ·Îúõw¾sá­¸»â'?¹êªyóæ^¼hÑmÜsÏÇbñÏ|IDATÝß|ðÁÆ¡Xß³~ý+¼óÎÛ;oÙò m…þ’:P„,Šè¤RÏç//xï½w·ß¸ñ¿{¼òÊËRž|ò‰A<òðçî;™“¥K—œ‹EzóçßtÑõ×_wÙO:ëÇÓ§_Q}Ùe—^Ûp¿ûÝïül̘óo?眳ï<óÌ3–áó°a§ß‡ ËàÁ§4xâ h>ýé£Ö~ø§žÇÔÏ!‡¼î€ö_ìË>ûìý‚kß¾»¾Ù§ÏÎoï°Ãöï"à"+³Í6½?èÝ{ë·Þº×G½zõlïsÀPæ×>>cŽáœ‹2(‹:PêDÝhm¬Ð6d@PlA6ÈY!3dÇ5 ÐãšÎ8cÄr\#®׌kŸ2å²ë®¼òdz®¾ºú duè+ÞòåËθï¾ß{è¡O]µªùø¿üeÍQÿûºƒqû2²S\«@{ô²Gî N/ 8u€:@ P|Óÿ³ ŽÿäL§OIEND®B`‚jbosscache-core-2.2.2.GA/src/main/docbook/images/SPI.png0000644000175000017500000010725510660354341022550 0ustar twernertwerner‰PNG  IHDR0 Ÿ ;Ú€IDATxÚìÝáëu×YçÿàCÿŸˆOç兀¤ÄT;T;Í$XѦjf¢7 ¦Dg:ê(ÌoŠ¡©I•‚¿ Šh†A…:Á–  eõQup& ÕÎïÆÀýûú=g¯u]×Z{Ÿ}Îy½ùî|Ï9û¬½Î9{¯ÏµÖº®'p&žÐ 0$nÎ|Æ2$Û8¡ÍÞÀ–0$†`HlHžø–þþø¡'îðø/K‡bH†$jH"¼û—þÝx!C0$ € 4$ÇgHlgHŽMHw C0$Ó ÉÝGÛ‹¸€!aH\¾!¹·:+eHÞ}÷ÝGoôþûïû´†¤bHÊ<:Ú+¯¼ò'ò'>*€! ’YžäÑq¾ïû¾ïå—_~øð¡ `Hê/.ðî»ï>óÌ3ó7ãs’­ ‰ÅZCrCòçþçk IˬŠÅZCÒ2$ëµLž_€!9›!À0$ C€!ÀC€!†ãáßÿÝýý¯¼ôÂO>õázôUò‡þ郟ù‰ßýO¯þÍÿþŽÎ`H€ùæÿü?õìýÚƒO¾ýÆçþö­×õå÷¿þ¥o¾ùë¯ýò§?þÏ>òú—¾øÝï~W/0$À*nä¹ÿèÿú/¿þȇë‘3yõ_¿ðâ¿zá½÷ÞÓW 0“‡ÿw?õì-¹‘ÇúÒ¯ý¿ýÜ¿Ñ] 0“7ßøí_{ðɶy¤‡ï¼þüOýÄ7¾ñ =ÀÓxéç~òí7>×5$ôço|þÅ_Ôc 0§>üCìbïêý·ûÉ'ŸÔc 0õ'p#ÈO€!fb†€!ÎÆK?÷“ßøÃ1$_û£×ì!`H€™¼ñ;_|õ•OE Ƀù3o½õ–`H€i¼ûÿÏ3?ö£ïýåÛnäí?üüóÏêáÇz €!fò»_ùÿÙO7ÜÈwþÛ—ŸùÄ?ÿæ7¿©¯`2>|ðó/¾ö+¿ðÝÿþOÎþ aHîùŠ®÷8þ£I†(º‘Ã?ž')¸= ÀuCrȬÈZú7`š!IíhgK 0$`H0$À`H€ö7oR‰»ÅC.ëì9 C‚Ëv#»ßÓ†„!Á™Çë'«§EÖïêøUKÇ_zÓ¥ÒìKuÜÛÏi4†! C‚½¸‘“Ö¢ûœö«N®ãj,îjƒÈC'Øh O†»6$‘çg½Gð8‘¿Ÿœé¶ùä<C†7gHâž$òŽqC4< °;CÙC7$ãaH\ƒ'é®bzü„öÆŒòþõvÏ_Úé<7†g6$ƒ/¼è1=C†{‹·§>¸† †„!Nó·û·¾êï¾û®®`H€­yíµ×>ö±½ú꫺€!6åÛßþösÏ=÷þûïò“ŸüÖ·¾¥C`;^~ùå¯}íkþñÎ;ïèáÆ0$V4$‡¿ú2-Ép`H0$DD †`Hˆˆ à§Ç1$€!!úòÿÀÝÿ-¼êä£wŸóÄ݃Eô×𫯼ðìGŸþð£‹áGžzòåŸ}æßû†`H0$t»†äž9ùG†„Æõð×_ûåO?ó±§ß|õ3ßù³ß|ô—wÿâ ü[¿øÜÿÈç?ûÓïýK À`Hèb É’…8¶+Ù†WaHhº¹‘—žÿÄ{ùÅ{ô—W^xö‘'aH†CB—jH ³÷þqÏi4œŒ€j+µžùØÓÇnä±'yîÇäñÚ-À!ÀЮ É=;™Ð8¶9“È´ QJ¯¼ð웯~¦ñ„¯~å—^þÙg€!Àи‘{¢àFº;I¸š«>ýáö,éÝ¿øÂGžz’! ]†!‰¯Èj›Èsš“É7üÀ!ÀÐE’ÔŽvëµÈ †`Hˆˆì!±‡`H0$DD+gÙúøG¸eëí7>Ç †„ˆH À]—WjÿÓ×?û¸RûW¿òK*µ †„ˆh»µ[¯¼ðìGžzòÑÅðÑ_zþë!2$C€!!"ÚGŽ` †„ˆˆ!À ]g‘)ÇÙUaHcT2!†`H0$tÙnd·ãû`cxbH†CBg¯Ÿ,Ž~_?9Crüœ“/l”r?ÙÚv5÷“Ïi4†!!†`H0$´7rÒZtŸÓ~ÕÉu\Å] cyèäáIˆ! íÚDžŸõÁãDþ~r>¤Ûæ“ó< 1$C€!¡›3$qOyǸ!±„€!ÀÐ¥’È’Ôâ¨Á¿0$ÄFE:`Hèj7µŸÜ’ÑÞ˜QÞ¿ÞnCãùK;݃‡âFˆ! ]RÚßÔ~ôÛLL †ðÓcHhæX¼=õÁC€!üô""†0*À1$ÀO!!"bH£" C€!†„ˆˆ! C€!’ÿÿmÜx†€""3$€!!"bH0$€ŸCBDÄð ""†Cøé1$›è‰'þÉc=þËɧE-·aʉԞ÷=œ&C¸xCÒf‡ŒwŸŸzí`ÊCÛ{/,¸‹k2$mXÙÌ0$Ä`H†$aHîÎt:ž:¸÷´»¶Ÿùx0züòl;Û® ÞàÆøø¸©K3$K³+Ólüäé4þ9ëöÓR.ëdã» k{¡ö“»ß"†`H\†!ÉŽþÛÃÖ¥Ñyû%awÁ>ÍmðÒûfÕñ·Þ=`° ©§¥&¦N:®ö©y!C¸â?žaˆ’àtDª‘wiI¼Á³¼Mw^"8A1r:µ7Í®”‹|É¥,}²ƒ«‰€!°»’¹Ó#Ý $ñmÓ'—úDopdÒô9‡);ËÛœÑ4–Éq#Äð ®ÐdmI0P|hi”2$àv IyÐ_«!ß›‘mƽ‚íù–xkkeÚ»£ülÉÅn²5Ç Éz È®¬†ï‘ŒLUEè À|yp}TvÜœ¾h,‰<4ØÚÈèsÜdÇÙ‘^åmÊë) ØÆ4ÖãÍr#í™1n„!À É„õQñSð¡¥qêÜ $ãMJ’Ôº )K§‚kNÎÒds…­Ý€àà~Ї,õÿÚ†¤ÖoÄ`H€›0$©†Ââ¥gƳ9Õ²0Z[XÝ”]“–]àìüƤöfô‘íÓ°ÙŽöFK ÓzÁ?ÚÑÎ`H†D’mVC0$2àÖ9Ä`H†„ˆˆ€!À1$€!¡©Û‹§¬ð‰çt*¿ûN“ˆ!À Éhž«‘BëåÚlp9×ð ÉÉ„Q C0$ökHK³/•NodYmdt^$Õàv©Šö«ºYtÛigSmŽdï§£›T·ûdÉm‰!À I±áHÙònI¸ñ"‰ÓÜ>f¼ºy䬳•Ojµáý¹T01[2xâ< 1$à¦ Éøˆ¿Q /^ý­;ãiä½J‚©Êƒ©bÞíWEª"–“­ØX[ç6X½[$±¼–ˆ!à \á ÉÜé‘Â~Œö»—<•§G"<¹Ü¨ñÌr·¤lÆ>ÝÈÒÁ¹bH0$CrÚdmIvJ¡;D>9Ξ¸d°Áñ…I©z÷Á6wß4µ^k܇4f–bH0$CÒɲLoÕØl™ nŸ˜b+ÛàFÎßÔ’­¥õc‘éñ6ïpG{»ObH0$C2­XÇÅU¹‘"*FüD `Tà: ɬŠWâð ;Y1$CàšgHˆˆ à§Ç1$€Q†äŠ;/yš²b*xø{•[µ·ÓÙç³Æ.`bH£"WkH“\k¬×€ÚÐ-^5%þ^çÁ·­T¹{’3fJ`H p†$Uýc)»ëñ£ígžÌ9;·Iªµ‘í‘ÿͶ¤›¸ý)Äß÷âNg³TÅÝ'Çkqf«ÜteH på†d¤X{¶þ]»àxyĹ­ ¾<øhû”³Uk…G.ët"iÄ–ªCÆÿ˜:ÁÚ’ªno¨ É`H€5$åA¤®_0èÛñˆ4ã^ Âö|K¼µíj‰Ùó a|ZÐ\ÄédWÙÕÊÌwKRFfŠF ‰’ö †¸é’òú¨ìP58}ÑXxyhbkO>Ô^ÙUÍŸÙÿélãF–>Ñ4V*r# †`Hê¶$5ŽO-ˆš»¤ÜÚø ŸÔH·ûà¼MüMƒï»·Ó î}HcBlUCRë7bHø…WkHR3 …MÃKÏìú“ÁÌKÙÖ¶÷UÇ×85EÖE6‹ìh¿”ÓÙlG{ûܳS|Á?ÚÑÎ`H†„®°ˆŠNÐáÄFE2DÖÕÄ`H†„!!"bH†CBDÄ`H†„!I¯É)l,ήä)<ÕV5v¥ïó&bH0$€ŸÞLC2˜çj¤¸Äz¹¶jï;ë8Árãkï[ˆg§í~Ž 1$ÀOo¾!,;T:½‘Yµ‘Åuz5’Tƒ³O¾véiÁrãu$k­bHˆ w†ÀÖ†$;úVKÕ¥îõK=4·Á…ÒïÙZ„³&¦´*U+ˆ!À~zuC2>âoÔ¿‹W|ëÎxDyo«C¤˜w°Áñòˆå·4$[,hÌJ †ðÓ›3C2wz$R©}dRä¡ñ·[U8ñòÛÅûjJ« -'bH0$€ŸÞ!ÉÚ’à”Bð¡îâ¢ñ $…w7uN¼ðvñí1³ZÅ1$Cà<†$•Þª±A<2ð-¯\*/j*4¸ûªÈnøàÛ§z6hUduCB †ðÓ[ˬ—ßv?uEvØC|"†C0$ëV廸â†[¶Íøˆ!À 1$C€!!"bH0$CB©å^›- Vq©í˜ßaoï¿ý…º:ñ-Fê:—µ +þÂrÑ›H9£¥²E÷^n¸0$ÎiH“\3ÿ®Ú†Yc¦xûgã…ØSã°Hδ]å˜[„qÕÏnÖ'’ú6^h*ˆø‰O7$‘Ê< À8¿!IUÿh$Ÿ=ŽG¶Ÿy/¼F)’lƒÛ#˜{­í+\:©HÊÝxÍHNáàÄÒÛŸÚÒ—$;:l÷vÄPµ¿{í¨y­‘‘Ùµîmÿoû'Ö휥þit`áÔ²æ­Qå³ÝÚÔo‡!û2$s‹µGj_ÿ£á" öinƒÛ­” ¾#ÍëV~ ž]·Û#ýpÒ›¥’§Þ%~š…ùжÉÌ’“ß™†…kœHÖìµ»«\ 3xj#kí– I÷]‚/‰|s w†Àv†d|ÄrMv°_|Í}p,¾´Ú~°Á©‘b¼Ìb·Á^±[‘]ÝöÄgcj†¤;:¨øÇ4¸F(Þçñ‰ø“àw>»&u㛺†¤Ð™©B¥ ÀØz†dîôHaK@p9Mü¡ñ7þ7Øþˆii/M©íYÉãÖS¶L º‘C22€n"_§Ôw)þ¾çr#Ù™¹³@ ÀØÚdmIpJ!»~|Þ&r´Z« ø¸á‰O¼ ’àž¬ÃÉNe‡¡å•uk¯×ª’k ϲn9;×Wø!éˆ!©­ÝbH£"—dHRij»H##©¥)SRlÜØA[KüÚhÀÈþÝÔ1¾£½ûòȺ;ÃìíîŽöàÇÏ\höà¶ïF Ú¸‡ïnæ ~£R F&BS~2»£=rF ÀØ!9W>ЋK`zM…Vn§Í>}ß4…†À5’h»â)Òp#¾c `TàgHˆˆ®O†;C€!!"bH0$Cb‘ÉÔ¦Ò7Õ²èNyw‹ˆ!qãg3$G´åÂk$ÚZµÁÁ4\©à C€!nˤª4²m§Úl?ód.Ô¹¥H² î¤ñÇÁZ„‘:w…cú1$ÀOoš!™[¬=X”z©ÀÙx…Äé –c[*®W¨)‹ž-E2ñŒˆ à§7ÇŒøg"†$2‘mäÝ뜚ñèÖRœbH"ŒÔj¬C€!üô¦Í̉ü)Ýyhnƒ ™2=’ ‰ŸÝà1$ÀOo¦!ÉÚ’à”B|Pž É6Œ78¾²«»PjÄÔÖnM?#"†CøéÍ7$©M-Ô‘‰‘öR¢5Rle¼´î+¾ÿ;¸‹#2›1²£}¥3"bH0$€ŸÞ*†äꞥÁ&ˆ ÀL3$'ãñ;7!go°}áD †`HæÏ1$ÀO!!"bH£" ÉUh%^‘£¿ïôUaÁ]øD †ðÓ›fH“\ųè®Ô€Úpyb.©naÄUíÄn!!†Cøé­bHRÕ?–òÌ?Ú~æÝÒ{'kkŒ×!Iµ6ž8ø¸ÙKI{»õ%§¼o|N#˜Ž9’뙈!À~z ÉH±öHÍxi¿ñòˆs[Ûn¹ÒH¡åÝJˆñ·ëVMÉöC€!üôІ¤<èïÖæ‹Ú»3‘fÜ+üמoé¶6Uîp¢!)¿ïDCR0lD †ðÓ«Ï”×G5Fá‘zííÑmcuPû¡‰­Ýؔ߷l~¸"†`Hœß”mIcœ|hi >wÉHkSK˜¦’‚7¨’¶‰Og1$ÀOo(ËVp†!¸Ç=²s:žª–b+ÛÚÔÎòòÐàŽöÁ÷ v”íÄ`HlmHh0ãíJãõ-‘áÀ`H.Õ¬ä Îõ¾D †CBDÄ`H†„ˆˆ!À~z ÉÆk´‚uÜÇ3_2b•Ïb¥U^ñnÐ"†0*p‘†d0ÏU<îzm(m#CäBÞá=’éãþ“‰Ë¦ä&bH0$À’ÁÒ쇅ÒéK)eGÉíòêƒÕHR nWllŒT|oœæÒÁƒmî6¯]r$XÞ¾–7¹[™1{RD `Tàz Ivô,ìªÞpû4·Áñz…Ýʉñ~hü±q‚÷Ü®!©C¸6C’JoÕØ ™i/Z#ÅV¡Á…QÙei…ÕVíwiì‰odµê6,þáæ¬j'EÄFE®Ó¬Wwb?uE®»jÊyßwð!Ä`H†$Te2$+u2I †ðÓS©ˆˆ!ŒŠ0$DD †`H’îFùà‚®)k‡âY§7ÀÚ'"†`H\€!LrL»jÊï•M(_šê.ÊŠœÝnw´7ZÈCâÆ0$ÎcHd’ݾ4Š>$bH0$C’I+ R|1$€!!""†`H0$DD †`HÖØ^Ï(YÑ4eÉÓªë¦æ|ðh³3½Ç‚;û‰~¡ÀÕ’m†˜Áñ,Oƒ)¹ºÉ’3æ3`Hˆ!À7mHîå™ &ù=N{ò9ñ»ñj!'K7œF;ïÚýÐxÉ–Éã'R˜ÓHå–5 ÀœžÊ8Yžb©ÊGÛ{4þ×ÖþQxtÕ~8Ù˜ààû¤/:yœîƒe[F–Tu{[MFbH0$À­’»Ñè¥iŠvqÀÆ´F×WtŸ¹TÙpiØÝ-eX3$Óû¡ý’ìb¶Záöã6Dºh¢!9(O †`H"aò†I>zì|â…ƒ±üÔ.”Yn¤Ö³öºŒ¸‘¥ƒOt#‘%vÜ1$€!©’à0=ò„î`º1H >Ôžµœ)¸©nWD÷ƒ>$õyM4$ÝUv ØC€!’èPûîtG|úÒÚ¡ÈžæÈ¢¬ø.êøŽöYýÙÁ¿åŽöF‡fu‚´£ ÀpÈ3«£ˆ€!Àjë""†C0$DDÄ †„ˆˆ!À Éš {" ~â;›§,Zu Ò܃í¢W[ÍjüNúpúg1’ €!p~C²Íð(X•<^¢ñh¹¾Ç®,C²7c3±7ø8|â À¸`Cr/ÉìR–ØÆó—ÜE*Ÿo¼2Éɺ㩊é[öCã%[fþŸH{È{Üùñ|Ííã?!å$÷܇‘?¦7ËžÌFE®Í,­EÚÞ£[¬#[¶/ò£«öC» `°ÎàRmÇø#¥W"e —†Å‘·èö[¶–||AàNú0>[Øí4ïÀ¨ÀE’»‘Ô¥0v»a»b`wh™8™/ »ek†dz?k#³Õª¶·!ÒE‘ˆ~÷ì"¿ô­~ —Ò‡ƒ†$n¶‰!»ž!©U(?zì|‚¯x:µ e–©õì½.##éÆÊ«A7ÿjEf6â Þ.±³ÓbÜC€!’Ü0=ò„‘¡gð¡vô}pz¤à¦º]˜Ž¡SŸW*s@jÞCr¹}X3$Ý•Š…™7bH†ÀEÎÜîˆoC_Z÷Y~Y”ßßÑ>«";ø·ÜÝèàû=,øilÄ_*kS[¼T>ýòç8Øà•~JÄ`H€ë7$ÙPwUfEŽ9e ¶CCÏ(µ‡í GííÏt‡†d›oCÂ`H†¤“5U­¼t5^{{V×n©“FY^z»ÁÚíëž{wšbzf¿Ní'ÇóA§jŽœZ|z0ž,XÆ^†C0$¡âwµ*×ñŠ„…cv˜-x×®qòàÙÚÙ:0Çž!ØuÁY©¹/†¸dHâ_³n—®qjÙ9¢F-EÖ À´ý·ç=ʆ$î ²ãÂZ;Ožcc†$Þìx¨;^·>ÞªÂ2¹)Ÿ.ˆ˜™õ IùÔ¦’ZewbH0$ÀÕÎ4†\…‘\Í<œÅÄ÷«D"î³6ä”#÷Û»‘Ôé, ÷ãCöÂŒÓ\Gú|S§Æ0$€!éÄw—&â#ª¸!),›‰Ì$tÛÙÈÆ×J¥Æ”÷ÍŽ\S͘ީ¯SwMW÷ݳý9~j#†¤¶„Œ pÓ3$ÝE\íE;©_åíã»–ƒk‡îmƒÛ¯#{¾S;¤#9VêÀàŽöÈÜÔøŽö5N-µ¥$²³ßŽv†C0$#£´Á~о‡Ä †ÄXp´r¢®ó $†0*À1$ÀO!!"bH£"7kHâ+Xfmؘ‰¨p„‘Ò"»h)תßV@MoF­Æ1$ÀOo¾!Yc–ªRRÈ©:¸Ýb|4<·Ó E06:ŸÅ9œË-0$d¸0$vaH9g—²ßžÌœ­øÑøßnšà`"ãÆ ¦’É6ŽÖ}—Bé÷{oº”©¶ý 6,P*nã] ùyG,Å(ë.1$Càò ÉÒ01^Ï.^Œã¥¡dw,¾ô.©í‘1îÈòª¥=åqÌ"Pndû]²36³¦}ÊŸ íÄ €½’+Ë|zAßmÞ^Ÿ,1$k_ÖØ†ÄÈõ<Áxn„h‡†D?ÑÝàö[©ˆÈõš!!""†„!!"bHˆˆˆnÏL_ES8`|ùÓÈÁ#I«â §4r¥%LÙÃnó°^‹""¢+7$k ø–rF­:ž{"Ùl‡ïSŸÒ°s¹†„¢³è‰àä¿ G8K³ã­=KS‰fé¯ÿàW_yáÙ>ýáG·˜<õäË?ûÌ;¿÷+oH™—ÒѶóØ6 ¨7Ž“²í£uß%Xþdoÿû¤ kŸ`­‘‘wi¤]Ž7 ~C¬bÉA6^bHˆ.Ç“0$DûÔÃw^í—?ýÌÇž~óÕÏ|çÏ~óÑ_Þý‹/üñoýâs?þ#ŸÿìO¿ÿõ/]¶!Y’ž¼ `ß4¸Pji8;ÒÚîp¹;Ún¼u×dË·Ç_;¥©Œ‘b‘fHˆ!!Ú•!¹7ÖÌñóÞ ñªãçDÞñäs–Þté€cíVÜÈKÏ⽿ü⽿?úË+/<ûÈ“ìÝÜ@GjÞM)Ö·…õ<#Ô³eCÒ®WX¸7\S­öy¹ Ip2Šˆ!!:—!¹;@o/âŠ;“ïÒx4þÐICÒhmÃzñ$´ÿ•ZÏ|ìéc7òØ“<÷ã?òxíÖ®gHÚ‹pVr##/)¡ìFÊgZØß2ÒÈÔg·±iOXq#Ä]!9ž[h{†“S ©CE¼JÛ4Qð}‰v«W^xöÍW?ÓxÂW¿òK/ÿì3—jHºËuÖ0$ÙôDCñ!ݦ6&|VZ×Ü­±CCRëa"†„h'†$îFÚ3ÝGÜȽ?¶g]‚nÄGO¡>ýáö,éÝ¿øÂGžzò‚gHÚ»ÒSSKž'¾á;r„²}ZZø´´§<î‚ëî†òH#Ûï’±™5íSþíh'†„hÿK¶ÚÛK"óCÜLß0$Ù#7V”íúž~Î…Õ!ÙC‘ó]yÿÉ•½‘³ bHˆ Iw¹ÔÉ¡|dûxêïÝç'MKj‹|û™DfH.Þì|äº}0ž!bHˆÈŒ‘=$DDÄ1$D—ª¿þƒ_ýøG¸eëí7>Ç1$ ­¢‹¯CBÝ5HåUI»ZÎ4·1SV¸í¡«·Ù+eaCÂÑzz\©ýO_ÿìãJí_ýÊ/]^¥öÈà©–Šwä……ÔñÔ½Ûìf¹ ÛÌéyô,'¾Á›2$ CBDD¬Ýzå…g?òÔ“n1þûÒóŸx\ñ’ Ic(¼”ýö8Iëñ£ígNeûMµ$¢¶öÐK¹ÛmöÒq‚9Žƒ¥ë·iLp*£ñ¦í4Êû<»ì§ m1CÂÑÕßඨC’2‘ú©šÜ QðN³Z›jvð]‚¥÷И‘™öѲý¿öÙ­Q°R¥y†„!!""†$dHʃþ¥ “ZýÁÚ¦“!ó¥ú†…Ö¦š,ј-…~ÆÆ I·c—=ïÙ ’”!'†„!¡ÕV‡>1÷8»J{µMc‚ï2ø4ùĈ!9q,¯Š £¥:߇…*鑇[› ɯ1÷²‡ÆÔ¦G5ìÝÉÙÝBd±7âzÍÐ5×oÄÌ=ò\KCt[K¶j¶$¸<¾Gb¥ $Ù&e·vÔVX• É6 vr{º#ršg?»š!i¿Wv 1$DÇÁ'‹‘5ËOÎ4ª³·ÿبt¾T½ýœFc–ú$Þís?yœã#t Æïsö‰Üàv—e+8ÃÜã~ìŽçYª¥Ø*´6rœìéÇ;§pR+5¦ûº›¼÷vvåOá`G»ë5CBûp#'­E÷9íW- ²»IM´ØhL»µ3:ö$ÙoÛž„ŸÇno*¯:œ¢ù†$òü¬÷'ò÷îBÃ6´çyjgô-Á¾eHˆ!aH ‘IWCB ɺ†$îI"ï82ó°±!)ô?CB ‰» CB É⌲!ÿË…’Â’6†„wA""†„®Ù“46ï‘h ÁËû×Ûmh<¿»ÝüÞ,y—¸i¯‹˜›Ú‰!q]ÌS^Þ3².(þÚ)[¨wrÖD ш!|á•÷sj 1$*%³£Ìr5†é¹¶Æ¼ñû±µˆ'¥ 65õ(_A®× ]ßh»=õ¡‹¸rƒ;§!,Í~X(~XHz² ^£¼z¤atªÁ‡^ÚânËÛyo/¿÷ªx Ú¥—·Û–JÖ¼öY1$DDD7jH²£ÿ`)ºTe놋(ا‰ Nµ¼ñÖKUóÚÍ8 WOO9œ3ž5CBDDt‹†d|Ä#N2ÄG¥ÁFÞ=l¤%ñ§Å[œëÈÖ2/’l¥Â3ž5CBDDt£3$s§G"‹‚»»´Û78;08á°ÞôȽ('ŸÙvÛœ5CBDDtë{Hâ¶$2rÈlï:Á $…wÿÙ¡ym¼^0$íéŽà;nÖD Ñ­’î±½†çß!]‚—Slܰ(í¡|ð¬S{l‚­Š<ùÏrÖD C’ÖÅ (/¢Á6|Ý‚!`{®ÄL©Á·ñøþ²|—È ‘""×k†„ˆˆÜà""†„ˆˆˆ!¹IK¦d·¡OYr6}MWꀷ Žˆ!!""7¸Ë0$ƒI®–žŸ쎴¡âo\÷»Åò‚ƒ×`#ï6Ò’xƒgy›Ô-0eH²wÖAC2þ™1$bCÁè³Ñ™ñËõcRÁ›æÒÔÊñ›ò$Dnpçœ!™{ ìyƒëÚË~Ú78ˆw#…vîU+¹‘Tg1$mC"6Ô~ÚÒH½Ýþ‘þÙLª{q.tšK7‘Ü™÷Äo‡Á Y|ÔÛn\$Phð¸!É®›ŠÜŠj‡ùhÊ‹­ÝÕˆ!ÉΈ Eæ&^Q³ý³«˜T0Tt<=ÒèF×m"7¸½dÙ nal¬Ê\Üã1­YÛ( .„¯²¡ÇZì0r­}4©ÕÉMóîmÄ”÷ˆ ¥.³íÓ ¾*>:?oLª`HjM%"7¸ «Crq×/ÜÔÊ+"×ë³dÙ N#ƒ>Ý·kÇYΓ:ÌÈ‚Øh˜Ý#Dnp—jH–®Ý{pËBßIDW‡DlèŠCQ«ÆŒ\ù‰ÜàTj'"bHĆ„¢Î3bHˆÜà""†„ˆˆˆ!¹XZ{ùìõq»Êw£Z©Z0‘ë5CBDD ÉŠiw¾¼¸VxV3fõÏR‚— úŸ!!bH.}«Iù:6rL•‹_'¶“³&"7¸]’“••õªº©`Û™^ºГï’Jo?—%ópòÃÞë„T2™C8}g7©Ž=ýDgH“\M‰­¬‘hk°ÁÛï†OÅŒâ]zÔÕ›ˆnÚtS{’“ÏTbŠÔÇM¥W?,¤Z?TÓÆ·_<—‘mü½"º\C’ªþÑÈlŒ‰tË¢O,E’mp6’?N$&uN¹ÛhLãÏ~ÖDä·µ!¹{oèæ¤_zB0z9Y{ÊÆkK¥²ËýF0ñüJ†ä,"FD{3$s‹µ×® Q°O+]Æãá¡x*“™ÙÈîŠÜþ¬‰È îl3$© ²å{^puÖ`}ܸ¯<îFj7ãÔÍ Up!ºC2>â?Ž‘'â£Ò`#ƒq®lƒS-Îu Þ;↤{ŽÝxßfgMDnpû5$#ô†!‰ïÞ‹’캩øÍ/{Øø&²!)ôís†dîôH$<ÜÕÐØ„Ö~h¼ÁÙy€Á ‡õ¦GËŽ=ãY‘Ü’øz­¥U÷°µ€Y6æW ÚEî£'FÇç4RÆ,øv´]Ð’¸-‰Ì¤â ]'0¸¤Ðà`$+>4Ÿ8µÞ œMYZ¼ñY‘œ:$7'ÓD Ip48dwH†àå[…gƒJñ0M|)Š<ùÏrÖDäÇÜ¢'q' bHfE7..#fDDÄÑE’‹[y‰+HÅŒˆÈ Ž!!"bHˆˆˆ’›_RÕMWUØž>V\5n7R ºüÌ‘slppM|0¾;å£YµöܯP¶ø÷þ£Î 1$ç4$ƒ—žŸ²œe”¤Ö*Ïí­4‚ ^!IÙŒÂ×`3Ú‰!9dòV_Äî†d‡›Lf]دióÌ”Up{èêéŸÑ÷b"†d#C’ÊðØH–rm?óp' {·`ð¡”n2ÛàC/£âRº’HâÆi¶¾ô^‘Þë~í9Ÿö±ú˜‘¯AðÓ9nX¼îMãPíX~;›j¤h·½D=‘oã¡—M;x ‘/gööß­¾“ù“=’Y±¡Ë kº©Ý#S¢$gïêÍÞ”!!:ì¿ÉÄ‚\ñÚí]û1^½xJƒƒ§Ô¡º6&R±Û{ÙŽÝEh©êŽ5Þ«ýFmS‘ýBfKv¦¾+U¸g2ƒŸ]¼šÐ!Sg3ÕàÔ×õÖ I-6tY¡lT¨Ðìvœ(âê#×ÌmS¸(µÛJ }®³Ë~ êq]°!ñ7* ƯÝ[ÐkJƒgy›ÁúÁ\°4dÜD¶ÊÔ Id)xRsËk¦(7Ê(’YžpiÈo[m5Zöjp݆d$6tY¡ò•3‘‰‡~&Æ\ÖhÌÈÌFv÷ãyÏ.¾n¶òSÿ‘èòfHæND"ÁkP#¤Ñ~h¼Á‘Ëå¸)´3>’}£íÝH7ߘÄ8yÖñ¯Ù’‰ vE¡7²?„ÝHciât7’úÞŽ!)úk>ü\¡ñ L-61÷µqc †¤ 9ïÙ ’ƒ‚ôDW°‡$~Ï MãK—ÖîÏZ'Phð¸!É®›Šß‰ CºøG“½èDŦ¬€Šß{RËáâÄÄo~!8:ˇ¤|ïDC²“Åë;™!)_÷."0T³£ñüs/{hLmz$ÁÙÃÙÅcp#DW¾©=¾‹±±A9¾,5>(ßIYhðàšŸàº©Âº²ÈM½üÑdw¨7ëÙ/Lw,uo5||ö#xÔkãŸcdeóNv´û*¾U=2~¤X¸©=$[r¡šMmí¨­°*’m“Zú8¾„øŒgW3$…à Cr…uH.îî’¤OÈWhç›Úã3 çy®ÀPÍŽrßewaêxãÆtß=òP$B´«³‰tØÑNt†äâ~Þ®G< ùò\–!!ßjND ‘ë5CbˆLºšÈ Ž!!"bHˆˆˆ’[ŽÜD2cÆmïP?c8*u giypÝóx«Rõ4j½ɯÚÈM, È]³!Ü˸ôürVÐBÊõlæß‘±þ¬1e7SÊôS8‹!Ií=h϶7$‘Ì­ Cr}‹sVŠ×Ì XwDDnp’Áò[‡…òX‡å¤¢ÇìnÍàC5ãdªÁ‡^ŠÃ¥‘h¶ªÔÉK‰Â)S»ѳ§Ð(õ½ÔQK'’‘w{ìЬÒíÀ”KiW,üj†äfcCƒU8wŠx|!!"7¸s’¹ÅÚkµT#é‚}šÛà¥÷U¬½;ÿP.þ}WŠLB¶àz£–E9…èÒ»2äú݆²à_? º&Crű¡BÞá“чH‘œà2Ôv°é0Pاûd¹‰ˆ!™6âolˆç,©ØƒcMið,o“h«+Öâús I£©ñDø5CÒ5xñœnH"ì.SÙgé@×ë5 ÉíĆº¿ÄÁxMaÝW7ºÿc<’âçLD É*·À¢ÿö»GÖ Œ¿ËH6p#µS[Û´«#³OÙ½=#³I³ÜHv&$8ÑQpPtцäÖbCñºàµKhÍ~¹Á8H!øEDt‹{Hâ·ÃàU5~oh¯)š²H pÇ4$‘ùñtvJg³S(Ø­ÚÐ?ë|‚Í1$µµ[ ‰’›Š MŒ×´nmïFâS©~ÎDÄ mal¬ZŽÜü‚K‡'n£,48ÖŠoo/ÎY²RµNŽôvá‚_‰øj´‘!QpG{vùSmt5²£½›€!¹=$× ®½ÌF4ºWòøz­q22“CDÄTtqÓë¾ú»·VKL®×[fÙºÊØPÛxâ5µÍ*‡Mv´Gâ5®6DÄ ×âÛ‘w2B½¬L|Ð:®ÀøÞž¾-ãDD74CBDäz}CrS¡–‚Ó3DD C¢R;1$«’ÈÂÜ`NÉø£ÝŠ çŠÆ¥Naã–‡Qã/,Ÿ`$ÙèRRQ«2ˆ!!""ºC2XâøÁ­„µ±þ¬h;UKmè¼CC²ÙAÖ0$‘ͬ 1$W¼"kË T7¶Á¥øìq7"rƒÛ‘!)¤.íî#lçi­åœ=."Þ®½u¯o-mîÈ)4 i-uÔÒ‰d‡ï‘¶Åd#SM£7‚_'†„nÜ &¹J…ŠVjÔYÙ3’‘8ÔÉËu-²ãGDfHúèT êl5½C²rÖHéîø˜{ð"í9¶"ÙRß©ýa9 aü‘­·Iü“%º\C’ªþÑuõÝ(L#r˜]Š$ÛàC³úJ¼ýíWeC`ÝÖÆ“å_"¥Šìà'¢Ë6$÷–ã·¯ãS Ip Z3$Ù,5C2x ’øãk F>Çé†$òÇîN¡àÔÑÅ’¹ÅÚ»¿îˆÿ±%³¹„vçWgÝq [ Ò¥SŽD¯ˆˆ.u†¤q=­bã;LÖs#µ[ãÚn¤ÕËŽ'&NÌr#Ù™àDGÐU]¨!ñgcI©Õ¶©F¶ã\µ/ÅkâµGbm©——ËMŽŸ²‹!]›! .¶I] Kt‚³‚ ^ǃ··¹§P°[)CR0<#†¤¶v‹!¡[›!™;=Ò½ÐÅJùØöC|¼xµv…¯µ*?Š?ßIJ´^—!¢Ûš!é.âj¯±IE¡ºA¦ìÚÜFSS“$O¡»)²ä©1ÜÑž=Hj4M_å6L×´‡$nK‚?áàC‘Í ƒHFœ}aêí¦ÄfÅ’²±*[éˆèz ÝæÕ<žòÅ׃\¯·Ì²Loqõ‘%[ÙÑs9ÅV¶ÁÝ î.ÁàÛC`#K¶Êy»Ñ+»Gˆˆ!áI®ü|ÝçÈõzÿuH.îwº‡‚¶¿õmp}&"†„ˆÈõúl†dzå¾ †æ[6x?ý³^"bHˆˆ\¯Uj'""†„!Ù6Æ–J9üh·ÈƹV/¤Naã–G:SyÉj'É—º”U¤“!!""bHBãªì˜ibõŒm¶=D|B­2ñ`‡¤ÚÜ=òà)œÅlv5 I$Ó(CÂЖ1¡ "rƒÛ©!Iexld/9¾Î¶ŸyX¨51Ýd¶Á‡^AŒHN˜xæß㢼~èvläž7x r]Kµt"Ùá{¤mq'¹ô^íÞH}¯†¤fHĆ6pBDä·¯:$ rÅë¸wíÇxõâ) ^zß5êͧ²ÚªõæO!Òžc+’­Î^ø@³6†3ò eÆ?YºYC"6 ‚ŒÄÕl¼BDĬbHÆGü Ýä÷ÁÑX°‘÷&»#ÇT}Àqo“•F:ªfHO¡1C?`|[H¶È檆$òÇîN¡àÔÝš!êŽËgÅ„*ˆÈ n_3$soÝr¼ÒvcÀÚ~h¼Á‘ín¤vjk»‘v`udöiʘfÜd‡ÁÑCü»G7hHĆÚO›#XÃUC2aIüvœR>Ô]S4¾H ÐàqC™(ŒSãœzÉôS˜’¬-9[ÃÔ¢ C’š!ŠØ§ñAÙUC²Q–­àö»ÆêØÈͯ¬*o£,4¸°R¨zl‡Ç–FÞµNŽôvá‚_‰øj´¬«-·«ížY&ÞÝîoØÁˆ ¥<+F0ÑUC²‹:$w¹¼îëû Þ½âr\ƒè" ‰ØPpfµ#8Tw´ UC²#C2^¥nû1ëe5˜'™{¾nêtq†Dlh›wwq "78•Ú‰ˆ±¡³5ÆWˆÜà""†„ˆˆˆ!¹XZ{±o÷Ñn¾ÅýáÆã‹ÓÏ·œÏŠˆ!ÑÏDDÄìwYæÆíʬ3=¹‹qûÑÿ—n3$Ä0$Ó£0{Xî•}÷HV´}^NãùB|@~§Ä¬kHço]*+{ï L#mW¬uò]óÕ o×нw²‘”ÁÝVÕ²øO<ì!™v3^dˆ!)’Á$W‘_ôÈ`kÕ|»Ê75òÖ—8Þ=ÎÞ¾óqù­}@D·hH‚ÙÖO^¿N>­qwì^²•øÉ*éÙV¥*ñµ{¦|w¯¶û^‘zÃÌAݦ!IUÿhd˜ FRa¦ÃìR$Ù·ƒ&ÝQ$L–Š=E4K<•{$òÖKïÛ=ýöÜ~êöáÚì"ºNCÒ˜oW ŒÌ”g3ºƒïY†$»ƒeŠ!ɆSUW2$©žˆ!‰’¹ÅÚk?䆋(اñ—Èfã&ñ툩2ˆé÷Æ­0²!=lI"•û€¶ù€ˆ®|†$Uª|Ï ®ÎŠî×s#µÛêÚn$Õ'‡±ç‘Âjܹ^Ï2$ã#þFø2ËîŒGp;ç 68r•N½Ñ”ØSê'ǾñVj¿b¶WËÅ1}@gù€ˆnÝŒ 냳ó Iw¸\ˆÆÅwYÄW¦N1$…ÃÖ I¡Û‰’Ô ÉÜé‘xp$²§=sRØ7ëòGfgï§ v#úîm´–²%uj«ºЬˆˆ!iíPï®zJ¶¶6©ók7&Òªî~îÈüu$f™õiƒ‡ÿÑŽvr½Þ`IÜ–øÁ‡"; 7œjvö28Ò{©5åÍŠÁÅ<…ÕzµïCyzÄ4å"º~CBƒ:{FŽø¼‡‹hφ$¾˜þÐÜ _C[X®SN±UhpÜ8EÒ«DBE©\Ž©™¨Èæî\AvwxûÔ"'™Cómó1$´kK°j N"×ë×!¹¸_·ËÑ>{Òâ[_urƒcHˆˆ’Ä@粨v^Äx×ÄCBDÄ1$»Œ¥ÅY4§–7í­)IÂYZ¾´ì{z(hPŸx1$DDD—aHv5J+Œ#ky÷&622Í=x g1$ñÄb—âbHv»|eãe]#yö!"ºNCrœ¢;¢‘D¢;´ŒtO¾K­ú{£Vk¤BÓ!|£Ñ#§°T¤ð¹ÔîÍÁs)t`Ü¥S$&¦K6$ƒI®"?™‘Aó‰4VŠÅ{¦QV‚!!"7¸³’nBîã±o#´_(*²Ôž5еg[UX¿4~ ©¬çÝÏe$|xÒðÄËÔÂ$³_SÊP’ëõ IªúG#‘n0’ÒNQ:½I$b• Er§Æ—¡¦â5ÝH\<\Ò}²x ]­!¹{]îÎ*¤j#+’÷Š2ÅÚk†$»ƒ¥fHO¡1C?`|¢ R²*Ûªµ É!_|Šh'†dn±öÚ/¥1Ù[°Oñ-sñrãW§ø=¢»º¬‰ÿ1r#¢kž!Ép­Ýó‚«³ºÜÀÔ¬ÅÚn¤Ñ\c,©J[› )»‘K4$ã#þ`8 J52犧‰—“+ļ IöÚß?A"¢Û2$#ÃúîÔù\C™@Ç7½l| »•]`][U|ÄFÙ.%ÚÛ ÉÜé‘Hx(5GÙ&·vd§ñœÂ…h'ndðŒˆˆnΤ¦›ƒivÛËmkSðÝhSä\Ô{Hîh/ŸBwvwª¤=€HE‚;Úã82c_¢m6íIÜ–#èÁ‡"; 'n ™2Õ\ÁDbR©õZã>$uF®`Dt͆„å&qX-0ÑM’îo¡ë´ãÑ“xà£ðSíîˆh„jÑ¢v|§Ûi_4qG{$Ìä"ID LWV–ë@bH6¨Câç3«õ$¹Á1$DD I"`!â®+DD CBDt‹3$DDD Éü0U<)äž<´+Ol@!Ÿï6 #bH""bHŠ•Ú÷°cV«‰VÖ;ýTJ®qû?ìÆ #bHÎhHSlo¿6,žð\—P""†d;Cr²íÉ’|Á|#µj‰K9 »÷Ö©„³…¬šÙ¿sæÆO'u‹Ý¾aD I*íïf†¤–,kíhÅÉkÎ×."rƒÛ©!‰Ô—]ºgœ|ZcpŸ*¬>R)=Ú¾“…`ˆˆ!aHˆˆne†„ˆˆˆ!Kg½ÙüBŒMȈ!!""7¸½’ÁŒKÏ/ç¢-´aʾŽxããÎ÷GRŸ1$D É5mØØrÃÞÆ ´"›CÊu´Ö^Z¶Ò}m¥ºÙ]€!Iexldw¹÷„Ãr‚Ž{׈¥rÙE´í|ù‘šYÛ¯jg˜]ÊÊÒ>÷ng¦®¿ñN8,çi!r½^ÕÌŠ ]V`h0Ñí®ÔJ¯ÍæJ¾C²ÍGìFF´÷:$ rÕJD5\DÁ>Ímð!\»ªûh¼¬a¡ÒÙ¡šœ¾› “'!×ëÍ I-6tY¡lT¨ÐìH‚ÝC,9o$$·ô’ÂÛ5Î"^î)[°rbvàlo§æ4ÊYže€ Úµ!ñ7ª"Һܞï6Ò’xƒS¯ŠÏ´4Â]íΙR¾wéVï"×빆d$6tY¡TkS;K¼ôa¡Àâ±'9¹ÂvzùöZ©Û¤l·D"\å%U#…,Ýňv=C2wz¤°#U¨;òÐÜj]5ž9âFj§SXGÑ(hå:N®×’ò ?Ñ8{`¨ÖÚlÐ'Uø|¤¸û¡T¾=;]_0$åÅuãÎpéËPh|ÁT‘'ºô=$ñ{^vJ¡;nînr\'0Øàx(¨ûhv9Ö`ǯæÙè ‘ëõ3$åëÞE†²­ÍÎ~¬4÷ÂúªáªåeáFˆ®Ê¤v166ˆGîÁEMwRfÜXošZ²ÕÍùÛ8~¶c óE‘öÛ=B®×[î!)Ø’‹ ÕB*ÝÛqC2QJ…¨ a©T6”îz­ò–Âñ.*{ƒñD‘ÂjDWbH²º¸Ÿ÷õ]V½Ôº|“ëõY²lg {ŽÏ*´6rœÂ–ëñàzwG{û åíñ™®3îhïF¸R“]©Ô‘v´Ý–!¹¸ßöÕ_ŒÖ;5—or½V‡„f]!ÏXW¸ˆ®y†„ˆÈõš!¹)RŽp™ÇæFˆwA""†„ˆˆÜà’ ƒ­ÈYoåU¤œÖaó’½›5Œˆ!!""ºC2¸—qéù©×®‘hk¤Á³FÃñ|ù+™œ¹'5è6nC’ ´ãSb¼Å¹X·[‰œrá*ݨgr¼?~WÑÀÚŠ¾T¿mÜ3«~§lñgßfô¸ÿŸÌ’Áò[‡…òX‡åä÷:º]B+ÒÎöm ÞàC8ƒa°ì×!OæÉy(Õ0i·¿ñ¾‘ÓI݃·oÑE’s%¨(Œž#9ˆkï{Ycú«Œ­Œ—™vlTïÝÕ8{íáìYzæ,¿Á ÅY:á‚~2»6$s‹µ× BuSª§šÛàH›³E#e¤NÓg]¿îàSÓnC9+èÚ #ºPCr2\Òˆ†,E˲7ÅBšÚ`¡Œ`"›¼uéhíØRŒc籕ÚÉÆ‚ñ“d.F§ »»ßÿà÷pð›0ýÝGz&ø%ì~²_¶T§5Þ´{AÛáÙíö'³SC2>âo,$q™º¹7E©÷lpd9©TM«“‡kHºv+~:s ÉJ #ºDC¯_>ò³Š\`S¡œT‰‘hQ÷h‘¨Güö[iªlì,þ•Ž®ºu ƒ/É~cá>µïá”IÈ‘_A¹gRwØÔzÉë:-x žþög?åóþdö;C2wz¤0‹Š+DšÛà ©5#8=Rž“M•ûMÝGÜÈYF´gCŒ§Ä£!‘È_|³ª!œ¾ –•§bö[)Ú⑸Y†$òÇnþ˜àä[{¸_~Vz÷ZÏÔ.‘‘náwŸ!É6û¼g7Ålð“Ùû’¸-ÉN)t‡ïÝøßà’Á£Gñ¹ÈÔ>ÎTçÄ×sG~ɵ5šC²MÈö?C2xa ® ¨í¹P7 IÎÚ”¿Yl%{²ƒ»ìe¼0ÂKÍ'6üÀ”-Óß}|J­gÜÆ¿Æ#ËÛÖ8»ì)Ÿñ'sY¶â×ú¥5mñU}ñ+æàZ Bƒ»´ƒ)tHÄ —@ãDº‹›³‘˜Ô¯tˆ]¨!)ÌÇN™Ñ­­HM¹ ¾Å¸ƒ*܃ö[™µ l CR[ˆ2qØôMIKPŽ3fß}–!)Äã6{Êè<54?ûÙœòÆ?™ë¬Crq#¿ý4xû´tù\ ‚ˆ®Ût×{,¹ô‘­“©ÛóÈŽöY˨‚»ã÷[i,ß n;L}SV¤¶ç¦bp݉¸ÈÏ!µßµ¼:kâ»Oé™Ôµ¥T-‡\»?êì’­žÝ>2×fH¦äÞxè¿ÏÚ³jåÄÚ |éÖ6µ_Ö®!{r¿Íµö‰ÍrbUzF‡ïá§R;Câ&}Á]1wV\ßê=£Ã""†„!!"¢/3$[’8kn=’ïlŸ–W”‹ˆ!aHˆˆˆ!É•žkH‚Ç©å:œhÛÍô¹^3$k\ñâA«)—Í‘]ãa5?1"bH†¤›å`陇£ Ýò7‘ÿ=„\.¥_<>l*±É!Y½$^«žˆ’øØ}¥xÐÜ6ŒäÏ]»gNÞÎju]"bHV4$‘ŒríçÄk#Æm@ÄŒT¤Ê®‹ÔŒ<“ˆ’¥Jã¡F^È`ü¥H:¬P¦6ð:Äö7Î4Û]©…µÝ”»í?Ž„öˆˆ®Ó'OŒ›“w‚`©©à ã‡í¦š>‹!9Ì® LDWlH²£ÿÁJçÝ€N¹a…ÀSüÚ^. ,¯™â8éÜÚq¨ZùÂ`QH"¢k˜!©MœŒ¥ÜHð…#n$±+L¬Gªçp#D I7642âGyRñšB##q®xYåîAjñ¬ACŸ$ïÚ¿xhÏ-ƒˆnÎÄ7KdAÑD–Øq#DnpûʲÜ˜ØØ Ö—)®¼Ùí‘;b{=tw­p;çoww¼oãSFÙUÎD®×;4$ñ×”lpÇÈHø)£Y£»Îò!©Ïk¢!鮲³v—È î’ê\ܥ꼥?Ö>)w¢œ!9¹±-¾/.Li•‚û¤»Ñ¢Uû!‹,Öµ£½Ñ!…YrdÊŽv"7¸K2$wZ¯Áûé· ¢+0$$¶¢£ˆˆ!!""†ÄP[‘CBDÄ1$Ì 9˜$>µkÕ¨Xw þYb{–2CBDDtC†d0ÉÕÒóËéh m(\#£ðlÒá‹3$Óßh)¯N<9Œ¥ ÄL7ÿÁ_V00å7¸ÙÅíìG»èKÖJ·† mÆ6âÛ2$©êDºÇQíö3§’–Ì-E’mp÷.ɼ4»Ò8ÍÆÁµ–^¾ô©5&‚y!ÛŸT÷£O]µÛ5›ÍŸÐ•’Íf#³6£pÕÍžQ7'CbÌ}¸ÁÇá'†$dHæk¯…íŽ;SÍmpc(œ:TwhÞ8x°*Ù±'‰œxwŽ(u ñ1MŒü1•¸¹0ÇHtë†d|Ä|ÍŠ’àtDª‘÷*G.»ÁÏò6ÝÛpûöo|Æ&XŬv S Éø÷‡èÒ I#Ð |t+——#ƒÃÔôéÜ~ˆ‡Æ"‰„–‚½ya|qoöâw†ÙOpÏ}Ÿ-ìvš‚÷ÄgHæND ©íõWãÓ#å $ãn$ÛÎøR®ÔÕpÏsÝHêƒ#ºDC‰§¤–†¦æ ŠF##ek†dz?”C…íñ¸R!R61ZüV?…KéÃAC2r &bHж$ø#DÁ)H 7$#ë£"†$˜ŒÌùÄß«ReHC´ç’Z…òø£ÇÎg$B1™šëFjý0k¯ËÈHº±òjÐÌ Õecd—؇Ùi1n„’Õ³l¯w ⑉‘ø2åÂU#»£½=ÒMó²ËŠ ‘ÂÆ[Ä—Wµ×vÇã©íƒŒŒ9FÝŽ!‰‡! óœñ¡g9Ð |¬Ñƒ+Hg¡SŸWêæ˜Z€7Å\nÖ I!.IĬ•kòâ~f® ;é1$³fHN&‚» Šn0%²Ç·-Zµ"¡±È&ŠY»±#‘¦Âdu$ZÔY ÆÛ«©w؇‹ìäÚ6"†¤2¦¼¬ß˜‹Â?ý@ ‰~9Ò?:“ˆ!!""†Ä0QÏèL"78†„ˆˆ!!""bHhi¡×®Ê¸–÷sGн,uI"×k†„ˆˆ’ É`’«¥ç—ÚP:Ç·îMôÙÿ†$’’!!×k†d¢Æm°øÝ”†­ôî‚VDt+†$Uý£‘H÷øâÕ~æáT¹¥H² ^‘g›½ô„ÁL¸‡^®áÔ‰3$D;4$bC#ãûH!©ñkÚz1¬•ÊeZÑ® ÉÜbíµ‚¦ Q°OÜM”<¯Y‰ð#Ú³©ÜSéÕ‰\¯W5$bCñl¼‘øNj£[/2ò—vÛ½t·ñ乂VDt%†d|Är·kH‚·œT#ïÍ GÆåÙlúÝ’ŽÙZ‡k’ÈÛŠ’ëõ™êˆ gÚážl˜)Ò )CrÒÚÅÃ=ñ.´"¢+™!™{ ,Ìe·ß}n«ÐàHØ©ö¾e7’½©ï…»#‘ëõC"6iðzóÞ…5Wñ"÷©óÝÀZ¹ÁíwIüv¼mÄGÞKÕOg-op-¦µ¥!©…Á¢ý̈ Õ6œÅ4Úl[탴"¢+7$©=m%§‘›_wáÓôm”ÙG²ä|âË$ÕíÁxUðÄ#.7r½Þf‰ØÐ\C_Y”]zÔÞ“Z„¼ ZÑ­’¬.î´·[}KĈ µc"Ùíyìho‡íÙåO‚VDÄôï(;7!{nðõ$‘ëõ>ëˆ íüÝWJLDd†„ˆÈõúl†Dlè"Æ÷Ó ž1$DD®×*µC²cCÌ#™ÝÌ=‘*6î&=‡/CBD»6$÷æjƒ!ÿZ)¥ÃpIÝT6ú†WIÕ¯øÈr‚š!Ù¸ëºc‹‘^eHˆ!i’¹ÅÚãEZ»ö£fK¦7xb¼fdÖ"xRão·dHâ• #q¼v$1$ëÎD¦wkÅe穳?ö´Ne7R»cméF—pŒ÷*7B I7642â/‚3²lǹF¼Ò²°Œ*ÕZϧÊÛëÓ‚+Š‰È n†¤;Ì4$ÙUO…Rá}k 6uÖÙÍ꺠!©5‰!!†¤})ž;=Òý±Ç‹m·K˜§ö¡í0ÔµÒôÈ,7’ Nt¤ngDäw6CÒ^ŸÚ]†›DޝzÊ®¶j¿KcO|d^ûÐÛý dÏn®™óéöª»1$Áí|A[üE•æn )4x›P× !),91$µµ[ ]€!¹YëÊ{ÝWüÂ>W"†¤6k= i2ÖH±UhðôP×!³U½vRk¼×ÈŽönFD—h"78†ä Éu_ô'¦+%ºMCrõדó6x¥wß2q0CBDäz}fC2^¡o{°Ÿ¯Ñ†Uk³1$DD®×*µCr9†$XT±›Žð#dSNGˆ!!""º-C22Žäf©•¾lI{mÀ[&ú+ž„ˆ!©]( •[“òPíÿZW®¹Qhg$+ÚtÎô‘Å‘èV ÉRJÜ“Oh”É‹’Æ‚ÅãáêNK5éƒÿ›rb‘ô&Òã1$å~<+nü]"ÇœxbHn¤s68÷M¢ë4$'-A7œ6ÑÄs;nÑue… j¼ OBÄD.Å©¯Ýo;Þ”M›[Z-U"?,¤$îæ >,W…ZºC¤ÓM…öNÞn¼sRߥvxô²<1$÷ï‚wÆÙ²ƒ—2ø„Hù¿‚!éž{üJ×°pÙ\õ CÒ˜Ôju·Ëù•‹Ï6"2¹î“ÃôH€,Û¶ö˜;^d05ÑÑ0$7Ø9ñ•]›õË¿‰ÜàvT©½vl‡ÓÆíJ¶Í©0^|§{%]º‹p#D I664%Z¿Â¯aHºÁ¦øf¼`Ï´ŸS8ÙnF–ì´ù vΠ!9lµ•È nG†d$F’½Ïe—c¥¦zW F®¤…>ta%ºÙ’Æ¥ 04ÌNOœrI¹‘Cf“}<–‰[MôxAC2¾þùr;'^8’!bH:û­#±ÿà­®ó7²º»¨ôØÑ^^²U»¾ßë@»Gˆ’ÔÆ¶ÆŠÐé†$¾H©;ìŽOgêwz[ó{5kHn°sj†d0´GD—dH®r@¼óS˜I"¢[˜!é.âj‡NR+¾²;Ú»qúì†øxBÂnÊÄÃÂþï`S»]ŸàjoŒÔ9‘%v´1$<ɦmsU%ºMCâºê’x­ãC$bH>twA""†ä—Ʋ—Þ9>A"†„!!"bHˆˆˆ""†Ä¥˜ˆˆwA""†„ˆˆÜà""†„ˆˆˆ!!""†„ˆˆÜà¶4$€6 1$kÀÙaù—qcHœ3”Æ`H0$ †CÂ`H0$ †C † 0$`H0$†C€!aH0$†C€!aH0$`H0$À`H€!À`H †„!À`H †„!À`H€!ÀC€!†Hóðïÿî¿þþW^zá'Ÿúð=ú¶?ùCÿôÁÏüÄïþ§WÿæGç0$ÀŠ|óþŸzöÇ~íÁ'ß~ãsûÖk‡¿úòû_ÿÒ7ßüõ×~ùÓÿgyýK_üîw¿«—`7òÜÇôý—_äCŽõÈ™¼ú¯_xñ_½ðÞ{ïé+†˜Éÿÿ»ŸzöÇ–ÜÈc}é×~áß~îßè.†˜É›oüö¯=ødÛ<ÒÃw^þ§~âßø†`H€i¼ôs?ùöŸë’Gúó7>ÿâ‹/ê1†˜ÆSþ¡v±wõþÛ¿ýä“Oê1†˜ú«¸‘äWÀ31CÀg㥟ûÉoüá¿‹’¯ýÑkö0$ÀLÞø/¾úʧ"†äÁ¿ü™·ÞzK0$À4Þý¿ÿç™ûÑ÷þò‹m7òö~þùç?õðáC=À3ùݯüοÿì§nä;ÿíËÏ|âŸó›ßÔW 0™‡>øù_û•_øîÿ'çF¹‘·ß~[G0$À*¼ÿþû¿ñ¿þÉçþÅŸþçÿðÞ×_ÿÛ·^{ô…ÿÓÿ÷ÿyéçîùç?en€!Vç‘ñxðàÁ“O>ùèÛþƒ?øƒûØÇÞzë-ûF`;¾ýío?÷Üsï¿ÿþ'?ùÉo}ë[:€!¦ñÄ?æø /¿üò×¾öµGÿxçwääÚ‰¾óúk¿üég>öô›¯~æ;ö›þòî_|áëŸûñùügúý¯‰!0$D뺑¥9ã?.‰““!m—ÂÐNôȼôü'ÞûË/Þûû£¿¼ò³< C`Hˆ¶0$Ç?­mNîýcéµ]ëB´åJ­g>öô±yìIžûñy¼v‹!0$DÍ´ ÉIß²´c$ò¦>:—^yáÙ7_ýLã _ýÊ/½ü³Ï0$†„hë%[Y7ÒÝIÂÐõѧ?üÁ¾‘%½û_øÈSO2$†„h]CrÒ‡D IcÚ~æãÁèñ˳ílÊã ¾{¥¶NgÊK¯jôØÉwâ?ŽèG Ij“C°‘wiIüiÝ?¶Oçäà»üÖ“=ý‘÷Š’Â‰ˆ!\á ÉÜé‘î~Œxh¼±Œ§ýÐħ|Bܔߺ0ÉÐ>ÎYÜHp 1$€›0$Y[™‰?Ô˜@˜µd¤Á…ÑyÖd—HM1$…uk C`HV4$©ôV ⑉‘ì§ñ[Ùǧ)º§ÙžÞÝDÞíÞìb°ãgŽÌÌt—¥¿…%[D €!¹ÅbûlðEt£Ù "†À윜m¸¬B É`"bH É.fHˆˆˆ!0$ CÀ-’öÎæ«YØSÞFS nsßrUÛôeƒs$bH7mH¦ øâ…÷¹1£¶GâBOöššq.·ÀC`Hþ¿öî(ÉqœWÂèÞf—wµ¾ot”-0AR¶dŸ/òa¦Ê–AÊÝÍ àDCrT›µ(b{T6÷e?õ¢ÏÉyo¾k½™Æ]ûrÅ/~øï{ëföa<‹“œ»”°”sRöšˆ!0$gíL´-ÿ³Xœ~KØMïßNÃOÉ?¨{dëFƒ-v‡ŽF‘ض֗¡uÀ/ŸäÖíkMàcÔ[“ˆ!0$mCò§{ÝËÏ‹†dø–nŸÄ•ÀÍîãݵò]û5¾ —éë–uû$o4$N¯L"†ÀÌïkñ³ÝH~¢&ùÉÅÝÈE»âF¶Ü‘á&7B 3$ùá«bÑ6á&²8’3<ù Î3$×l½(ýùb}}YÿNCRÜ=iFÄ’¥’á!®$£½•=œäÖÕÅSÿ<[c%£ýƒ=E’Ñ>ÌöžHß;É]‡ÞíÄ’ É×· ùš®[‚ —ï¾D É[—ž×_Å®Gø þ¸bH`Hˆˆˆ!0$ CCâ¼ÍÕVu¯¶ý¾üÂá1"†ÀlèÔ~)Còž8'>夲KŸº/Ÿr C`HKÁ¾¬…ú¿‹ÆÖuTj¼¾¬;\¿÷¤· ß5]K·.ïÛª¥{䋆µtëÈ]JXòX-]"†ÀDÝA«Äÿãåë[Mßî‹Ã·<š “æ€ù ¯œ˜™Öx»=ûòyní`´æ0é(BD à› É¿O¦ë¶}Iĺo]ý¤¢Ñ{ó#èö8Ñ€¯Ûè=_¦OLÂܲþ¤öçohÜNÄðm;$ÃÃ9áVCxn§uÙ|åšÇ<áÎs#Ý)½²Þen„ˆ!0$3†¤X…—é…!Éß•8¹QL $4$ÝEÿ„Ç»…!™8˜GD €!9<ì”/Ó‡ö#¹lž'=¼x;^ìoä;íÃlï‰ñnFûÜEòÊh'bH IeH´ûØ~ï[[s D €!aH®»ÈNÎq™("bH CBDÄÀC`H¨(ÕÊ´ÎӾÚcW>šõ›óCÄ’Ó I½@œ^ݶ޻Ãô²µU÷ËÜÿö•o –!!bH ÉCR<í.Ê×µù÷·õ+_Ém.|ó€»]AŠb¾Ï«ü£1ÁÕ>ª­<7áÝJ¾?5?D €!9ÑtWÿa“¾V_¿a{ÁÖ¯¶<Ñäè‡Ã6‘G»aÁ0žb­?± õ;óCÄ’³ ÉúŠÿù¡õÄ#óáŽGä¿—M"I^vö‚»Ø¦Ø5„dÂç ÉïÌC`HNÜ!Ù»=2L É{9Óõ¯6Ü]X×?¬&µ–éùˆ>íF~d~ˆÞ‘C’Û’d$ÿÕÑŠpcÉJÀõažâá}})<9¶rÐhzþ É÷ÍCÀûªl…å­ê4åd“$?>´¥ÄV7à"z"c;·a­œï£³Fц)C÷;óCÄ’7’óÚV\§»È|ô\eóCÄpuC2Ñïv-oºü]©ù!bH¸ß 1$†„!!"bH`H¾EEK“[œhÊã³Ø§C2“þ4C`Hz†d±ÈUXù÷Ô¦W—jI±å£‡™XF¿¬mõõåΘI"†ÀÌwÿ(ʧ>—R­_ù8èɽ±I7àºm=®zº†es_^9©V<ìb™¿·¸S5‚ÍäÊCÀ¥ ÉÞfíaÿ죎uë·\,Cë²¹Ée“Å}>cM]^öAï¶ lí˜I{&Ä’†d}Å_´œkµ«›«H{Ôó.‰$ c¸a’PþÆd¿•€Ã8W ÉÏ$C`HNÙ&äyÅ®þÕzÀáâûO*E>Ìâ筡À§íÙ´ùÁ™$bH Éüá¨d¤û°yËI­äj‹Îáÿv—Ñ+³7}‘ä´Òú›Þùµ™$bH ÉRy«"A<Ù™>4]bk"àÜ8½¬:f]/kåa¯<Ì÷˜ºí?>“D €!¹JAÕÛU€ýñ-f’ˆ!0$Ÿ4$·KÏ•O|Æ|š"†À|~‡„ˆˆCÂ1$0$_w0é(aú(?¾î˜q—ü™ï;CÀm Éb‘«°òï©1L/ó±I½©ë,Ê_V¯êÎÃMÇNÄpCÒêþQ<,®sZ¿òeÕÔ½­Hºç‹ò¹æ-ÃáçUnÃÉlÕêý¦±1$ÜÀìmÖ>l W÷•[ï¸7à¹&ÜÏ~ nð—ôÏGÑj©Öö½ã؉®nHÖWü/s ÂÆˆaS‹0È?IÉvGý²â!}Ø ðh›bKxádÎ’ï;CÀ vHönäùɺ¶Þ9Ùu©»=’+’7þü¤ž¨n Ç7òc'bH¸‡!éÚ’d$ÿÕÑÊrcÉbÀÝŒðTØÊa¤é¹í–¸õ؉îdHZå­êò¯É??b´¥ÄV7à—! wròí—?ÿùÁ¶á(Â4Œ|ÃêÖc'bH¸™!9¯µÅuº‹|G”‰ÿàØ‰¾Ð¼|V}—‡ßѨqºÉŽˆ!à›wHˆˆˆ!0$ CCò]ÚuL¨h²7Ô¹W~üT7%ÆM!†€o6$‹E®ÂÊ¿§Æ0½˜›ë—ò†À¶/è/²ö}Yn«[ÓÙM!†€/1$­îEÖç'Ùõ+½½7¶"éü²úmýâz€uEÝb–êÉŸ˜Û|ލû¸wïòbIb7eûM!†€’½ÍÚÃ>ÜGïÖ;$î 8yhÝê&þr½X_3Y»·æöùšuë’n‡“°¿átÓF7åÔ›B ï6$ë+þ¢u]«íÝÜYü£ÞyI$MýÂ!¥%$Ká0ì¼ge½0qwMïô!%7å¼›B Ø!Ù»=’¬ÃÓöEªqý«õ€ÃÇêCëÕZ·®Ù}ÙÑúåéä¶v!6º7å¼›B Ÿ1$][’ìä¿:Z'mL ™xúLžQ¬J§×¾+ŸPŸù™¾éë†ÄMY¿)ÄðICÒ*oU$Ë&#­“<[Š&uží OMذâ1ùp’'æ¶TëdÔpzóåûp~Ü”ón 1$|ØœZÛô"­E,h®ÓQÄMñ'†À,u”»WC+³«M¯›â;O €!!""††„ˆˆCByŠ“>îÔÓÿÝ4ðØ£AD €!ÙcH‹\…•Oazq:UolEÒ 8i¥—5îü¨½ÝQaÙ?4¼eÝ2»ÃÚµÏ&§ŽGš5C`Hþ[´"­îlù[ 1aŸö<Ñ•onàE¿íÇqWïºEÆtG¼¤éGXwb‰ˆ!ü¢!Y_ñØZMÜæŠÃu‚K"Én’éÎ}ÉI»O†çî’톄ˆ!0$ã’½Û#Ý…l½ô¯wN&Ž…Ï5…#j <9Ù5··3íFÂàFˆC2oHº¶$ÜRu´€Þ˜@2pnHjÏÖúˆá$‡†dnh§MÙ‰C20$­òVE‚x²12}šhºÄÖDÀÉ™®ç‹ðÈÖœ?9Ê & Ïe´çiñ‡ôˆˆ!0$ïnjq‘¾"?ÛPå 3À1$†ä¿õ5彞v{<žv3ÿD 7Û!!""†À0$DD É÷pZ9n´øÞ£üò-á½3·d®ù 1$€ß2$‹E®ÂÊ¿§Æ°¾:¿ˆ!iõrÉË.¿ß-¼l'/3žˆ!0$óÝ?ŠBºÏucëW>þiüW÷÷˜nEÒ øô"|þáŸñ£K&ᨽÉQIߣúÂGãz9Àaáæá­ÜUt¸þbð$ÄðU†do³ö¤+Å#h/8mK6œ’ÂN¼4*ÃIèö[ wH «PùˆÛ±oé_7UäFˆ!à{ ÉúŠÿùÑõD›¼áŽGä¿—M"I^ÖÚ!éš±nƒÈÜ'äǽV¼ÇÑ(ÖûÄ·FJÄpï’½Û#sùu>w÷WÛ>ôÂNü@2áÛÝH7Wž!bH É‹ $ÉHþ«0E{%d1à£SC¹!é~úúE& Ix–,4 C`Hz†¤UÞªHŸÓO¬qWªEu®½Êð4WžÑ>Ìö.N@MìJÕ—*Òîó[Ù xâdOB ‰ßlH¾¾«µìçÜä1$†dƒ!9jÕwñ‡jÅ^§Ë$1$†„ˆˆ""bH k‹}$qåè¼YÝþ¼¨Î¾>1$€_7$‹E®ÂÊ¿§Æ0½N:š¿Óä%ËÞ`^VÜR]€ˆ!0$Û I«ûGQH÷¹lýÊ—eg÷¶"éü˜êÔ^ïZ»Gy9®0΢ïðv´j"‡Õ{‡Õ‰ˆ!ü®!ÙÛ¬}¢ayÝ‘}Â>í 87$…xiT†“ÐjÐ1ÑÖ0œ‡n¯˜G³ë"1$€Ÿ6$ë+þçÇÞ‰!IÚvƒü÷²I$ÉËZ;$]3Öm¹1Îa$u0®1$†dÛÉÞí‘VþC½6-öÔ¿Úðn¤vxñ97ÒmhÈ1$†d§!éÚ’d$ÿÕÑZ|cÉbÀG'ŽrCÒýô¹‹ S>ÂH"†€’Vy«"1:/ 5±Þ.±Õ ¸ö*IÚF˜Ñ>̯OO ãÌ3ÚëŒóÅŒöŪkDÄ~Å|}ñVëàÅy3D €!ù°!9jÕwñ‡êÌîíID €!ùü 1$†„!!"bH`H¾ôœR÷´Røâ-' ò‹ »‹\?¥Çí bHßoH‹\…õ Naza·khœÓŠßí bHßlHZÝ?вÏÕcëW¾¬»·I7àG³]ãË÷½,Œsº€ïѬæc?®HVê»gÜbHø°!ÙÛ¬}¢ayÝ‘}Â>í ¸[ 7l›X·ïè6÷ÎêË5zÀ‘lßõr;¦#!†€O’õÿócݰ§x²Ñ òßË&‘ä×O sÿsÆóë×Ûg,Œd‹!q;Ö#!†€ïìÝ&,žÆI~µpÕÄÀ“Q×3>çeòFòÛ#Ù’àáv,FB Ÿ7$][n)äGtê36ë $s&>wýú¼ÍÄpº‡—΋dݸë‘CÀU I«¼U‘8›¬ÿZçy¶œí™xø®$¾õðRÃO\™ÕbP­³vÝH&2ÚÝŽSo1$\ÈœÝGâ }E,h>>±ǾçÄ’w÷ ¼]sC:ubÝßsbH 1$0$DDÄÚr ¿È®OÔ´2¼çB}™¢í^1$\È,¹ +ÿžÃ/±Ëäœ0$/ 11$D ·4$­îEÑçb£õ+]«7¶"é<¼Hñü=ùp^–y]©Ÿ›ÿ("bH É~C²·Y{Øaú¨§Ûz‡Äí‡è¯úgç;'ü[+Ú|ïèÏÛ“aò$D €!9ˬ¯ø‹¦l­†ns%b‹ÞvÃHò€W3Œ6é¯7ôls %먹٠"†ÀìÙ!Ù»=2ÌÇhmÔ;'ù9¥•€'.’ŒñÏ+Ã=œÂÞäÉ-E pn„ˆ!à݆¤kK‡èá¯Z› s $ëç'»ò’äØÊ&ñ×ÿ¡Ÿ:CÀû I«¼U‘ žlŒL=š.±Õ øèÜWžÑžl¹üÉûO2ÚÚÖ.M1$†ä\Cò¶f·ë.r屜1(>„ˆ!ࢆdØàï‚ ÷{ÜçFÄð£;$DDĆ„ˆˆ!€!ùºÓPsg½&Î;]دŸoc®ˆC²Í,¹jÒ=)†éåi^#ë Ù \d¿lTrÆþ‚¹"bH É6CÒêþQÒ}.S[¿òqÐA|c+’nÀÚÅõ•“r½Ã )Ê WèަǨ}{· ð¯ÍC`H6’½ÍÚó¦Cû1gKöœ÷PÏû &‹QÔ] Wb{lj¹ø#sEÄ’UC²¾âÙL0ypž÷ƒü“É0Œ¤û´>ã´Ã)Ößëà 'Îüæ\1$†dÃÉÞ푉|ŒúÓëóWëÛ#ù˦7^ZOâŸùÿùI=±ù§ot#¿9WD €!ÙcHº¶$ÙÉu´òÛ˜@²pý–éƒIáá±]ŸæîE·„ÀOÍC`Hv’Vy«"ý7\ÖçG†¶”Øê|Rq‘V–vw»æO…€ü ÜpÔaZE¾ÁõSsEÄ’͆ä¼V×é.r»+¿9¶7¬¿¿f®ˆCòC2Ýï -ïråón·2hIDATÏÆf®ˆî±CBDD €!aHˆˆ’/RÝ.0¯:uÔ¹oc2ÃÇC%"†Àl3$‹E®ÂÊ¿§Æ0½¤>ªnœ´nÜFË|0T"bH É6CÒêþQ”I}.™Z¿òqÐ{{c+’nÀÃÞyÉëú¶õØ' ï~$T"bH ÉC²·Y{Ø'û¨3ÝJ`a«Š<à•nz]ùVZ¾?T"bH ɪ!Y_ñ­åZméæªÐõ¶K"I^Ömþü°Õr‹!9)T"bH Ɇ’½Û#Ã’a«ï£ãFù¯¶¼q‰_G;íFÞ*1$†d!éÚ’d$ÿÕÑÚwcÉbÀGGª&VùÝ©X1$g‡JD €!ÙiHZå­ŠñduÛ:³´¥ÄV7àÚ«¼LñÏWùõ!¨¹Œö„JD €!ÙlHÎë•qî"w‰ "†Àü_¸Ûpý‡· ØÂ‹ˆCBDD €!aHˆˆˆ!0$¿£"ü¼Ü••’ü-·1$€2$‹E®ÂÊ¿§Æ0½ä-Jè^yÕþ¿PóOaHˆ.gHZÝ?ŠBºÏEcëW>þé5þçí»Z‘tv_ ãÿ³ Ÿ«;œ|V·Rð]†FD à' ÉÞfíyǽ¡ý˜³%»~^sOý‡Ýè[½GêÿXl_x—¡Cø~C²¾â~pžwÜ ñ„Aþ{Ù$’äeGÃÞŽIøâ:­Ïš;Òvý¡Cø‰’½Û#Ã’a¯ñâØOø«¿LÏøó“¡m«§"ܱÙëF®?4"bH¿bHº¶$ÙÉ•$9,&¬ÜúÖª}â¨ÛvCrå¡Cø-CÒ*oU$ˆ'#­3H[JluîŸüIÙ¯wo£kE*K+I#ß/ºøÐˆˆ!üœ!9¯÷Åuº‹|_¿ z"†€2$/w®ßâð‹+8)NEÄð£;$DDĆ„ˆˆ!€!ùºsP{Ïzå×¶#œûˆ—Ýè‹,v"bHx·!Y,rVþ=5†éUuÞ;¥Cwņ!IZ¯0$D 3$­îE!Ýç‡ñõ+½Ã7¶"éœ×,®w‹ó&ÕrÃÑ1$D ×5${›µ‡m¼¯º†/–¯¼ó€ëîÃ>ÚÖ¡Nt'<ŠDí`"†€Ï’õÿË„„V×¼0óaø«?É’½~Y±‚Ÿ³1 Ih™†C˜>FD €!Ù³C²w{d"£þôúüÕúöÈ„Ùxéî„„á CÀ› I×–LLªå/WÆHž°'’¹³[ CÀ I«FS‘B.ë‹óZg”Øê\äZ 3Ú»Cc°’ÑžäÙ3$D W1$ç5Ö¸Nw‘~¨?1$†ä¿õåõÞvïlqøÙ¬¥ˆˆ!0$:µ1$0$DDĪOy]á|Úħ×yöW…É'bH?mH‹\…•Oazñw©ÂS‹}©5ñÿ‚Ùõ50ùÄð…†¤Õý£(5û\g¶~åãŸÆä/Ûk¬·"é\Wà­ÇUOWX>¸ÕBþ凶FZ\¡{ßîf«ð±Éß8ùÄpC²·YûD›óº#û„}ZøhM\/©óUx룇ý“†‰ÅGmaývuÓ|¡lò×'Ÿ®nHÖWüÏ~CR÷Fœ³%EîÁDÀÉbºõAùÃÖŠÉ‹åòâüäӸјü‰ï01$\}‡dïöÈ0$ÏÓ(šë_­®¤ÿ<ü·Y¼qã‚ø(y# ;yÙ„…ÛâFLþÕRžˆ!0$ó†¤kK’=üWGëª $·ÂWf/¼ÈÜäg¦oîÆí“¿2½Äp9CÒ*oU$×&#­>[JlMœ§âAxž\31Éçþðë ó»¹7£Ýäw'ŸnfHÞ\õvå\i¥ˆ³É÷&†Àl0$iW÷Åß4š|ßabH É);$DDĆ„ˆˆ!€!ùÜñ’¢ Éoô7!ëqþéÝ>W+,™ÿ3&gñš»Bê^g¢IQÆwü¡`H_kH¦ÿaþláÑKµbXÿè—%¡îë®iBÂîQ©ëÖùƒ·ï"†d¥ÖóvCò(Ào’¢Òh«êhÒÍúesë—­â†E]žŒ•[‹â°IÙz*ZZÇ0×_ò¾òõÄL¾6à ¿G‘<JRÃz½|}w†íJ[÷1¬h¼rû&˜‹&×=WÑû.(À’nÓ· C’<„®{Ã%Ý ëõPxÙ‰võ…z´rZÙº˄t»^†ÁL±ç Íé9lí¶ÝÐî±zJçºÍ´ IýÍ©}Zâüïò‡‚!|›!)U׿¶’ds \GæBóav¶'¿[cÉWNÓ‡U®„½c¢=ûLÎâuÞÆI3Vtü˜¨±F w2${A«ÜwXßeHæ à.FÂLÔ¨Ý>c­Äðý†¤.©™,Î’êÀÊŸÝR§a!Ñá.ÄðõÝŠ±uAä°ÂlÒ­¯þˆzNòʶ­z™Ú‰Z±Ý•ÛgŒ!!†À¼~Üí>–t'{„Õ])&0ç— ›¯­ô]Î;¸ÍýG÷·õ5Çn8ÍötS¼‰/@þeÛ>cÝÆŽÄp{Còçz²Š*޹·NgµZãåÝ©çzTw;î2$u§¿ä•a/¹EC27–¹ƒL‹Ö.ù~εík’‰ëöË#†€ïÙ!É÷ºGYÞéF¦×¸Ÿr#¬«zøqÉÃŽÑëc™¾w[6šò³U[>iÆÂC‰Äðµ†$\ÃÍ2.é¶’éË& !ÃQç§wZ#ùEŽRwBC2win}¿Ë‡´¾Û ÉÞcHˆ!Ø!ù¿0Ÿ¸µ ¬sÜç\P7£}qÇc8ê—ÛDÉÔYàÃCYù™ŸðœØD~v‘[ÿÎŒöb¼ÝïUË„ï1†„À’³K辿Œl·ùƒUù&CÀ×’V{‡7/4­AÉ7.gHˆˆˆ!€!!""†À|ú$L‘ð½+ÈW>;ÚóîËDäÝZUs“³8“É'~üf­ñ#alù¶oÿ[bý‚—ýÃ˾ּá_ß•å`^x8°oã×g#ÔFâTC21KoXìNWþ2CòÙ*ïù[¢ÉÕþ83$€ß2$G%bÿ-¢šw/ŠœEMúK´.[TÎG½·qò47™Ÿ—3VONØF£®oÛ­±[´WO*ç_¶¹‘­; ;ÙœyCùãÖ×c½üñÑÅ?ò·Dñ²•CòVCrôôt?ìü‘ü°»b«yy·Kc=ê½mã皇T†‹òĬ´Q§äãÊïÔÐ6äïmõ@\Ü*hý°õõØÒ 29ù¶¿%Z™L¼ž!0$; ÉŸS×Ý‚GOÄ“kÒìohH¯“’îB­;™Ã¤‹áõóv‡ÉSáS ÉÄmÊMEؾpzPÝ»0wüi±u}ëëq†!ùÔßù†€kít—‰“É—æÃÕÃħœêF¦šÓ'Ú“y˜XmY"¯¸—u›´e‰ßÒÅÅý¢™ÛšX̺¹Î߹Ͽ£aH¿eHòsSÝ¥F÷DÖÑõ×j´.¾bHö^vx#&L]’æÑ=;4wÌ©5Àa`Äé{ÔÍ©Èk-,þ j}=Î6$Ÿú["¿)ùw€!0$o5$­ƒ4áI¡‰Ãú¢ëG¶’ë«Ò¹q­„æ£lòVo‘¥ÝÍhK$go†)uF{2¨"¡<¿5É ¢·e´w‹ ÌY”Ký-1Q÷¢þ>8²Àû ɱ½~=Ó-5‹¿x¤ڲ[F €!ù/\”\¿µßû»Rœúé܈•47B €!!""††„ˆˆCBDD €!aHˆˆˆ!0$DDĆ„ˆˆ!à–†p_ü£¸·!†C † †„!À`H †„!À`H † 0$¾ÜÀ ó†ÞC€!ÀC€!†C † 0$8äÿ Mj!àå¾IEND®B`‚jbosscache-core-2.2.2.GA/src/main/docbook/images/TreeCacheArchitecture.png0000644000175000017500000006247210660354341026304 0ustar twernertwerner‰PNG  IHDR—[Ì2”sRGB®ÎégAMA± üa cHRMz&€„ú€èu0ê`:˜pœºQ<d¸IDATx^íMènÕuÿojr½Q¯ïÚ«WÅ6&±T­¥bÄH’š`l-Vš¤ÚŠ(fÁ¼Øf  Ah BBE°ƒÒNì¤ÉÀˆH fPèÄa⨙eè°CÿŸëòròœ—½öÚ¯ç<ës{ï³÷w¯µ¾k­ýö¡÷Þ{ï„ÿGÀpG`‹ÀâY~¿øÅ/^zé¥^xá©§žúÜû¿S§Nmï³#°ŽÀÍ7ߌxßÿýH;¿ÿøÇY4è8ùÙÏ~&0>òÈ# zûí·»ø9»Dà®»îBŸxâ ¤ýÅ_|ûí·s©ü‰Ä†þû¿ÿû™gžÁ®ñã?èß+¯¼‚fòû¿ÿû¿Äƽº#Ð!8¬ˆ÷›o¾)ôóÐCá°BBÿò/ÿòî»ïvØáÞº„exíµ×0g—^z)vM`ä_@5£iëmÔÞŸ#G®D±HûsÏ=‡Ãzã7ôbI‘1²ø¯ýk>æÌü BpìZb?¼º#°]hÉ£ó¥©Ä¹Ó³]9÷žçEàÿ÷ zÉêáΘi4šÅ‰6p%p"ø<\žwTÞš#°up·!*¼[þcëcÉØ,”Àâ.NFT½©} «’‹"ŸM‚ÊÀª,NÀAØ Ãâž-߇ôø( !@Ð)kçž"Æ*a›°PÎß…„Í›Ý äÛaXÖ¦£Öæ´,Ž+-+ßQ­ï\ˆ#`@€/V¿ðz u÷Q…«„mÚÇp|Ž@i†hYŸÌS±8N4©0’ø¥àí;ûC@vÀ¡û‹ÓÏjŸçíö'Ò>¢Ò`.Hæ‘üÖ|(Ìâ4tœ6HŸ—q4࣓ÇãcƒXMPÚ €^Æ8BðƒY úÁk,Ne\i:Bø|ÈŽ@^X '»®Ï’åýzÍÖX}#u—~~¦fŸý[Ž@Ÿ°E°žÉ[cq¼i–µú›÷ÊØ’%Û7‘“o€ÂÍgf67§ÞaG 4²Uv…ÈYœcéÜ/SºÞ¾#pT°a{Ç[LÈÞ1:ß–T"탭€™-‚ê¥ͳ8!8¹ô óO8dž$Õ׺¶ hü8Ù'ÎûÜ?ì2YZÝžaq‰ßwieúŸ*ïá1 ÕAx;)©;x;”ÇèBëÙ5îCßwƯŸùðž9;[±Ú¥_rä"êÃï BklÂ샎²8ûÚýІ¨É“;î±bÃÓLxLÃkWþÀC˜ÇS…ä¥}œ=c,\íb¸9òx¦û`¤ &OFÉÛü°Î¼L!¯]ÉŸö!G;Å….G]ÖX|¶D¡Þl½Yh{x $A:<ÍDÒcxíJ[”·kö½9yëZ¿ÿ»‰_WVìê£ÚóÙf mãåËcnÃ;•˜ Œ/SÈkWò'Œ†\üçþ{žÓú}›æÕ+÷Í)Á)AÙD ÁJÿŒ¼]ƒfR‘l‡Óyç#)€™ÞúŽnBp’ Gx3]”ˆBÞòtê—¯Üu„lÀâÀ+wk{t…ù^ £qÈÃX„~Ã⻉ M…ÂÄ(¡Ùf‰3.±»ëd¡™ÚP³;P:ØÅïh[9¦XÞªJ¹[È´C¤n6>Ò ïê:J÷ßAXPhîå1uðIÑþ¡Ûâ_ûjb¡YÛJ³›N€M­À^¡Ÿøýâ¯çJ‰~±jŽÝ€Ñ•Ñ|…aú'ê#€'‡K7pÇ,Nޫߛþ¿H&\S/ÑUñ¯ý‚¼Øn¥ÍMßÍ û?¶uµ~B±â÷—X;Âãý{ÐUm6ûü6°Ò,¾³s/¹pG[«¨Û‹NÊžö\}öv¶…ÒÅriQ+××LϽ”ûÜ&Z–—` ùýÄ÷Ä]~ÇÎ&D¢D'Ç÷,N¸éË´c¬£†KŸ$y¼r£¦<}øGÞF?ãbM50±ØjŸÛćj¾yƒÂhø=Ù›Œì„,Μ9#›$α8nyÝìŸÙnƒò(S‰lØ &þˆûv&±çÄm¤|©_}£Ý.T“÷gI£ú#îåæ´ç–‡#gçXܯNOUÃG™Ä‘÷=¨=kN‰¾±K·ºDËEÛÜh ¡&ò^EååØËþ.ô-4M{jvØRsŽÅ}qk˜Úæ2‘Àxj}OʦËæ6+AWx.¨2¹’½kBáÒÄ4Z¶³2ÈÔ9Gý¼“LpÇ~2eáëêŒ.afÝ«;–·ƒ¡‰y‚㞬èϰ—Fª\˜${&/-ý~ô£­Xÿë°´æ/Ó•Lnîî—͹%ĉˆÓ=šÍ”\Qü·Þzëõ×_O±ÃèüešÝm›r÷ˉÜ䜱^ô9ØÍYythú»óÎ;/ºè¢¥¿ÿ–Ç׿ô°HŸŽ­· A`s êÍ-hf!¶Œ^C!{‚¥% pÝu× ¯œ­Ä´u‡¼¿;‰›./—'žð½TQr¿~å$÷%—\b‹ƒÆý’j3’›«¸¹…-ßK•-[¿V Åÿìg?kÚƒÕ7ÌÌHn®¢,lð›õ8s\“ÅùœG<›Ó+[‡7Çâ¾o#ê€nMG•ëƒ6YõZý à,~n.Æ÷Øiæ¦2‹{8®™””qßÖ$Æn†­ÌâŽoKœÌ½u?]¬6VfqzØÉ9³œyE Îâ”ú)CªïOe Nôñ’]!à,~n:bµ±>‹û‘ž®Ô¦PgœÅ [¨ÙØ›²*³¸Á²Ê›-Š€³ø9xcµ'8f)}ö÷ï|ç /\úkðßiyúDéF/Ù.*»ûkÜY|Csj8Ppýõ×c–,ŠÓM7íÃRO~ò“Ó»%6wvqCÐOWÅß3hã“O>yíµ×Þ±ð»õÖ[Ù£¾ô×à¿Ó2íOEÄwõ£6…zâ,^ØÍnzá,ÆaÉ\sÍ5_|qÐ>, rxã7FºéwëKÌÚ.ÛtÏ õ3êŸß°±K ÊY|CS¼žŸHýŒ:Ýð-5*[WÅ-oŸ4aqßqjñ ÕrßÐdžhÂâ~NuCBe몳¸åIÖ&,î·äÚD|CµœÅ·2Y¶™jÂâžÃÛŠP™ûé,þž!3Ö„Åm†Ã,^±>››â£Ý«as©›°¸o©¯È•¿è,ny’µ ‹#†$^eyòÏ¥ à,ž‚^ͺ¶å­&,nؽ[IÿV:ÇÎâ¶\­X<öD\º|x 5p¯‰vÊ·lO4añ_üâ¼Â’2X¯Û9ÇÎâ6»ùÕ¯~•°xåwúôéõë¥ýY¹1$ÿ;—?ïÞ›46Äðh3궇'8K–bÖëžwÞyÓ“fÈÆæ„ª¡N¯Ù=Îâ–7Í{챫¯¾šŠf‹ûâÖ1h¬³ø¶f9–#ë³x¬Ÿ±-ü½·Îâ¿‘ØÕ£Ê,Û=î"à,¾­‰‹=6R™Å 9ÿmáï½uÿ-ˆº¹2‹ûY‘#QW3‹Gíê˜ÓÖ‚ï¸d%ûí·ßVÊge¿ë®»¸dBÙ7/¶]<£þÁÜï¢c\«®™Ëš,N—èÝÓtÌËl‹Ã¾H# .‰ÇS$?L¢ÚqŠwk²xì*aÔ¼{á®pÿÍtèdc7QHLØìïÎ;ï¼à‚ –þüwZ¦ý¡[O=õO/t%4Þ™BD±¸ð7 ŸÜ®µ7s¶ómòdV—;‹¦~Ï ªM|¼d8BÆlíÃRê{Ôqúù>IPHž½Ù:8‹ÿgDŸ‡x5átý@È¿ÿû¿›OŒPqHo¢‡¤ìꈂ¥9Jó7¼{êÔ)[>ü`¼pÌðö¥’ËÅÁP¯¤<õ½b^ÿ—b7† Õ$ÍeÞ;Ž€³øoa»]%}Ö[¸ÿþûéRé¯xû dñþÒÅõÌÒÿGyäàë —;‹ ò]í\AH åâ`ÉñFÚ"à,~ˆ¿áJ¦BSØ•i(4FovŒÀ ‹Ïò·®,Á¤ÿÈÁ°x0Çî,.ÓG(zØJFOpý§×¿¸¢ígñ™Éí!æŽ'b£KžmŠÀ,‹¯ð÷,éúÇÙ¸ÜY|˜Db_Ü Íz\QÉ÷m4Eáí³qgñ™yÁn¢,bµš3œzì£ïKo…«ïβ8bÀ2ç™3g ѳ¦YvNëOWßÅÇ¢Â9v£¡Úúrx+Ímû]gñyü±§l+kâYóQŒc“O·•EÿúJF}…Ë‹fÔ—ø[&ËYü@h9ßE&¯ ‘³†ÝòM>íšÛgñEüų®¼ÖÅ‚V+ï¡­ ú×A ¸»m–Ëañ,èÁã¸|¿Å—0g- ç¦òæ2¢ðVÞCÙóFRp_CUD!Óã*gˆsá|.Ë©!å½XWY\z{Àåœ4ËÂÃI3 ;‹¯H®Ôn)Bˆ0°e=¹)xÝM#à,˜¾:JÂWØ–ÂoÓÂäOD@ÉâS.O¿Hn}Ñó·³øú\Ëf7âòD‘~¥f˜Qt,Þ¸gqt$¬ˆT é$s@ãé†X5/Ô1Q,>æòôã kœÝ¿¶Ž–¯‹¯àƒkμ:&'°•—ü:Öžãíš³¸vî±°è$‰²Œg1Ù Ãý $IJdDµ#ñr½"``ñË›ŒÉY<;æ‚ œ=¹ö«ÊzŠliô½lAü¡€³xÜ,³åMî1&.7/`SÜiÝnxž-nä^º<f/ßµù/8‹+‘Ç_‡wñ×S®bD<àoü~VÁÍÆGÙa/¶!œÅ-“…M\Î""4L&\I£„²ŠèsƘÞ2¯ÓÎâýÍIÎamÙI.êO Œ¤‰»!oüø;WLŸs`ÞVSœÅ“àÇÅfK7rÈSEº\„ùý÷òßü#"RØÉ; ñ]Wvßõô~08IÅI ‰=H]lÅ?þã?ŠÝ`›‚ü £éà_^AÀYýëA€ÎÆ7ßs³W±QŽËY\ Ô¹—*V¾55•×){€®s÷eœÅ÷7ÅÁà[ŽÏ–„Å12¤ÜåôšŸ#ߟGä,„è=}ðmÇ=@OÀÑ”pßÍTGß±áø´<;r†Ók¬µ“{÷}7²ˆ³ø"D(ÊÀf´aÛyÍÙÊp|Z×tàÛ­å,¾Ý¹zn¾SÂñi]:@ÖÐôÈ’rÎâ3@±\ s“¤BÒÏkWÇ5Så+è”¶^ÆY|»3˜|§‡ã oWx{î,þ‡à›“cyŸ/3‡ã 'Ê÷¶ª;‹ok¾¤·Y‚ï¼á¸è[$sŸÅÏA—7øžNF–p|Ú¬èf¹ï³¢³xŸó2Û«ìÁw‰pÜô I”¹«GÍâå‚ïé|d Ç=@7‹{çÅ;Ÿ ¢ÁwépÜôMH—¡“GÊ⥃ïjá¸è¡ï¶Š³x·SCÇ*ßuÂqÐ{3Cߎ‹Åkß•ÃqÐ Òß[gñÞf¤rð]?÷½O‘‹êÕ±°xýà{: ‚uÔôä*<¬ su3×¹Ë{-þë gñ®f„ëÍÑYŽ_Oo;¯ÙÏõ«ÜÊõÄÏ —Ã6oË;gñ¶Á÷ÁTa8}Þöy"ÞMâi56áóÌÖÝÓÒå•ÈcnÍY¼“ÙGMx²H.DkþÉÒÍê5±ò3è5ÑŽýÖnY¼‡à{:0(¡pì$•(KÁÎyLŽ…è%6´é,n-c•!øF#ppû¹Í´U8>ÅÖôŒò–«©½±xWÁ÷t’zǧ½ò=—:%¶ã,ž ¹zWÁ÷t=„ãÓ^y€n–·¼÷Ãâ}ß=‡ã³N†èy,ª5gñ(¸Ò w|O‡ÖO8îzºàåmaó,Þyð½•pÜô¼zekÍY܆›¡VçÁ÷VÂqÐ ²—½Ê†Y|+Á÷¶Âqг똾Agq=V¶’ ¾·Ž{€nÈ,µ¶Çâ› ¾·Ž{€žEÇô8‹ë±Š-¹¹à{»á¸è±Â™X~K,¾Ýà{ëá¸è‰j¦¬î,®J_lÓÁ÷ÖÃqÐõ‚šRr,¾ƒà{Oá¸è)ú¶^×Y<#¶;¾÷Žkt\®ŒpøàƒW¾ÿxíµ×ŽYÜœÅ5â„qøçþg®:GrxàgŸ}–•TQ¨¶O i:ŸRña¾øâ‹ ÿá‡>ƒ9€ö?ÿó?÷wY|øÃ¾é¦›¾öµ¯³òS›Å%Oò¹Ï}îĉ<ô™Ï|æ–[nyî¹ç˜ ùñ'~È(n¿ývþeŒoÁR<ò "~2L#a€"‹äŽøóB B]St¾·ºø.‚À¥—^: À›QO?ýôã?üi@Ùc{AÕY|Ih¹H ³Àÿ—×B£Á C¢Pˆ Ø!…‘Ù£#ø(ƒYab0ñ]$ìÙ«Á$yd6™S¡ /3þè£N@6ÀiéÍúíO%g&VŽâ§Au©Å´m‘Ρ.A¨ –‚Æ4§)_‡ZðܦÉL–-e}KÐ<5¨®àF¢¢¨tÒ¸³øÁD`äÙ½(‡}‘wE©n‘ΑñZÄô)St;0˜còfî˜Á(‡ŽÍ‘ÐyqÇdã9"‹).’8¤È4”¶¡÷°áoñZÌ4 zÌNîçCs:Ìš™†%‡€>o(ÿÀY|€ (dÒQ"³ÊcÄ¡@‰Ï4¾cÔd*ŒÏAŸÅ`šã–L”™bì⾘iX!ä)2ËO¡)ÎÞlA'cý†ÉàÿkPÍØè®<†Ý¹NBZÂß¹â "G"ÍŠ­7c@…àï\ Ï’L3+vÆÑjÊY`‰¿ÍžßÁì+`7ˆ M\–fKôs+S„&$Zc¾rQFvSœe®ó6RŠÅaô(<;ÝÊLÓ8]Ï‹E–Ö0Ä,br‘Hqb²tµP#̸8%è¹ç —’ÁÖ¬³¸¨6Q” À•ZCŒ[B,Ó{Kº®PΠsƒ9@ǼˆqËå÷'EÒ¢»qf)*Ââ²\b2†qB˜òÞøŒå+(\¹ˆež3dšÌî™û3®ˆ*VH}#µ PTÀ² ÛÈ1³8dCŽFõÏÄÉf&vjŠ–G£Ó`¨2#ÌKÑÔ7r…t!cE¬¨œ,5žŸÅ©R"ýðYMj‘´·ì:©‚Òª8È1®ôW™5ùhY\ò7%Bðé\`ÄÙ/IšÐz`åšÌN‚é^h6þýTÌÀâm¥a ¥ÜÈX \lnCúSN“Wøð!ˆz Ïœ‰tüŠÅ{`2™²ÊW*‰ÍM—–ô*̡íìÕ±¶™Ôô”RYœ\.v¼m(6Æ¢šwÏ»Ú%Þ$ÒjaoVúû1Žfå<ïj²ÈcqR¹Ž3ÚC*»‰Á»þ Ý*w8ÕM<ªÊ Íöa©b*‹ë—Ãå5ž¥ßo¼±^`ý¯ƒL`‘ ^…Þ‰#L,7ðaM±þò°~M˵‚ÀO~ò“”©ï ØúÙ³#aqd•+84¬‰^¯Ë"¦Ï`«ííP:¾@TÇ`ÖÃä1 “E`?þi¬@š!ÛÊ$±¸Ñ|˜iž£‘{4~çwÞì¿kþÚoQ©©Óg’¡pÞóXOìýÉŸü‰f˜Ó2XÆ>à_siCoˆÁjxÁlv˜^xáÇ?þqÔ¢ñ3u¥˜–9×'rÑk$|I6›+®¸Â,9X¤1Tpõ*wW/ íSŸúÔE]dx}ƒ9ˆz'”Á\WÄõiÓëÒµ’X\/÷Á¯Î™‡zиžbÍ_Ô; Á «®ºêç?ÿ¹­'S£_m…OŸŠ2ÓwÜ‘²½è@rôÉ!æEk±*úuCãy‹ }Ö³(>ïº-UªªùCCE½Á\çää’K.1÷gj0«£z)Js­I̘­hgq½_ÉŠNÉ´q=ÇÀJÚMC^×»º†U¢ü¤ 3åeñÞÖ£pbÕZ…Âz£Š•ÙNÕ¥ú,4+‡•¾jÂ⥽ÿ®(cÉ,#ŸšÊI¯VÌÂâmüîw¿ O¯ü>úÑ®Xÿë§?ýé)d%rD±ÓüŸÿùŸ¢[éüú_ƒ˜pÒf:ð¢;5bµñW¿úÕÉ“'ƒ18}úôjÇó*ê¾YÜ –èõº`œþùfÉ¡"vi:ƒ±:”–ŸÆ'Bƒå)ðo|c}\\pAÊÀxài7ÊyÿRÆÿñOˆ Ò4SY¡Œ…Å ÚX?»ì¦Üà§7‰Å :£5CËAf*‹—,”pÅ bÛ`éòQ±¸$êÇ⠖ݔǦ¯šÄâ|Ô`Ø•fh¹(e,™åBë°J”ÌÅ,,®?-=t«è”,5ž}ÇipÓihÂâtƒ§ú-xzé gÚT™J°8Ý(´MI•¡d+C›E«D±¸aFš°xösª†¶õw·!'íVJWo”±4ÒÍ) àoaqC¦:ÈÙ×Å›!p\ÊØ-*Å(±.Îwc×á”ÚœÇ~X<Š`”Ã/]lsF$ dƒŽ7añì9¼(”DÆš°x9ñë2Vüƒ”–6 Áö-,nÊ õOÁn¥ñ”f§Ø<Ê {[ˆÅ s” ” i(‹½ÁCƒ•¡L+C›E«èÅŒÌù¡ØÎ4añ ÎÆŽ"êœtC·Í‘ ½œ ­¥Œ•).”ÅÔ d.caqCf¬è”¬4nèê ”2h ±¸!‰§‘!Ã9œ 3bñì«›|˱Jl?{u½u6ìð¢·MX<û,¶Ë5‰ÅœW JR5Øá¢”±b– e1³«Þ¸A ‹fºè”¬4»¡zk½ÍÚiÅâºÕÈ™Á9ÚÄB,nXþÐ P´L«¢_74®×ˆ "Ì~½ ‹Ccã'v °T1d[±¸n5øôF+Ò¨iÍÀ딉fq›ˆÿûßçñ®•£_|qÊÁ‰/|á ³x僈óâêú9+^ôJøÙ³gg^ˆÃ x¾óÎ;ëcL<,„äÌ"të(XÔWvÌâ¶é@¯×µ#E}°HØ¥Ù 2ðîÒDÛæôÉ'Ÿ\øå—_žb7êLÁ¤OʸõÖ[ Žc”:W+ÍâôÌ âA6´9`´Ò¸uV 7DöÁ¤PF½‹Ö‚V¬P,n8ÞSMëòZü†ÝÖë—í O“XÜfâ–fÁÆa­bñBùdƒy/J+fÙp²¡ʧ-,nI‹NIµuqÆ©V,nH}kdѰ¿¯‹ºªA h™ VE¿nh\Ïâ¶cŸMX<û,6LµbqCW5bÓe¬˜eÃVD EËXXÜ’¶bqÃzÌ Ü† S­X¼Gipƒ6±P,nHÕ4MãA¬–I¿6’OkzxPFÏâ¶cŸMXÜp¹Ó:t†· ‹ÛÒ±é2V̲a+¢¢e,,nI›°xöƒ†4u+/äQçq*¬Af*Äâz‚)ª`Q±š…ªC%£>4-Œl‹ªbH«6añ¨g°5°G¡$ 6aqƒøi†O™Þ(ÃÏ‹[n( Zƒ†´Ô¸ípËŠ\G1­ÛŠÅ y”ÁáôÃâ†$žÒ$•+eF) ÉÉ'.*IìÊ"MEQNTaÃŒ4aqÃu¡ëà–·š°¸A»•RgÈbm eøÝm–«?ŠNÉRãÙ}jàb”ØÝ–= 1èªÁ1 2S‰X|£“±’‰ó7¶ŒµÌ,g|Éß Ûž•ô¬,&Ý6¤U›°xö†cŸMXÀ¥¿rÌìšk®™ö9¨)"»ÈÇ›fœŠY àÊØ Õ§Ã1ø[)˜äªÔ¬þÒ}/k©·ì$88¼$é`ñ· ›*Ðëí¸úê«™}³ä ˜Ø¥)†œÁºÚ¿ùÍo2º¥¡]{íµ‰óÁœö¹ÜV’ `O;S”20›Óü­\ºŸÒŽ…Åù^¬)õÕWï»ï><¥æcå¯ëzäoŠB‰¬r¬g@Ï9˜¸Ò©þð‡¶±“G½þúëkj#ߊÝ€cVø‰O|âé§Ÿ¶!@­Y—L"š³?lÇÊ_×ÿDÐö(φ)F“-—šqÁ&®ôŸG™x¿È6vk‡•6Cc¬ô!/X±e€›á¾XÔï‹Ö55ób›÷Òµô,.=¸œC\‰}c;EËç ±8µô[½äZº%ÙàOœÅ2Ki¼Ø_ÁÖ›&¶KCCñOŸ>mx}ƒ9ö>{  ä x‡^e¿ç'Qc«'±8Sº0ÜC².sßøÆ7ÌBIÅa÷,ìB—ôëO±x åõ1ª»24Y¥ ;>tÉfRÍèMB¶2FŽÆr®Æ  †Ø —¦b,‹\/jÚ_)þ³û×Ö›µ‰œÞD¯W±AxÌ’CEì’ 0˜3K„w¨Þ‰Á’7ú¹È…€RfªQFÍTD. ÚIeñ ;¢£F®wó£šFú Ç+ØZzõØ#pé_DúA K.7½35 q–ÞÎ6bcñrý ¶¬´ÈÓv ! vx¶’L gzŽÎÖÚz­£5˜,½%ÌÄ ,1×ÕÚLeq:j¸’©Ðð*3þ;©àò·ìNÀ Õw)úQHø*ä` ‰®4{<,Î` =»g˜ j®¿ô­¦²Á¦¦Ÿ voN•Az©’Åi¥ÜÕ}úQq‘È 2§ö Ž7iºV1±~o„~*cKÖŒ¥bûUþ¨X¹ÅñRn‚1ª°~a(ªÙõÂ=,ý41˜,úÉŒ°4Õ*øÉ>¢<,Ž…A‹ìýS6H†EhÂdÍűyLÓ<¶èÁ&*u½ØQ±¸äp@ëç†Yh•NkîÁ44˜>”áÿ,Šch¤mðcèðJ•<,Îd[Ye>Úú|ÌLÞ‰Q¶F2`BY¸\1x4ýqL[÷šû¶nÏÖ:6ÙVV9…&àŠ5ô!ăi²ÔÜ` þ£M’1Í}ˆŒFƒ¦²±8mÉI§Êî•dEZ‘è ŽX¢ÊlŠÀéGŒ²ÈZ!'‚²´¦l„²”Sù£Ê¾ÙŠ!‹þ_ýD+Aõ?z b0+§0{0˜²e|ÌĦ8Qµä£m)#ªÃÁÂ9Y\Ü+ò$¬?œ¥CKk2RëDÆu¢ ¿¡²ô§ B…Vë )ÁV € ¶ÇÉâºÀ|v"p¸q:fò‡^!Æ(r51îÊ`!sQ-ÂgRö³©p“Z™Yœ1`Ä!³ô¬A8˜x UºŠûR¡K¨b·‚X'®Ú¥*"EGËâŒ]øÌpf=h(Æ$ƒÃU«háj]êÐ`À2#€:ö¹¨´Ì6žŸÅå3àŵb…‚Eñ(»RÅ\†“è.’bå‘õ ŽBŠ WqŽ||¥FJku±õ8ˆ;8T6‹É1³¸r©V¡£Û;t³Zà%öP,Zs„s@‰yÑ_ý…­${«j¹ÒØî%–/ÅâX*Áàf\)—6Ûîe "Nz€-Wy%RÚDÏû´A˜`Žaqz›qmEîrÕŒm§²r#gqA[RMx«·ÊJ›¸Mv“)¥ˆk1n˜MÌf‡9Ê»¹J"Ÿ¼m*'´Z±‚,.cS‰Hg"—#ÙxRåâûìˆ ï¦ÄåH!‘}¹ø>ûÀÇ:É”ÁåYÊÂHJ¤‹§ Yn¤¹ZvGfÌ8ù¶Ä=ÌD·ìÔÉK¹¦{¶Á‰96ƒ9 !q3³–˜™@rŸ­D>)BUœÅ¥sÌë¸ZQ¬†( yãLm1C"ñO:…Cÿõ¡"i1jÆNžÂ‚)ò‘^—0éL½l{ŒB÷xk mÔWLïs«œÅÇÈ㱉÷† zV£¢ÜJ†Ô¡w[ÜLÿ1˜H>ºEf[7˜ƒ0kÌ3(WŠéÝwg¢äG_±•Ö§·‹KG¡"¾'Ú…ŒÊ1!~LÐËOþæÖçÿcú3æÖÒ!³µ€*2(Ô’%†á /¤s¸<ša3dAþgÆÜš­Ûk1Ë‚Ä< €<€À믿Îÿg¢>?ÐØA0Åg!Ù™…ðÈê&r‚‡7èÊ%vCvà4‹éß®ï+8๢ #b\0úñÌ2Ä!Ê`Æ™ýÿøÿ8  ‘¤e”4Cª,>|ÿô™JIyÉOþƒN­+áì4°ÄŸŽ ÿq°MûAˆy@à±Ç»á†Nž<ùû¿ÿûc뜘GÕ+@W%Å×§V;ðöDw†×KÅ-îjN³tF æð6àQÌÀ1e|þóŸçMçóÏ?ÿ¶ÛnSÆ1dì¦ՆųH¶7²uäÀ˜ìç'ìîçè+`Å[!ïßݲІõ€³qhHQì2ÞÓψ³¸+/™ YÚH=Ûg¶Ö³øÖfÌû[¹x¼Ú ‡±ú¶Åݹ°sÏ…¤·£E`…­<(w×Ê—;>†|:ô#ÊÅOÚXIÒG”;‹·“Mÿr¿̆à³ËÃÇ”;‹÷+»;ëY7+ù~g9‹ïlB}8é¬„à” Îâébæ-0Srñï`œÅw0‰>„\(CpÊÅs‰œ·3@"›=€-·³øgÍû\¨üȃrgñèmžC #'º[™gñ­Ì”÷³æühƒrgñrÒxÔ-gçÝŒ>A·ã,ÞíÔxÇê ‚gPî,^G8è+Eé6»sÐÕÄ8‹w5Þ™šd Á-(w¯)¨ûÿV–-ê%´!gñ¶øû×[!=?ª ÜY¼•Üîí»•Éµ‚»P†œÅëcî_l‹@ÑüH‚rgñ¶2¼“¯7áÔÊ~C…©r¯²¢*„àÇ”;‹÷#Ò›ìIs*mâ@š*gñBÀz³½!P9ßwPî,Þ›xo©?0hsO"ל9‹çBÒÛé&!øŽƒrgñž¥½ß¾uHœ¸)sæ,ž‚ž×íæ!ø.ƒrgñþ%¿»vË—úQ“ç,—Þ„àû ÊÅ·¥{» šìÖÉNž³x"/°E: Á÷”;‹oQ)ÚôyCì¸ oc:‹Îâm$Û¿ZnCðÝåÎâ%åw/mo”7ävˆ¤8‹ïEc|çØD¾ƒ ÜYÜõ-€Àæ¸p<žmùÎâ®»A`C!øÖƒrgñÝhMþl‹WÆ¿GÄY<¿{‹ÕØh¾Ý ÜY¼ºŒoäƒ[a>%œ›ðHœÅ•³éźE`Ó!øFƒrgñnÕ¡YÇ6Ax6t:wMœÅmÓêµz@`7!øæ‚rgñä¿£>tÎséHõì£8‹§Ï¯·Ð…àÛ ÊÅ›È|í™Þ²ãÕ§³â,ž}¢½ÁÒì8ßJPî,^ZÈ·Ñ~Ÿ¬V»½gñ¢3îgG`÷!ø&‚rgñ삽!ŒŸ½ÿCï¹çžßû½ßû×ýWþç»ï¾»$÷ò׿þ5ã}ùå—Ïž=ûÅ/~ñ¥—^@ø÷ä¶ 8‹ójµxûí·Ñ‘7ß|óÙgŸ½îºëþò/ÿò­·Þâ_j}¿ýwÈ=0Þ¿ÿû¿¿òÊ+¿öµ¯Á b7öÌY¼!øµ?î=õÔSgΜ9qâÄ7Þø¹÷Ï=÷Ü—¾ô%þ]þ祗^Ê_o¾ùfþ­ÝÅÂßCÙžyæÆÎÁA†üõ¯ýá‡æßåøð/¤( ÷è·šw¯‰¶Kƒn=$ñÈ#œ:u ­¹ýöÛÑ‘ûï¿ÿêW¿Ê¿‹Öð'~üÇ+¯¼‚kZÞJ’v¯½öÚO<1ØFAàÛßþ6,ο☾æèœÅk¢Ýæ[ƒ"yè˜&ÖD %W¥Að•É,;L¸/¢„è:¦±2”¡ä]wÝ%ÐBö^Mt¯²Bƒä­·ÝÁ†@fÁŠxÉ( ^2¦¢Q´`›­ 0^Øñ¡‡Â}ÁYá¿5yJ"¼¢ „êй³x+!©ñ]¸uÒ‹à´OP>Ê ™¡Ï[ Í1+ €û¢TÂ%hJ{3Îâ5´Â¿±ŠìÃÄð±9QŒ­€Ã`2(MC~½Í Fp_~üãÛú&Ј&p²}Ej9‹§ ×o]´HÒ>¹¨Wè°‚DæÂ-ÂÎè|€Þ m–‹0œÅs;·cC€˜[‚È,Ô‹C@B :„Ï4¡¼­ÏykI䃓‹z¡XÈéì`ÅóÊ@ûÖÐDA4ûÑ+c‰|á…Ús¹ I-³½2:Ú3Wgñ¨z›ðz%o—ÝIÅ!¸¼„EÒ MYÚÎù ß•ôà$);UÌY< ®Þ £Ä‹¤ƒÊu‰„Å Iût®1²ø]t’¯d‰WÆýt/7kÞò  Ø\y»Ù!Û¥3eŠ{¡Ègè^NFJ?=ÏŽ^_ ²j[Z‡’ò­ìn{" ¢Š¥W¯¥“’`Ï»{ÅY–—ê¯0± ÉÂÞ,\éñ³xºz ëµ"{×6w5ô/#VycMÆ%°,ÂÆÞºÄsäÎâY&¢A#úðWáYúý×ý×Ê_ƒÌA}ÝÐçÄŠ"0Þ€}L9të,Þ@‘Žì“zÇ—í&+êÿóŸÿ­ªOEcQ¦,™̲³ø†Ôðƒ®ˆëoY CvJ:âQö¨(³,n^v5º¼-8‹çų\k±¢ÔâŒuuéÕq\ý&²¢Ìš³÷ï,^NqŠ´ÌvkVPôMµ7;‹“êG[ô=Œ-Ql€›‚À,‹›‹Ø¡ÅB—½¼³xvH 5{ ²(‡Í6®ßébƒ(8¢Ùwå[)~Ì,‹›½gq›H4«{,;H )¶ÔxQãkŠ"0ËâæÀ"ØÕfb·ðá¢ÝÛ`7ÝŸØ™ r^ ‡Í6ŽïËq}¬;±Ç²‹"0KØfïßY?jÁpv&Ø™–’ç6ïßYÜ, *®U C ‡­4Nr¬ÄŽSÃÒQQ–Xœ7vu*sˆZ?Ú«‘TÒ'ìšÄh eœÅ£àjUØpŽ«(‡-5u, ÌØ…HKQ‚Å `èê à £@ôÂYˆ]Ö„w…Xœ-x%ži1øªAÅHA`…Å éÁ`WÇR$ü-IËôëôÙ“(MEq¹³x½.ÝHì’pi[bñrc Û¿‹ú1KÑ9¼S§NÅʃÇⱈµ,op¶‚ÄÂa+²ïQ'ÅeªŠ"°Ââ†%®`WeDcþ†wsí bËŰ©äòB³ÜRÇöømÃ4å°•ÆSÜ•©‹:).íE`%§hÈb:‹oIk e ±¸Áý×ÌDÔ7mY¼DWø[H7×c¸Û‘‚\n Í,{™¼cE9l¥q©*o¥Œ‚(vSpC7O–gÔ£D¢YaƒGÙŠÅ KqXK¸)~ÌJ,ž7m0ËßBºW^y¥ù]ŠqE8{vSñ —;‹k„¶ysx·Òó” y…Å »I4ðFÝ”Õ67è”Çâ襌a‚ßyçÓ§O_·ü»ì²Ë®ºêª•+:{öìÒ/†ä¿eœ4cŒ+£¸ä’K BԺ袋f{Œf¦µf].þqXÿN9Û“X;8}tÕ0šYö2y00î?üÃ?\qÅ+Jg`VŒÆŸþéŸÎޱDø§ú§uÖíê:8Ðýƒ?øƒY гx^})ÛšAÄါ¯¾zå¡ßO|âO?ý´í©`öš]ýõ³c.Äâ†D=üîïþn!hvÉ@äbqà…>IÃ$ÒpJõ%Á3dY ñÖç0pÒ{ß}÷­h mÚŒµXlzôÑGk²¸!Q_uŸþô§g0ìŠuß’ÞÜ´VuCW53ÑÛ΀•ŒºÁYŸ¬%.×?TµŽ0ØN™~§Å5BÛ¼Loùä×ÐU ¼æÅæBk +»Û k Îâè¥L‰ S)«Â+¬cð(5(g§F>š‚À ‹—p8fãr Ÿº`¦lÌâ†Ö” ~× ”F ·½]+,Î9«—°5˜K3d8†‰^aqƒã,^Zƒr¶ŸwÔô,…ÃVXÜàQj ªÖ´‘¢Ùˆ/±qÝA\ž~4_.›×›<}IÍäz™BD1¨h)¶Ô8÷D!„%@0¤‹"°Ââ`ÅKÈL©6{»ód… QÔ 7)¶bqCìêD—“¢Ð ·R†=ðQü-M9‹'Â^§º!-T”Ö}°Qž!‹Y%·Ý{ã,®—„ö%¹ì:v4H %bqÛ D| w4E`%7ø1Á®ÎB&) z+e¸Ðp»³x"ìuª^'*ÊaK|t%€†,fQ–XÜ`ß@ÀY\)½‹=úùË_þòòË/_9öÀi 6±ÛpÒŒŠSh ÷½ëñ]7*ŠÃ¿øâ‹§·i£ÅõÐe/é,žÒ ¤ñ?øÁúÔõSXëö—MÓ‘ºd‚4«(œ4û£?ú£)†}?Îâ%T¦l›± \”Œì,˲L£¼„¢̲xð‹KC6W4`˜¥Š³x+4«•E9l¶ñX–-ÖK(ŠÀìtï=ƒ^ÊG Cv7lˆE6*c_Y7l&‚]ªtygñÒgl?Ê÷-Êa³.f‰'*c_Y7k“Çⱒо|”S$†¼,ÎI T¥Ä½ cÜûA`ÊâŒlï2'«½ðývÌv§·C¢B½¢6mbo3Ô"aQlè3w° §Bb1}Ð_†mcÑ÷jvøÎâ‰RѬº2…éY¦]Ñ·Ç{ìùçŸ7+$4@ ÏdAM¿ãµ2ÀÇüÙgŸ5#@ãÃp”“²4|gñ,‚á,!€5àÚ¨é ³ÓòˆâºFüÍßüYe¨8\1•!È2³Ê¨_¼(0÷0}†ÀY<‹ ôÒHbØ—})ë:¶ÎÔ×ÿõ~ê$Îâ¶÷Zé$†}é8hÁ¼ÔÜ“ú–j½«†{µôXÜ, í+–»°0vlø­¸“ÐXlÅÄò\I‹g­YYHüP°º>ÊYiÊcñ Î^ ÄŒQz†ˆÈYΫ¯¿ú•…ŒƒmŠ«ãÉŽ˜×à¤MgñÒÓT¶ýÄ%D ·pg&¸ %ËW‚dYMpâì²  Ì*gùÖR#H;®"™{uZÕü•õй‚gñBT¯Ù ç³×ÓܵoÞËI³:‹×S›ãþR.þ0£HˆAÓ_Ô5w€ØW†dž¹…ÄŠƒgñĹh_]¤ìz“®ÔÜ_º2À† B³—B¦ÃYÜšW±!%—kût[“5ô™­Exõ×¥$0ž6j9‹g±q#8¶ø•ì’¨ÜbÐñ‰‘Ê_NÈ¥ú°7‡\z®…=gq=ò^2VÁÈiWˆEUÇg[ÒbnAvºUŽÈ1 L‡¹ÛÎâ¹ ë«$ƒˆÜrn¡Šy1K·Y\à—‹S×»$€§?ì=þгx1ðFô rÄ£ãSOúº†’MXs½Ÿ•€€{,nÅ~«°Ù r-½aAÄ›®¦ùQpŽ“]/€äâ²'?œÅ£æÚ gA@<òñµYš6‚Åh˜Á^9Þ…ˆ ?ñhøì(œÅ Il³fQ®+”[–›JbF¼È‚@!«$·èоæêŒØA9‹Ç"æås!‡•Ûí…`ã(d\{Ê5êq;X ,[¡ Føý8 é‡ÊœÅKL}mŠÄÀ4yÃe¤œ6añÒ‘n:¦ ·Ãæ —ñyi“,z!œÅÓ§Þ[0#€û ÍÀµWÊMÌk‹Ìc\¯ˆkN&/¯7#‘v£Pdň</$í›EQÈôE/¤PØ ^´=ÕÕ H‘¥kük,HÊb9uñð`ØÊWgñV¢âßda@b¶IîpÅnÊŠ•›²Á›IÜõ†­`ì )ö'8Rgñ DÛ.€ Âå§N‚Ï "}‰Ê3©À^­Îc¤£1FÁÿGÜ£ ü€^ÆeiPÎâéÓí-dAÝÇý… ó¨ P7H ? {eéR#²ŠæÌ¬_T"C!Éyêùè Žy8Îâfè¶TQ¢Iˆ2#_„l¡i¬ï¢ŸãŸÜþÔŠ¢À…V‰êc¨"C”“¡­ +XN—³x"€^=/$tŽÅ»!öÎ ‹ü;þ.Ì-/¡UpyóŽt©5‚™Á•VÓ˜ŒÀ°XÜ~ØÄLFÔèœÅ£àÚCaäW‘wÌþê¯þŠgIOž<ù¡}èꫯ¾í¶Û-­)‚õ1%?2XtÍÿþš˜L³ ÊY܆›×*%vÕøö·¿}Ï=÷`.~çýäÏÿüÏEk ³ífì‚€€¼‰—?6¸/‚M÷ÅY<8}»* ÙòääØ>èê¾ûîCý?þW1-ºô»+4sÆY<7¢Þ^pjqy%EGBëßþíßn¸á†wÞyBèIâ ")ºô›g;mÅY|§;–$“ñ!iÉö yòƒGú`tE%5´›\ú†&ØY|C“µû®J2Y–áàiüû!EwðµË\“`t÷s±>@gñÝ À4ìžæ»–^âòм‰X8‹7Ý?:Fà ìžn‰%* ˜fìȺ{hÞD–œÅ›À^ê£+a÷ô“ø´€‡æ¥æi¡]gñÊ€ûç•°{ Qð5dÍ+Ë•³xeÀ‹|NvO?¬ÛCó"Ó6iÔY¼ÎþA vOZ ħ%=4¯&fÎâÕ Îü¡¨°Ûˆ{hžyÂÍ9‹+@ò"ID…݆@|ZÅCó¤ STvW€ÔS[؈Oëzh^H"œÅ ëÍÂî”@ÜCóš"ç,^mã·Ãî,¸‡æÆÉ‹©æ,ƒ–— vg Ä=4¯ ¦Îâ@6~"WØ7÷ÐÜ8ŠjÎâ ¼H,awÞ@ÜCó¢Rë,^ÞèÆ³‡Ý…qÍ£§VQÁY\’™A {Ø](÷м„ø:‹—@5ºÍrawé@ÜCóèÉ^®à,žÌchªPØ]:÷Ð<¯p:‹çÅ3¢µ awµ@ÜCóˆ‰wÏÖ±6R!쮈{hžEŠÅ³ÀÑHͰ»~ î¡y„(LŠz,ž‚Þ¾ëV »ëâš'Š®³x"€ªêMÂ‡æ*±p·Át4µš„Ý qÍm¢í,nÃMU«mØÝC JPþ!Å£àÚká†aw¸‡æ±‚í,‹X |'awW¸‡æJ!sWµ¿b„Ý]âš+åÜY\ T XoawŸ¸‡æëbä,žG·ÓJWawŸ¸‡æAqvB´X Û°»ó@ÜCó%‘r·kãvjvvwˆ{h¾$ãÎâÑÚßؽ•@ÜCóœÅ£µq;:»·ˆ{h>EÀY\e6vo.÷Ð|@ÀY\¥Û)´¡°{s¸‡æÎâk&a‹a÷vqÍÅ·CÐk=Ý\ؽÝ@ÜCsp?ƒM‡Ý;Ä94wß.‹o:ìÞA ~Ì¡¹³ø³¿°{Oø†æÎâ›cñ„Ý{ Ä34?jßYؽË@ü¨BsgñM°øÎÂî]âGš#‹ï5ìÞw ~ ¡¹³xÏ,¾Ë°{ßø‘„æÇÂâ»»$ßwhî,Þ‹ï>ì>’@|ß¡ùÎYüxÂîc Äwš;‹wÂâGv[ ¾×Ð|W,þî»ïþìýßw¾óÏ|æ3gΜ¹á†¾þõ¯¿ùæ›X‡ÒÝ å <ÿüógÏž•ÿ–Òßí§ý_þò—û·{ùå—êSŸB "ª~z¸Ògñ&Óì"'?þøÝwß}ÑEÝrË-/¾øâÛo¿Ý¤?õ?: ðñög&hÔïFÃ/þä'?¹ÿþû±Ÿýìg_}õUAsÚ°KúOo›Åá§×^{í‘G9uêÔ‰'.½ôÒϽÿ{ôÑGañ»îº‹ÿþôÄOPx+£œB†Ã c; òßò'þ?@Qxg¤.k%SÐÆü9硇Bâ»%ugq¥Ø'ç—^z Õ@*øÝxã¢)?ü0,~Ï=÷`:äO7ß|ó3Ïti{ \‚Ÿ†é}8¬f<•`îp‚8êÛíYÛ]gEgÅd´G4¡‰O7¤‰8èÁ¨ïÌ9‹›Y¼¹ù¦çÍ݈¶Á¸õ¹îXg¶‡„vC£Ð$ œµžõÅQº!æ+ÇÁ³TvæœÅÍ,^y¦–ú)'B›lªè!øv €@“ŒHÁ8s Psß~_,ÞP¦jÙijFúÉ 6±³TǶV y‚ÎYÜÆâõ³&+ýl‡pâ‹M$6ô²×jâ…÷ü€ge3Þ‹#ˆ]Y®¯Ž@ÛLþÔ¢ÉQ¢ì–n¶A‹Ë‹8‰=„{ ¾#«° × eÈ\ëÑCæf˜‚ÊZÌØA è¹ðX骯ŭöô-!SÓ³ìˆÅõ”ÉNHyogéÇÛ‚ëÖÿ:ÞkZS8ô„Aׇpñŧ 0ÞkZÓ$é½x¤e}€\pA ãíœ÷«³ý8–Å1ß y¨#ÖΔ'ÉcÌ{—÷ÀâzÂÞéJŒ¥º¼5$Q+;zžðZE 8ƒKXÎjŽ…¤e}€çŸ~ CZÓµêˆÅõ|¹nU‘¤ë®»ÎlÔ0dcÓûæ/õ´tòàÓÁ%—\bîÈÄP]ï[˜¿hb:9ý4ªhîÏAãÕŒ²žÅþ‹“CÞþz.oÎâQSCÀº²^˨yÍÖ,3Pè} ó‡T/ (}¯õùBìyXÝÜŸ³¬gVó¥b_®sG^ªmÑí…ŃFy<Ó5YœïÖ F£Ü…š,u‚Ñ(w!(0Yêe ‹ð·¼œh¥úð¨6m*ãòæ,¥›5Y¼Z0%™5Y<Š\SxÝ´\“Åù´>4MA  ïÊ,Å.æÉÀ¡Öok¬ÌâQ¨-ñ•Y¼ë,>åo Äs-Û£V¹Ä —·eñXŬÉâhA…`Ánè÷Ôdñ:Ä*feŠÍÌ–³ ÝÕY™Å7Šb “!ϱè+Vfq:E±ú %ÑÆ¨˜²2‹ÓÏ ‡q—X|‰¿SðôuW¸¼-‹ÇÞ T™ÅcEÚ 5±§Ë*³8y~”Zïdˆ=]V™Å”ÞЋǞõ¬Ïâ±ö"VcÛ¯Ïâ±Ú‹@lûõY¼Â*×,‹Ë?êI·PI¸|z¯g[õ­+³¸,R½I-¶ýÊ,^Áû.ê³x,»ÅZNÊwÁâ±ÞJ}•cg"ÖÕgñÒE¬6Ögñ ÅR,ø,Êî³&?4nv_XCgù ­‰R´ú,+G Ç ’õY<Ö;BÀ ’õY<6Ó…€nÏâ‚ü¾°nˈRŒÝ“O>9…2ÖÕÐO†ÁÑÃõ^~ùå)€ð´ÿ±Ž¿ƒ6~ÿûßÿð‡?¼2ÆÄ³v³ĺz¤äúºø—G­Å¬t‰ aŠçK; YÜ@gÏžMQŠõº'Ož|ã7à5¨¶^f yë­·®â /4C„>¢•ý7¸z é± wdG€¸Cyˆ@?öqÉö,›L¦÷õcq>Z.1b°Gõcq0ôS)”m¬‹3ƒÝT" aq)3åò¨ý+ýÁG[ðuþnÎ⟲~,J±i6½À|Êú±8Ã1Ì”ÃV•ú±8c1ôS‰€kÏâw¾ ‹i# ÷qaÃ7aqy¯Ó<Ì•ŠO.8yOšIç y£(¸4'Íf¹<}ñ•ˆaÈØkø»9‹æ· ‹—;¥iÈ6añrñÏú„Ϊ^/ÿôÂâ_µ ‹—[Þ0ø1MX¼\zÐ`éš°¸že£È{(Ûþ—§¿(·êù»-‹Ûò´MXÜà¡*…ÇàÇ4añ¨óÜʱK1üÎØ ðMXg ;vÍ  ‹ÇNÕOŒÁ£lÂâL“¥—¾¤Á *†ÁÆ ^i<¥Ù ±,>ÄåéO3dÃ;T†‰ ‚ )`SÆ&,^èÔ¸MTš°xPU53>-c3GMX¼\³‹X\òx±³Ø„Åmj£šÁ£lÂ⌥‡ò1AÓÒÕ•Æ iL H™r2¦ïCTÉV,nKŒ5añ  F>¶-î4aqÛda±¥›°¸­«A¢Žå’ë]±™­&,^ˆÃle+7¤ 4²hÈÇc!/z¥¢M4*ӊѳ?;Þ&,^(³] Ö„Åm‰“ ÄÚ€mÂâ6#D ·y”>ø !OžÌþøÓW\±ô×à¿_}õÕßüæ7g,‡Ù 7=¤ŸKc¹öÚk¯¼òÊàHWáY m750î÷¾÷½õYN‘&úÞ{ïE (oÙ„A¯íÙKEc¥·¶íB°8‡Í–Ä•át¢Yk·Ÿþô§Ó>ŠÃl~Ìþá^sÍ5KcĤpBÕŒÀUW]õÃþpŠ@!©6l¡oèõ w€Àe—]fFÀ.ÍÊ­ÁÄéµµñº¸ÍM{ôÑGÙ3‚/6û“·—þüwœµçŸ~ACÈœ ›ˆÓCú¹4ä{éR°áÙžŠD "þꫯÞwß}+c¤Í88à,”ÞV,n[l¾þúëQ%©@¡o³Ìp{öe9›‰ "ocñ»ï¾èVÿcû˜ô‘^Ucq[6½^áþ”ˆvéèXܶ״IFݶ„ÔF[¢¾UF½D6 ‰ú 3x*CVV3Ý+el.]âGSª·bq[Ö$£^hUØ–On’Q·u5(–¶ò‘àH— €íìI3CĬÔFC”ÿƒüO|eŒ°x $ù§/­ÎâJ¡˜A9GDæoI*8_túôi³ÌpBé­·Þ:è?9¼Ø[B• ¼ÿ[n¹æ[#Û³yÑËŒúøòË/O;oˆ˜•¢|ôz…;>þñ_pÁ)`—¦7䛕ôÂ↞¯|å+x¸2…ÓêÍÉÅ¥¿ÿ¶~öÙg@4<©ŸƒªÓCú¹4Þ¢ÆGºTlA¸¦6ò­ØmêØ Îö¬ŒOA¦ÄvR/RÒY\˜Á÷å°8ª±$(ók–œ€éI³¢ÉÃN;î¸󏢸@dF}œž³2tR/ï½^áþ”ˆÀÔ© Ôícqæ,6Ì­ŸQ7$ ô²HÉØí]õ3êEµb‹àŒdϨÛvG‰A…êO°p¬æÔ0ø¾õ3ꆄA±W×Ϩ—N_Å:Öõ3êå6÷ ¢Ò‹Ç.‹ÖgñrK;㔈^ë³ximŒõ곸!}§ŸPÅc±2ø¾õY¼\2YàŠõê³x,ËÆŠA¬÷_ŸÅ+xº]°xlR™Å ^¬,Æ.+ÔgñÒÚÈQE}/½¸åõX­ ÊÀAƒ•Y<Ö1>åc—*³x…ôU,È•Y¼èRl_±8½‰ÚÉY™ÅËíNëm”[]™ÅËíN#î-xÞŒºaÎ`”cÝYÃ'òV©g¬tÏbÆVª2‹GÙ4åŠ»Õ KeõÊ,(+GqP,j§ze¯ÀbñØœ¶ î•ZÜ6L¬£ÜÌY“Åc-…ŒVF‰@M§KL dš²bÔ•Y<ÊR(Ç;-¶9ÝÏaFf©b”‡]“Åë¸}Àåa×dñBÈO%!j aM2é)ªñ‹ÇffR>¹T—í£è¤¦efb}:GJÌ;-eï÷ШQÓù•2zèaÑ=ê <ô3ÊP&" ÿ‚ ³–Û£>F ÊP¦ PÍô§tr\·N°µÒÛ¨`/·GÆÇ{Ôë¸}‚Œþ[õ=êì´7[Nº1~ ¥B*b ý·Ö¹ƒv2" ·ç‰ú(Aø¹×Ÿ˜àĶ«ë=z»"jœÂzøá‡Í²HÅ!îÔw)qìR]ŒÒÕòâÙ—¿üå{¤ïRe[׸âçiib‘«¹ Y&‹Fê$ ×{« ŽY™ú¯½ÿÓÈÆRA¥«ú.e™}ÔKÇVÈ-?þx h¥ŒHߥʬsÇFwáD'©¼æ‰ý‘ªæL ßÕ£Y¤?ØHÍTD D æŒT8”oTàºFTkæÂú`Ôü }E_îøL_1¥¤>Mùоnýiž:§æŒÈª¶{‰õ“ª/Ùv¿Ì¸Ÿ•êáÓ5§}^¢œôS,µÑ/ØZJ¨í)’ºõ}¦Ä>·’ƒnC™ÐÆ '*±zéS©³Ýcì PÙuXª‰G­­$Nq°zMןÎÈñ™ü—á^âà` :ÇÊæ{ TµÝdë³ÓÐ2‚ÎriŸŒUê‹b?î‹ÆÊ‰Ó•^‘)e«rs¨rt†b•Í÷¸‡ìTÇš7weZ?@ÑÐd'¢¾k+Û6ϱ¸án}ƒ”kª4G–Ãч†Ž-Ÿ¦ú#4T£Ê` è€þKTãšÂͧ>€¦wjj:o.ÓO"J9„Ø{”ÍÚŠ5_¨o¾€jÞ†Á@ÑÜ™kB^rªöÜÿÕ_^ÑUÇ&žuó=…¢¡8 L͘æª%¡UlAX +䋦 — iº[¦þ èJ.޶ד†[‹$öhü¨íø€I¬<§”oü ;ÓϱxÛÔDzq.©lĉ-ˆ‡š˜àÀ†:S9"p`ŸIëĺLT¶ °)K›XÃÄÎÇVo˜ŠŒíê¸|ÛÔ‡þÈÜOЦ M_ãÙI‡‰Çðf*‡@âÄT¸UB3)(/bP‰Âë#0„ßçX<ö L š‰eКž¾,ò5_ŽƒFgê#P9¼.$2)Õº$ñþS¢èªonQ\ÆØáwXœ-¢ÕŒ8káøš•ãu#­Ù%ô¥'fjv‰x£Õäp—Ñ9çסÁª"Ž„)³UE±rà«é¡¬Wà@®øjô@…ü¶R}oZ@(ýr†j[™Òwìz…0W0â8 D½C†–®R-ÀgªøjГô@…¨ 7®~ÚXßTÿ‹÷pƒÛtz0âxÖx…(GöûDACò„t²Pn`åJ£®â‰±$ˆ¹¤‡…6Ü1plqýõ‹aŒ½­gi¬äP¦ájôJ?%‹CrµPP.áWç2j.Æ…%±Ä¹¯Bz AÜtiîj¡Š²BŠjB“ö õ?Øìx=ëg¨ìT/D–Á­`¥—?¯DŠífÈbýÄáSNÒÕ¼ @ °,„¯£$ÀE¡êBæ^3õ]í-Õtx\¦Ã¤ºtOęɛÊÛMøÕ*m£Ÿ oP^n­lY"\„jK]ÊŽM‹cGÁóúsùÔ\ì›E`¼Þú‹S®áyÇà<%2Ñ·¢z¶;8äƒc‰LG€¹F½ó wìˆ å‰~è6Nh"—&¾ Måu #ª|ѯ¡‡Á*ý\U4í*”C* {—¾k•TP¶;80ÜD˜,Ý›Ì E>ƒ?‡Ê':ë€ÙIäs;ÿ ‹W¾7;V)/É5 z”)GeßÕ›ÛnÃØÇC` '*Í\T:ªczæg¯ˆNÂâ,$ƒ¾HÔE“1ë €EŽª›} Ò`Ϩr?׿,u˜=ˤ“Ô‰2åÔ•˜žß&²V³ ˆ7sêÔ)¹].* À}ÁÝÇjžµR äÌ>2Ìc,˜ÀìôùL™ú7,Îà·râ…~’\Óám+|m2<ÃÒ)ºÇÿ¤JÿY ¥¤2†Ã }@A`‘åAªlbA£“ CBjÆÈÐøÇaK‹ 0< šŒ‹²ãšÎØÊôÏÊq5¹yTÙ·qL†ù/VΤašEBVþ§<2X w¡=±CH,ØK 'cdh2äÁ£•ÿI1þ:Ö¯þW4àBçÃË2äaŠ1 w PôƒÀ4kþ[,5O7ip_/3<ð…¯Í¬`йˆ£ào[õ3éC>há.…,à ‹ï‰äˆCfÅnŒÀXW³ƒŸØàqA`[ëbÊ…¥~8¸"3ò?åÏØl_¢0T®.,%CÆ#0x9=ä« !3öïÇܸ£C]öäYœ±1†BÀy³Ž€# ìLÑš<ƒá²ä³§BYPzÞævlsæãÝ%<ÞÛîîÍ;"oÍè ¥÷fXœ~÷y ´+@½3Ž€ &W.ÛºU«þCpQÝóÂŽÀ¦3ú³{zæYœ¢T¨p÷ͦaõÎ;v³>{“g SàUm!°þÖå<‹3ÂæDn eï­# A`÷ëÇ ß$ÔàïeÍ! Ù»•Ê‹,ÎP;|#dsàvò[²ùy÷€pR‹L^Ô‘ÜÝcâtlH¾~mÑ‹óUTQ.±õÀk9Ž€d¶Ð£í^;‰Õäˆí˜—w6„'Y8<¼® ÀâC°Ù߆ï]uúAà8sZ _zígê½'Ž€ý»·a—NŽoâ|3d^ÑÈŽ€ÜÛç»·Ù;mP^ä+÷&a…!ø'ú|³Vÿ`š–ʼnìt§õ_VÂü‹»D@^¯‘·[:¹ðµÎ%Þ$l5ÿ®#P–ÞH{Ç>¶ÁâÒ{®î#SßÉcEõÆò`ÚvŸœ±z¥ÖðlId7è¤#0<mØ…ÍâÒ]ñ¯Ip×ÞÓGè-8#QáàâÝr)·û¸³“%vÊö®Tç³ïÝslŒŸš4û¸F—“c—g%òÇaŽ<h›K¯µEäÙ´á¥,›øï>ùZ ÿƒw¥äa1_¤«†¿¨9òlš¼÷šå©É$àÀœÉ†:~¼bËó8Dêò?ýçìñ³i†W«›Û‘N:€-“‡Å†·øäE)ÿ9ûCà@ÈÉKår^ó°øÔ(pÎDBsÿ9ûC Ü_7$LñŸ#°?rqöTëÿ î•ëŠòãIEND®B`‚jbosscache-core-2.2.2.GA/src/main/docbook/images/ClusteredCacheLoader.doc0000644000175000017500000005400010750374443026075 0ustar twernertwernerÐÏࡱá>þÿ ')þÿÿÿ&ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿì¥Á‘a ð¿]jbjbA]A] 0+?+?Zÿÿÿÿÿÿˆ˜˜˜˜ÀÀÀöþþþþ  öJ¶""""")))ÁÃÃÃÃÃÃ,RR^ïÀ)")))ïÙ˜˜""åÙÙÙ)X˜Ô"À"ÁÙÔ蘘˜˜)ÁÙÙulTÀÁ i›ÇÃþXÁ0J•,°Ù°ÁÙÀööäÚ$ööÚ CACHE LAZY GET(KEY) CLUSTERED CACHELOADER CACHE REPLICATION CLUSTERED CACHELOADER  ./56BDZ]íéÛéËéÛéÛéËéÛéhï<»hW Ï56CJOJQJhï<»hW Ï5CJOJQJhW Ï#jhW ÏUaJmHnHsHtH ./56BCDZ[\]ýýýõýõýýýõýýõýýý$a$gdW Ï\þþ.:pW ϰ|. °ÈA!°"°# $ %°°Ä°Ä Ä¥<@ñÿ< NormalCJaJmH sH tH jj b}È Heading 1#$ & Fdh¤ð¤<@&gdÙ@L5CJKH \^J_HaJ DA@òÿ¡D Default Paragraph FontRióÿ³R  Table Normalö4Ö l4Öaö (kôÿÁ(No List-4ABY]ÿÿÿÿÿÿÿÿ0ÿÿÿÿ'ÿÿÿÿ&ÿÿÿÿ)ÿÿÿÿÿÿÿÿÿÿÿÿ.-4ABY\ÿÿ]ÿÿÿÿ ÿÿ+£] ./56BCDZ[^˜0€€È h˜0€€°Ê€˜0€€Ø@­˜0€€€@€˜0€€È h€˜0€€€@€˜0€€Ø@­˜0€€°Ê€˜0€€Ø@­˜0€€°Ê€˜0€€Ø@­˜0€€È€˜0€€È€˜0€€È€˜0€€È^š€€â]]\ð8ð.2@ñÿÿÿ€€€÷ðœð&1 ñð*ð( ð ððò ð@ ð¹<5+ ð1ðððT¢ ð0 # ð €ÿ ðå|¡ð ððT¢ ð) # ð €ÿ ð™( UÈð ððZ ð S ðÿÿ™¿ËjJÿˆð¹<™Èð ðT2 ð C ðÿ™¿ÿˆð ðu XððT2 ð C ðÿ™¿ÿˆð‰Àñ( ððT2 ð C ðÿ™¿ÿˆð Àu ( ððT2 ð C ðÿ™¿ÿˆð‘ Àù ( ððT2 ð C ðÿ™¿ÿˆð D u ¬ ððT2 ð C ðÿ™¿ÿˆð‘ D ù ¬ ððT2 ð C ðÿ™¿ÿˆðD }¬ ððZB ð  S ðD¿ÿˆðÁXÁÀð ðZB ð B S ðD¿ÿˆð=XÁÀð ðZB ð  S ðD¿ÿˆðÁXE Àð ðZB ðB S ðD¿ÿˆðÁ( E D ð ðZB ð S ðD¿ÿˆðE ( ÉD ð ðZB ð S ðD¿ÿˆðE ( E D ð ð` ð C ð€‚ÿ ˆð¹¬ ù Èð ððZ ð S ðÿÿ™¿ËjJÿˆðU<5+ÈððT2 ð C ðÿ™¿ÿˆð© ð"XððT2 ð C ðÿ™¿ÿˆð%À( ððT2 ð C ðÿ™¿ÿˆð© À"( ððT2 ð C ðÿ™¿ÿˆð-$À•%( ððT2 ð C ðÿ™¿ÿˆð© D "¬ ððT2 ð C ðÿ™¿ÿˆð-$D •%¬ ððT2 ð C ðÿ™¿ÿˆð±'D )¬ ððZB ð  S ðD¿ÿˆð]!X]!ÀððZB ð!B S ðD¿ÿˆðÙX]!ÀððZB ð" S ðD¿ÿˆð]!Xá$ÀððZB ð#B S ðD¿ÿˆð]!( á$D ððZB ð$ S ðD¿ÿˆðá$( e(D ððZB ð% S ðD¿ÿˆðá$( á$D ðð` ð& C ð€‚ÿ ˆðU¬ •%Èð ððl ð' c ð$€™Ì¿ËjJÿ ˆð%Èe(˜ð ððlB ð( ƒ ð0D¿Ë8cÎÐÑÿð}( ½( ððf ð. S ð€™Ì¿ËjJÿ ð‰Èɘð ðð`B ð/‚ c ð$D¿ËÔ”Ñÿðɬ U0ððB ðS ð¿Ëÿ ?ð]1±úÿÿœþÿÿ-$|t "DNZZ[^ "DNZZ[^Ÿ4v5°2Bÿÿÿÿÿÿÿÿ"„Є˜þÆÐ^„Ð`„˜þ56CJOJQJo(†*‡hˆH.0€ „ „˜þÆ ^„ `„˜þ‡hˆH.‚ „p„LÿÆp^„p`„Lÿ‡hˆH.€ „@ „˜þÆ@ ^„@ `„˜þ‡hˆH.€ „„˜þÆ^„`„˜þ‡hˆH.‚ „à„LÿÆà^„à`„Lÿ‡hˆH.€ „°„˜þư^„°`„˜þ‡hˆH.€ „€„˜þÆ€^„€`„˜þ‡hˆH.‚ „P„LÿÆP^„P`„Lÿ‡hˆH.Ÿ4v5ÿÿÿÿÿÿÿÿt¬        ÿ@€à|-®ú¹~ÿU]P @ÿÿUnknownÿÿÿÿÿÿÿÿÿÿÿÿGTimes New Roman5€Symbol3 Arial 0ˆðÐhÞûÁ†äûÁ†!ð¥À´´€4µððÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿjV‰ÿÿ Manik Surtani Manik Surtani þÿ à…ŸòùOh«‘+'³Ù0À ˜¤°ÈÔä ü ( 4 @LT\dl'Manik SurtaniNormalManik Surtani4Microsoft Word 11.3.8@¤“Ö@Œ?$dÈ@0ÓúdÈGL þÿÿÿPICT D€Zÿ ÿþHH€Z €€ÿÿšÿ€´€ZHHÖ0Ìó€Z€Z§ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿÿÿàóÿàûÿ/ÿÿöóþðóóÿõóðóûÿ3ÿÿöó~`ðóóÿõóðóûÿ/ÿÿöóþðóóÿõóðóûÿ-ÿÿôóðóóÿôóðóûÿ-ÿÿöóüòóóÿöóüòóûÿJÿÿùóþóóóóôóóÿùóþóóóóþõóûÿGÿÿüóþþóþýóþ÷óóÿüóþýóýóþ÷óûÿYÿÿüóýó~`ýó~`÷óóÿüó~`ýóýó~`÷óûÿCÿÿüóýóþýóþùóîýóþýóýóþ÷óûÿ'ÿÿîóúóêðó÷óûÿ-ÿÿïóýøóóÿïóýøóûÿEÿÿñóóóóùóóÿðóóóóúóûÿIÿÿòóþóþóûóóÿòóþóþóûóûÿEÿÿöóýþóþýóþþóóÿõóþþóþýóþþóûÿ[ÿÿöó~`ýó~`ýó~`þóóÿõóýó~`ýó~`þóûÿGÿÿöóþýóþýóþþóõÿþõóýóþýóþþóûÿ6ÿÿZÐV°ZÐRJOçó÷ÿüZÐV°ZÐRJOçóûÿ(ÿÿâóúÿüÿâóûÿ'ÿÿâóüÿýüÿâóûÿÿÿàÿÿýùÿàûÿSùÿýO BgF†BhBgF‡> B*> B*ûO üÿýðÿüO BgF†BhBgF‡> B*> B*üO õÿ?ùÿîO ÿÿý ZÖZÖcÿVµR”kZR”R”ckZøÿîO õÿYùÿþO BgBhBgBI> F†BIBhO B*> B*ýO ýêÿþO BgBhBgBIO > F†BIBhB*> B*ýO õÿ!ùÿîO çÿîO õÿùÿìçÿìõÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ§ÿþÿ ÕÍÕœ.“—+,ù®0Ô `hpx€ ˆ˜  ¨ µ'  Titleþÿÿÿ þÿÿÿþÿÿÿ !"#$%þÿÿÿýÿÿÿ(þÿÿÿþÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿRoot Entryÿÿÿÿÿÿÿÿ ÀF€z0dÈ*€1TableÿÿÿÿÿÿÿÿÀWordDocumentÿÿÿÿÿÿÿÿSummaryInformation(ÿÿÿÿDocumentSummaryInformation8ÿÿÿÿÿÿÿÿÿÿÿÿCompObjÿÿÿÿÿÿÿÿÿÿÿÿXÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿ ÀFMicrosoft Word DocumentþÿÿÿNB6WWord.Document.8jbosscache-core-2.2.2.GA/src/main/docbook/images/PublicAPI.png0000644000175000017500000005230610660354341023661 0ustar twernertwerner‰PNG  IHDR±’‹Ë"TIDATxÚíÝϪtÉuç}]†/¡¯Ã# ¹$ÂFE ûì³_ýêWþ28q݉ÏË_|ññÇÿë¿þ«? N|S'V5À‰oíÄû·«j€WœøJ¨šàÄ'¾ÌÐ| €ß݉À‰9181''æÄàÄœœ˜€sbpbN N̉À‰918ñžNü›° >‡vâÇÿþS‘UÉœXD81'pbáÄpbáÄpbY%_ú‰ÿÊJw.7Ì?ýå}þé'ßøè+ïÎ'_ûê—?ûîÇ¿øÿ…8±l*Äþß5œxdHríüê?ùñþOó£Ÿýð{ÿçþ·w?ùâï~ôWòûßþí¯ÿ×?ø¿ü_ÿ8±l*ÄÏ3»K?|þ77k8wûΟo°ô“÷_Óz9HÞ ñ÷¿ó­ûû?~óów?ùüÓOÞi1'¾Æ§ô78±œÌ‰Ÿÿõùfm?nmðÎ_þ÷Kó~y9KÉÄÇßüèYˆßkñ·ûëï‹(|~ŸûÓ˼}€Ëyç‰SÚÚ˜å¿ó FsâsåóO?ùٿ׸ÁÏúƒÏ¾û±ÏoN,œàIJsíDVˆÛ÷¿†/=º?ßøè+¿®!^Ê÷£¯}õË>¿9±pb€˦N¼TÿÒÖv1qüλ…œø ÉáÛøüæÄ‰N,Û5bkÿ¤ëÄKnÜyá>µª0OŒ›;ñ`ÃÇöÅ«rpb€‹\á3FÔcã‰ÁÎ3·’`N N̉…ËAûNüÎ7~«Ýwâÿü}~sâìÛ¿ÝÃ1x%ª½&øee×Ò…µÂ¥° ÎlÞSàÄœXDô'Æe¸»¸Û'²Æ·½Â¸ÛÇ=;NN pb¹rÞïc÷7?ùƒ÷ûØýü§?°'^É»‹ƒËNo°SxN pb¹KÅçŸ~òµ¯~ùÝùäÝÿ~ÿ;ßz¿U‡ÏoN|.'î÷ɉN,"â󛯸Æn®7¶íäÄ'æÄ"‰q”‰¥•j^Ö¸¼Æ›NfÛMrb€ËäO‹¹÷s¨&‘ÁèJÁ‰qm'ÖÜÆ{ œ˜Ë:x:'¦Å>¿Á‰§L?{Oœ˜_Á‰ŸOî‘nKóÄË…í6>c–®~¶oóˆm+íÁç78±xOsbBü¥—vÛ½Mû·^T4ª,nù§îóȨÄç78±xO;ÙqâEÅŒ;qð‡)-ÎGžãËÙnNìóœX¼§À‰91'ÞljãZyĸ+)öù N,ÞSàÄœX†œ8ROœªRü 'ŸßœX¼§N,ë®±{YžÛ.Ò-/§k¡qû¥…wÁ»"Ä>¿q('Ö#rVqÙÞSàÄœX/¶Ê/žZ+91'ÆqœXÈ]ëù–ÞSàÄœ˜çšqž½='!æÄ8¬ß¹Gdá:ÛÒ‘iØÆTº÷81'NŒ}œXÈìúŠÁçØ8€ÞSàÄœXD81ŽëÄ÷éY^=̉á¬Â‰E„ƒ_¤G$'8±ˆ'æÄwï¹¶·{ yOsbáÄØÇ‰õˆl/‰‹*òXm)÷ž'æÄ"‰q'¾sÈïpbpâ-œ6ÀÇÀ5œXÈmvñèî[ä=NlžXDÌcO'ï)€‹ˆøüæÄâ=pbŸßœX¼§N,"âó›‹÷À‰×^^ðÞ'õ[ÙG9à¨D|~ƒ‹÷8ñœx%™ëÞmùqã¿øÞkS¿²ö¨D|~ƒ‹÷8ñ9œøÍDé{³üð'þâóí_Þ k/'h»ƒyù>ÊÒs zíÜQ‰øüÆa?½ ¿!À‰;?yi´ßmß>>‹ÜLä?âó¸ŒJ„8ñœøÃªÙ—Sž Y|ù+;‰“wë–ÖØµk'º-ÏU]ûŒ=§Ê$æŽJ„8ñáœøÂQà ‰œøîN¬š'pbN,"œÀ‰9±ˆpb'>¸×Ú÷¿bp„³ž`ö~®úr'p⾯ä4ñ=áÖS½ˆÞ­áqâì±Ú}´Â‰œøXNü²×Øs3²Æí_Þ Þ -5˜%î>µÈ°Ÿ÷ü îrÒ}"Ý.rÝãÜx¬Tºm^áÄN|&'^jHœÚ@®}ûøôm{0‘ÿÜ=¤°ÇçnÜg÷‡‘ã\ëÄ\›Ô/¿‰œøˆNüfî³»IG»ð4¸gGW•²ƒ  8—ܽ“BíDðKÂ’Pfó\'žør'pâãÎÇ+"â5.jû šeDÛÊ{!^ï8oür'pâ“9qÄ—n·ÉnÅBd0Á›eh܉g©pê8Owâ‰/‡pb'>¥w/—/­ýj×Nt;5ªºÖ­ý]ÆÒ£&Ñ×X`aö‹G¤ÛÄ—C81€׉¯½±óÍÕÄN àÄwwâ›K!!N àÄœXD81€sbáÄN||'®u±Ý±* ±ü.ØÖ·ñ‹µýA&¾ ÇÙÿùt‰§wâ•L"¾5Úz‚5·õAêqkÿ´½g[;ï{ô81'pâMøå4çsO®Æí_Þ Þ,5˜Èu‘g~jƒ»ô5~±»AFwx‘C‘Ý¡pé‚ýæÊá¶ùÃN àÄÅ­ì²È6i‹l5·´)Fj¸¥Zúï‘ êâãŒo÷lÃåýä" ›»Çjƒ]¦'þa'pâ×SzñiÎîVå™ÔœëËÉÔ²ëod0ÝÛÄoÙž~NMŽfwŒŠYN<ñC81€'‰ƒÁÇõ(>W¿ÙöBŸ$ŽÏÎÆ…x¤w_!ÞàC81€'öv®Yò’‹d5bfñ›Åk'jóÁƒõ$m'.T‰DpŠggèSÕí³þ0„8q‰»©—–©µ¯æw×c5ª2ºêYÆ_`´±Áv¯}ó'®r[z] Nkìí\³äÈNÅ‘Çm¨OwæøÌhdðñ’€îÿƒ¦n9X;qç>åKÂøŸ'ö1¸ˆw//-S ®”ZºÜܨÊèJId_|]ÜÛ #ǰk~‘áªnòÜkdüÏ ?ñwÖ½³ä¹SRN àÄœxŸ®º· <Ús'ÄœÀ‰9±ˆpb'æÄ"‰œøŒN\»þ¾ãUòçd'ª¬v8õs¿öŽÙÙûYû}lj§tâ•$¾UØz0·ÚõÔ.x{Ûу×ð¿ãÕÔ›bí‡àÄ€ë8ñËTK[ì¾¼ýR‹®`‡¬Ô`– x°[¼'W|W¶g[³÷ß^¡ô%ŸûÒS‹ü}>oî<°Ý¿Ø`k¹¹/z­Uß”÷'\ĉ—ʦ6‹o¾ÜNâå`"ÿß³#¸!Åø&µ#Ù£¼gÇÅž{a›˜Úf(/¿B´dê6þÄGþ&¾ï81àdNüfJ¬»IGwÏ…òlbp7‡î¾‘‡8ÎöΩÖÁ­ì"»ú]ì¹·/tÿZ µ#Ï¥ð†šëÄßwœpÊyâøâÈçñøçwd ´Ôtõ1…8xĦqäh\õ¹·•÷8B¼Þjã÷'\lj#аtƒ¸d®¤w7×ÍÎŒF/ ^%O=hê–ƒµ—|î5+ÍNŒ«pùÍýv4þ¾ãÄ€«9q÷*êÒ’ àJ©¥«®‹ÈÝÏæHIh|]ÜÛ #ǰ+@‘á.Ž_æ¹Gþ>Û#/\-Yc]p„å«"Û¼ï81à”Nlcg{ Ÿý¹ÛÕùhG€8ñ½\apŒ³Û#v¸»œàÄ'81À‰N pb€wsbûØàîNl®œ˜€7wblƒw àÄÇuâÇÿþSY;œpbN̉91àÄœ˜pbN̉8ñÑøKÿ/]Ï‹ßòͯtï0~ŸñgŸ'¸¯]³f–ïëù×ßü$eºso9ø91àÄqâ—Šüü?Ü%¥^úÅ®‚¿œÖ}ùÐKƒ_z:‘;_äËa/N|'~é ‹í:qÛ¿ ³ËKãlmjêºñ,–šybÀ‰¯æÄí™Ý¥éÛA'nÜmDUÛ_»ó FsbÀ‰/èÄZÛøŒrʉ»qUÍ΂sbNœ(“hûe£·»ÆîŒN¼ôCN 8ñ™œ¸Qt¬ÈÎw¥±ž/²˜/òpñÅ‚©o œpâ“9±L 'œ˜â/éO 81'N 81'N 81'N 81'N 81'N 8ñõœøeS…Z†¹w8wTÓ'œø"N¼†¿žÎ‰kcæÄ€_ЉŸ·»[ÚXni³·¥ßZºÿ¥]j ¼´%^û6Á}§91ÀxI »=7$rióäöýÇçk#ÿÔÞ–98*N À‰3îÄÁ¦´88syŽ/g»91'ÞljãZyĸ—sbÀ‰oíÄ‘zâT•ÂàO81'.:qpÝËòÜv‘ny9]{ Û/-¼ Þ•5vœxB³Cµ`Û 'œø"NœÒÁö𭄘N|)'{;pbáÄœX81'N À‰…pbáÄ—qblƒw àÄub€œàÄ'81ਟ4pð6Yœ°þì‹Fï"rìí81€‹'æÄN,"œ˜8±ˆpbN àIJJ¾ôùðÿ~KäŸþò>ÿô“o|ô•w§”¯}õËŸ}÷ã_üÿ‰œX8±Ü"¿úÅO~üŸÿÓÇßüèg?üÞÿùŸÿíÝO¾ø»ýÕŸüþ·ûëÿõþã/ÿ×çÄN,çpâ÷‚ûá¼ù§7?|¾¥ƒyϼâïç[ÿö÷üæçï~òù§Ÿ¼ÓbN àÄrJ'nXò›ÿ~þ¹[ÉÄÇßüèYˆßkñ·ûëï‹(81€ËqøÚvõ÷ù‡¦Šo›Ï?ýäg?ü^ã?ÿé>ûîÇœÀ‰åèBüf¶¸ ÄŽämó¾òëâ¥|ñw?úÚW¿Ì‰œXNàĩ҈¥ÿ–›¶W ߆8±œÏ‰S 옱ybóÄN,"ê‰Õ8±ˆÜ²ïÄï|ã·Ú}'þñÏÿ8±ˆèỎœXDn°ÝßüäÞïc÷óŸþÀ>vN,"·+¢øüÓO¾öÕ/¿;¥¼ûßïç[ï·êàÄN,"‰œXD„8±Ìj!«¿Áû‰üüå¬pwÌ/g»91'æÄ"â4ʉ91'ÞljãZyĸ+)v2çÄrŽÓt£d­ñ+<ç×mÞ¿ñÑW~Ýæý³ï~<·Í;œÒoèÄ‘zâT•ÂàO8±pb¹¬– ;É›¼ßôg?üÞûí@ÿêO~îv pJ¿ü»—'Ûv‘ny9]{ Û/-¼ Þ•ON̉åÐBÜ.üúð‡ÏÿÑ]¿,—Ï;!þþw¾õoÿÇo~þî'ŸúÉ;-æÄœXʧÇÔò¸ áÄœX¶sâ—ó釮(ËMJ&>þæGÏBü^‹¿ýÛ__DÁ‰91-ζ]éJN̉å|óÄ‘N“onfªøžùüÓO~öÃï5nðóŸþà³ï~̉9±ˆpbN,§¬È ±£zÏ|㣯üº†x)_üݾöÕ/sbN,"œ˜Ë œø¥ GœXÉ„3cü6œ˜‹'æÄrD-~ž^úI׉™±ybóÄœXD81'õÄê‰9±ˆpbN,"÷Ë?ýåýÎ7~«Ýwâÿü91'N̉EäÊÑŸø8N ÛÀ‰EDÞæý>vó“?x¿ÝÏúûØ™'óÄœXDnWDñù§Ÿ|í«_~wby÷¿ßÿηÞoÕÁ‰9±ˆpbN,"‰9±ˆ8™sbN̉EÄÉœK9³:þ.mÃqüg§ç±Ó(œÒgŸyþÇÙàáR«ý¢—U8±(!Þÿ>× -vÅ”SzܲRUS·ñ1”åoiÀsmòͽ½wÜà£pbáÄœXú.ø¼QÜÒÞr/÷“{3O¼´·s÷‡ýêÞüSäÛƒáÄN£˜uJoLˆ>ÿÓË9Ôÿï‡ÿÚ¾å{]{þõì8ò—ðK'~ùëía/Ýà¥Ô. ûåO–ÆYx¥D81'¾ ¿´ÛîmÚ¿õ² ¢QeÑpÓÈ?½¼ÃÆ`h±Ó(ÆOéYm+]C+·lˆlÁà'¸qo]»})ÙYÞø,xûYŸ-N̉ïåÄ‘Ûgõ7x?‘Ÿ¿œîŽùål7'vEö”>.Ï3 'nܲlÆo*€#jؾÙË¹ÕÆ°ã»ªG~¸Y´8™sb¹‘ǵ8òˆq'VRì4Š)§ô¹“Ä…ÚÜö£· !Æ'‰ãÄ¢…Úã–…8;lnX81'æÄ‹ò©'NU) þ„ 'Þ·ž8nÆ‘™à”ü½´É‰ÅÄãDîp%'®QˆpbN|»5v/ËsÛEºååtí14n¿´ð.xW„Øiã§ô`ÇƲ­Èôp·azÓ‰ì€#w²$ßñrêò»ÈQM=qN̉¯æÄƒ¿xj­äÄN£Øþ”~:¯:Ú€•4ˆ“9'–}t°=Lˆ…sâˆÆk¢ñà6e+NæœXD„Ÿ{žXD„‹ˆpbN,"‰ED81'¾xeZ¨•rv©ÜÅÐÒ 'vNŒú)}°íÃÒí³>72†²F:`Ô¶•žUs¼«U’'áÄWvâTWàF“¯çyÊö-l‡ÑÝíùQjQœ𣹓È#и1ÇÜxš/ïü1ܲ­ûdE81'N̉'oeoÐÛ5àñ­§§ ¸a–©»ªí²QÛÚ£kÕ„X8qeX°%Ìu3'—Îç©Çˆ'eSƒüðn##‰x–^wçÚƒ¿U«]IsNlžXDÌßkžxî$q·˜8x¡¿}}¿ýOãŽT)Œ qaœµ²ìø1áÄœXD8ñ­ë‰ãfœX þÓcy“äYÅÄ…;q¤V!rĦ8ñàýˆpbN,"œø^}'‚ ëÕ"ÓÃí‹øk4( ¸PÛ­Uˆ—šdØ5ÖDÒbáÄœXD81&ŸÒOçUgAÀ‰9ñ)ór6"ûëœûFî¿Ð£ôòÇD8ñ}œxð}ºÓòqž‚“ƒpâ£;±ﳤêøÎWÐÓËáÄwž'áÄée·mðÞ(ä Þ[ª¦méFî¿1¼rYÛå‰pbN,"r_'Öà=>±½´¹Ð¾'Õ >{W©á NÀ_æ˜'æÄw+‡‹ŸR&^.Û~m\öR­ù¹£kð>·ÁûJ?|”ºÁOïùc"œøzN¬®{ ±pÕñDNœ­‘[õN|ôyb ÞkgÏ-åïÃký{ ñå‰pâ‹9±r¸ÈÌËË2ªT‰Wãi6î|éBVã×—^µHÛ`\ãÀŠ“ùë‰5xjâ`=@¶-ü”º”Uø2ÇD8ñeœX9\êÒ_ä"Õ\©Í>"4Ï6?³\LlÅÉü²}'4xü­BÝÈËó]£m{°±||Â>uqðÚÇD8ñÙX9ÜÜr¸õª¹óÄña¼\g<èÄ㯯8™_§ÛzeûY`a$‡ýÆï<ë4 åpÇ)‡¼Ú,ƈÔT¤¾ÌÏÝG:Õ€HœÌoçļ_æÐ9&‰¯ZO¬n®*t8[¿‘*f\9·s¿pâË΋ˆpâÃöPW®m(ÔdÛà´"âÄÝ¥o…v©u{âdΉED8ñ9z±)‡»sT 'æÄ“¯éÏ:›¤Ö7Ô¾”;ñ 'æÄÊáäP5râd~V'Öà}Jk‚¥.’ñ®gœXœFašCD8ñN¬Á{ªëPª®«Ö5=ØY,òìD81'áĨïñeÂíõÂ…ÕЧPØÌBsuáÄœX6¨¸(×Â9- '>¨kðÞ¸YûDVÞ=xŠ­ÝT±pâ›;±r¸ìà»7X lj…wžXƒ÷‚Ún ÄÙù`sÉ9±r¸H9ÜËéƒ`!Ü£Úà,2È—‡N„o]O¬Á{y?äÔnC#N\+¢áÄwsbåp…SqÐtk§ô¥…y´X8ñÎ}'4xOÍ(ŒTYÄ'ÎGØ9¥ '¾•+‡ËN‚‹è¸½ ÅÔ†pâƒöb[i‘Áq–Yìø Npâ4 åp;–Ã-ýS[yÇ…x© ¹ÝbÈÙC8ñÑXƒ÷ò¼£ÄiÊáv/‡+wtâȘCNl;q…r¸µÊá–fFʵµZ¸öý«$N̉EÄiÊáN0àUçq©°pâS:qä+r­h!Þññ€g“l÷kœéœ˜N̉•ñÎÙL8ñQz±Íµ“)]Ög;Ö;Õ:±ßáàlvŠ÷)‰9±ˆ'^щ m¿º•"½Ì– ª•míú¶B{ùàͲ¥c·:8©ë ‘"ÅH¤pbN,"‰ך'^cµ¶V¦êï³½ 8»"xä:þÎøó À<1'æÄþƲ”f4²Wä|‡N|h'n7E¶I;qdF¹ÐŒ½ëgñYíø#Nwâ+œ‰NüHnO%œ˜gç;ÖÖÊ5ZO”<åŒñ²ÙðàSp*N|ˆyâ†Þ„¸­}#s–µ…TQAðþG{]þàÔŽ !vÅ\'NunÔÅM©¹šÛ¢8;àGsSåøF}AÞ$£áÄrâø5ô¸© vYŸ¢}³qº_éàŒ;ñC[{§QŒÒ'neW»’ÓµÀÔ?Mp÷LX˜,ˆÏÇOq"œøˆóÄÝjŠÆR§øj¼Â×ôçbö·í`{ùxúYËé®zp²ß"K<-°ãÄ–ÃHg¶|.[Ld»´¯0àî†Á_¬µ^!âd~G'îOVXUîÞpÞÓÓÜ‘Ï]–Z>rù—{›à‹}Ðrâƒ;ñéV¬®7àìéŽ$ '¾¦4oÖi­-IÛO~ó´8ݰW}нܛ +'†K"‰äăÖâ뎗:«¿¼Y|1òH{ùàÍË–ÛûÆuE†—š@]zÐÚ—‘eæç}¹SWH"e—{4˜ãÄœXD8ñEæ‰'îí¹åÒæ ‘_ úÓËe‘GŒìKR؃­;¤TùZjGèõÁ\ìå¾Á˜''>lÅÅÚµd"œxO'n7E¶I;qdF¹ÐŒ½k3ñYíø#Ö*ªGŒù‘Ù0©öÂÍuâ+½Üøq³ «9ñ¾N<¾ÍäxuÜÚߺ§¸1ß±v9™'>è>ve]kO°tY/ü$u >xÿÙ è[ qö NÙ™ùÚ/wí˜bN¼¯§º7êâ¦T(ÍmQœðxQY¡ÿÚÄ6N¼§ǯ8Ç?×guY‘¤YX;-Æ/šÏuâòÝŽ8ñ•^îq'N•Ê8bü”>q+»ÚuHuYüŸæx¤´i—6N¼ÿÈvi_aÀ³ª·¯aáÄd}ÅÆŽ¿¾Æƒ:#«äĘ'ž;Iœ½ÆòH6¢‰üÓÜo#ÄkØD81'¾ !í>8qµ—[8ñ‘ë‰ãf,)-›[L<>àZ%Û^5l"œ˜‹'ÆÐ)=ø5¸]û™*ζèl:‘ðHQÙÚ ì8±pbN,"N£8Ö)ýtf¶Íös—ÁÑ_áÄœ8}Ö(””û$á$µv7øsœé_'œøVN|ºõÓ_3§ KœÌoáćz—NéÐ>ëþW:2ÁMƒïyp6û›ôáĉ9±ˆ'Í6š®¿¹A£[¤­¶f9Þn=µy¤5}ðfƒ;S\ûàÄgvƒŽÛ4>ãÄœXD„Ïwâ`Wö—›^¾¼YdíÂHiW{mop£à¶.=»GxS†‘ëøw88ãÏ7Þ~ŸsbNœú ÜnäDù²:þµ6[«VûEoq2?“›¢·Ï†ÁE»…>êÙÓq¡‘{×Ïj]ß ±GœøJg¢?î·‘2'æÄµ¿íîI©pÖŠÜçÆ%dµÇâÄ"{;Gn—’ÁéÒ†XGL¨V¢0Øõ}®ßáàÌýÄ"Äœ˜+¬ÊS‘‹ŠêŽð1Üì,¾H»Œ0R£Õ>¤Î‰¯àÄ ±ëN…fgâã⥥S´oÖ#Nwâ+œq'Ž×iøˆâÄ÷œ'ßÜx¤"k°²¢plÊÕ¹ÂY¥P(‰OãÄñ‰Æè¹Sñ5|/¯Ý?Ÿì‚åÙG\c9ÝUNö,òGe'¾‰·Ëá‚ÕM)'~îÄåç2݉ƒ œs„ŸflgF‡Èi[NstkR—wvâl¥Ö”%s¿NˆpbN,œÏÁqÅn—þº]YjN™--˜îÄ…Å#N\+¢áÄœXDœF±ÿsbN|U-ž²ù‘ˆÜÚ‰uŽX{k¢5Ä«fŸ—7l/÷Æï2N̉ED8ñd'~îvþ²ìy‡ç¥"ª‘<ºÅ[Kc>ÄRíW·õzê# Ú³ìa÷8Oƒ9N̉E„ŸÞ‰ƒklŸ{Ù.Ý,»2£=ÔìêãàöÅÙµÕÁGŒ,‚.ìÁÖRª%ЬNø/b^àå¾ñ%üœ˜Ÿ¥.¾Qêí¼ÍÈÈÛÖA '¾¸·¼ÏhÁÞ–ÁõÈñ½*RüÅà¶Ùíµù‘UÞ5[šÕ ܉¯ôrOtâÇ 7¬æÄ»8ññËá²W™ù]Q×;2÷¬ƒN|‹yâTšš§¦K'ˆò _í‚~ê|ù`>’{,¯'ÄÙ'8egæk¿Üµcr!æÄÇqâ–ÃE®=b­Ü–¦~Wª»g”pbNÜy·w§B³ßìãÔâ' )’4ëË}1ËÕ#N\¾Û'¾ÒË=îÄü†œ˜¿®Pû´ñ6xã…«ƒN̉;3ÝZ‹Ôݶg–Χ¾”?Š,Ýa䮲7+ðò»Áån…jŠòrº«¾ÜÙÕàÃY&–8ñ–N|®r¸à·¾éN<¥Bì†uPÂ‰ï²ÆNv¯®;xY¡øO"N¼Ë<ññËávâ)b÷¬ƒN̉冴û\àxI€—ÛiÊá²N,Æq₞޳J81'áÄtâËáR3¦Ývåu©á©ƒ'sN,"‰O¶ÆÎ…—õî–°Š“9'¾é%ï·ö)•m'úÈ1±!œ˜_@‹§l3$âd~'žX6Z^1°öÖhÁ_\{—£5<{Ö×€¹Ï§ˆÓ(8±ˆpâÓ8q{CÚx»´ç¹Ãö-fòÁq¦*Æ~‡u+ó–n_=rSÇydqwwŸj³ÈN£Ì•‹'>‡gt¤Nw½íÈÀ‚®™pûQ² ™ƒ{gŸBðSkGâÛA§°‹Ó(8ñAÊáâÛ¥>‚·™/ˆÿbpgA—øäîN<./7Gè:qpR65È`³ú€³§ÔÂÃu¿$¤6ï>bwyíÔ™}}ÅiÊáö*‡«]uŒŸEWÕÍñ6ÉœX8ñŠ“Äݳga{³ì?øÑÛ{©<¡›:…Æ@©GlÿJí3ïÈ …ßʉ•ÃÅ·Ó‹”EºÂ-}6 v…‹Þ5+¸jäØŠ“ùÅë‰ã'¬àÄj🺵ãgÏ€SÔ‚O¼ÐÙ>h«åG\É'.U§Q(‡[»®Pê¶ñŽzã·äÄÝGQ'·î;¼ÂÕøß´ bå«l…w+2Ñ~Öo(-xSÇ9R ù¾Ñ½g§Q§Q(‡;l9\Y¯Ç·¸‹ËèªNù¡ÕÒ¢?ñÕŠ¼ŸË%"œøóÄÊájÅÄÇœ$ž%ÄÙù`‰‹ëvO·ÐXWL‹pâƒ×+‡›ëÄÁ*‚'.Ô~Œ8q­ˆBœÌ9±ˆ'>_ß åp#å ÙvmÝãVZ{¬‘vÌØÉœ‹ˆpâ³öbSwºGß²››'öÒ®[w1¥¤ì2¥É)€É¾‚&E8ñMœX9ÜÑ´xÕžÍ"œ8qÕà}úý̲´;×oÜßÞ'¾ç<±ˆ'N/ƒ¸sƒ÷n÷±¥.ë©Z´Á˜:ΑQµ_n%\pÀSÊï¦ô·o¯€1‹Ì‰9±ˆ8™_Љ5x/Ll·{e›ÀÇÿ©°,zé8tð‘oT´4ÈT£ŸûÛwÛ6bN|g'^¯Ž6ØÝ|ã7àô‡Ûì™®îó%N¼Å Tƒ÷Bƒ÷îà ×ý’?€ñGl?ÊJk·ku5[ö·ÿ‹N|„^l³D*x¢˜ès»€áv|¦å“Þa pâO ¼§Š‰Ç<\|Úxâ#Fæ>S[R¯'ÄÙ§<>±Q8’‰ìÄ/+‹–*š‚çœBŸãÚÙ¯=ìF1[»Ò©=›?ÉŒ”­ýL;0áÄÇ­'Öà=5cZxâ)¹lkÙ¬G,8q{idæxü5]ɉ'.¾tžNürm@ùëeãw³WT"ÍÆkÒÊg¿Â™íPÏô°N|è¾¼Ç+²8¯ý¬ßÜU{ Wð¦Žsä’èÒ'kª–#ø‡1e]öN k(â9ña¸PBVsâöÙr®w—RÄ‹ÙâÞ©oûñ©Ð]žéa&œøè½Ø®]$ä¹êñÙåðêoω9ñÄr£‘IâÆm²̵yÙ‚-M *ÁŽùLkgÈ-_áÄ'vb Þiñ¡¯vœ˜–¥–ǯ̌,i-WIe+â}uâ |õL N¼ñK œø"óÄ""œø,óÄæµê¦T§ó¢êàê®`±V¶€êå‘Yúáàòñ¹Ï´¶²bË 'æÄ"â4ŠNéÛlоÍìà‡tØb¿õ:I;³9™sâцc–Cè?–gªÿ<'ÆÆN\ûÝUOªwи)Ÿn+µ‘v¶t2¿E/¶Yo¼ ¼¯×A}ûÔ~â8õŸçÄpéOD8ñÖÍ,3¼Ç[w´=/ÜmwɈþó|¦úÏsbpbáÄ[;ñÜï]oi#0r·úÏ?ôŸ×žsâû¥V¢TLœÌ¯éÄ›5xïÊkÖxÊ£Hé?¯ÿ<'æÄ×pâÁ]"gò½[µ—âôº>¥bâd~ýyâ ¼ÏâòhS“ÄÛdOĵyYýçË'hýç91'n×YÅ·íŒT½œž\js›]¶•Ý´[69õ)S*&œ¸UM;ØàýQêþ=>ÚÚÜêÈb[ýçõŸN¼£g4{Ò^oXmddO>¥bf„oÔà=^ö0k]ê ¯ÿüCÿyýç9ñ™x\:ƒuDÁ­‘ ìBi_êÃed²C©˜8™_yÝJõ[ÛW)®Áí[S¥ÿüîOÙIŸ_ižxî$q¡b5õ55òOs|¢¢8¥b‰ÏíÄ#סÖö›£Ù§þó»?e¥rœøªõÄq3N¬ÿ)[{V(&pm…R1q2¿©‹ˆpâ³÷Î4Ö«E¦‡Û×ß×h:18à¬å+áÄ""œøÜ½ØTdíòÔ”Š‰“9'Îu·}ù[{¹¦Œ¡Ð+mû£½K‰È;ɯ±åø1›ù—§â8ñœx¯sïöÞæ *N|z'žU@\»ŸKË-áRu][ªÌÑÎÂÿNŽ< sØfþ»N|¢ybNmǶԕ½Û ¬ÝÑ=Ré•í¿4þHk³ö£ÄÛÝwÂË£W[]¾ÛØ5o›NòÙþzñ1ç™F–*"\oÖsbáħwâF¯ïÂ:ÖÆžF §,w‰ö\,oðVØ.ò\RK¡Sí–ƒë]Ogî×ëu’¾ åŽEy¦‘Bì_Åkk81'¾^¥V¡lã˜%d"œx±·¦¿ñæí©…ÉíÚƒn³ñlS÷àpysÇ:Û\—_µà¹ßÇ'î~½)w’ÏþeÿTŽöLøQÚ÷‹Ÿ¨ÛÑ*€/V©UÞÙôøëðDn=O\hŠ^¸žžíÔSîªÓ=MÜ t_!.¿j©IâÆmR#ܲ“|£-QyšÿPÏ4þµd_!æÄÇqâ—×+Úågí°T9Ðõ*µ[X%‰ç;q[³Y˜$~ t‰~öÇ'ÒR‡"âÄãF’}Õjs«#+—wì$ß-ŽÔÞŒ¯­‡?J»Ào£•Ç?!žzð"œø°N/Ì}ÄöyΖŸeïVm˜'>Š‹ˆpâ‹9q·¤¡]Ö©¶*×N¨ áÄœXDœFqÓSºI\N̉+§Î‰'͹3ëÕt· œ2€ì¯o60áĸü4‡\N|,'>Ô))ÒÏáP»ÆÑ‹wö˜2ÚÚâÈ &œ.ý‰'Þ¿™åswž¥,5n‹´Îé.j6J‹w1K5tëNct{à÷›k©{<ß¼Lñ²ûÀ„ƒ‹'ÞÙ‰»K†;5öoK­DÎMfÇ7ËÜmûéjæ"ó »´Ç0xÜÖ˜pbŒ;ñx…U·áqmÍ\¶Žb¼òm³ž¡Îiâd~&'Ž7x¬.쓜=Ï>æmÝã#¸^{Õö'î~Q 6êŸîÄ+ L81f]úÛ¬à*µ­éàéq=[åÄ"·›'Žl>×5¡H™iwUÖfBœ$ q°:¢<9QžbŸ+Ä» L81V*‡[ªYŠÌStË¥ºÝк5r«V¾¥ªÅº‡¨Qæ×øøk—#Špâƒ:ñc ©{¶t8>µ0׉Ëw/.¯ck×rd÷ÖžåÄÛ L81¦—Ã¥¶µkœ4²URƒ•s+ßâWO6þÃxù™ó¡pâ8qê­¾ô5º}·Ay ®Þ›2ÍúÀx.(ËhãFºÓáã L81Ö(‡k5¾HoìÄS*ß²(…‡ëçø™'¾ã"åÍV?ÔîvƒUÛßó.GRœF±Ë4Gc±G÷öGâ)•o q·èŽ 'æÄ{®~(ÜíÑ„xJåÙJ͕օ܉³—¿jN<²óz•o©Â‰qN:y 'æÄ"â4Š­ç‰ƒýËSå ÙvéS ê¶Y`×>òœX81'§QÜ딾o½éáħ©'®}c.œææ^âß²“üÈú<N̉/©Åµ¾C"‰zšXɉËýá7>ÅoðBøHN̉ED8q½Á{»Áxªñ{w#v]Zwv6Ò¹]ÝõÈ·|߬“|ªdmâ4¶'æÄ"âd~G'Ž4\|ó/oi:󬌩…Ìp? ˆY¶(µ ºp·ÝÇŠ4µ¨Y81'>ZQ\üì7¥îkä!vìž\•ø8ÞÖÓªõœÌÏíÄÁïí3Zp{çà’䮥0¥ž³¶ŒÞ¦“||?mN,œøžN|¨·yáD—ÚÓ´\Pw@'îžÕO'¬>qœÌÛÇ.²häœjÇ3Ø2un"ÄÁIâ)g¨v!N̉U·4ÂîÉì¼i·Pm|v¦vÜjxÁçÒ}½]öTë '®¿3ƒõ óNü½,Þxĺ£§ä{НÔI¾}$ãJ„ßʉw,‡kŸÄVÚ oü!"³-ñã62·òÒ¹Sƒ‘=ºUë 'Ž:q¼p¢±.x·ã ìR=ÛÇ[¾oÙI>r$}eN|C'>l9\ðbÝt'|ˆÆµÝ‹ÜÊûc; sâ}œx\:ƒ=ÃkMÝSƒ 6«/ 8åÄY·K­•î:q{:®w¿ä¬Ô@~¯ '>£+‡ÄÔ6¨©®>N‘'v2?ß<ñÜIâÂ[4Õ’&òOs¼ªg‡Z›$nܦö‰r„òL8ñéœX9\|;½îÆ×ñ‰Ø¥;l¢¥W䘵d©µfê5N¼Ñ 4~ N¬ÿéÑÛ¤~üì9>à`‘î'.ßm¼h¸¼ŽíP ä7˜pâ9±r¸øÉ³PdÕ­¬ˆ|¶áøCœ«È­°»G|£áÄ+^h ^áj|™‹oZØõ§|•mpÀí%wW°Õª)"õ!åÜ1Èo<0áÄgqbåp©r¸ÇÊ»LGÆŸ“>i‘[m?íÂqN¼ó‚ŒÓý™®QÏ´Ëvг ìõǰãÆÚ‰/3O¬®VL¼žgL§Ôy§È-uy¹Ý÷¶ò€Ë ÑÖ6¹£ ñaÈ›]àÄ·­'V7׉ƒWó³’]¸^z–"·n¹yªˆB8±Æ="‰¡nB9\¡2­¼À®ûë'Ž/n;f‘[û;Uj3æÄœXD81”ÃwÀgt5;} '>è tî÷¿“ãÏífmØ7>]ž¨g¾pâ3:ñ}Êán®Å‡-rN|Ä^l«ž)–.ëXJuœï''z!œ—91\úN¼g׊¶;º·‹Ÿ–*½Fú–î¶Q‰ÕîmþèmªÜî$o¨Þ~úÝA+SÝæ 3»©ú¶³_ pe®œXD8ñ‰xIÚ¶ÜnVßòÂÝFÖÉfî‘ßé'õôÛ½Ù³‹©#}à˵ ‘ž”©åÒ‰9ñÚ×dj«¯Nw½(¸œîÏw³gZhútÌ—ÀÉüâNÜnŠÞ^'û8ÞVÉÙÝ.â ¢³½ÍÛ«‰ãN\kÛ>Ò ²û—}q Nü˜Ý¿S8ñ {±ÍòàÆoÕŠ¸í ð™6Ìg_ܶñ3-ïŸju 'Þzž8U‡pX!.;bêBÿˆGŠOjïÞ‰BÜ(#™%ÄÝçKˆF±Ò¥¿îû®QlÖØ„"[ÁÕ-ÇJ5+´[Îþ$Ø×léY·g7²»ÎªµÛò™v`‰ÿ´üÇü—uâq öu¯9ñ#Ó|1R›qâìëY*ÜÝi%'~ä{æ 'FʉåUÙ‹KNÜ~ˆÔåÄÙvåå#¿Ûø'_ª""å[윛UxS¸qío`ƒ?lN|4'Ö°e«¿ï½ô§Á¥®Á'UÛ-{ùn|„gzØ '^·ø,8/ØX¯4ËÆ9w¦Ù·Å+^’:&…:„Á©”ÁB‚Èæò#‡ë8ûl¶À®q¬¦´Äßþ›oìÄÁ²ZÁLü-9u4~q®?©ì…—l X|*t—gzØ '>Ö‚Œö_éžk5[›ß)Éö.§Q”—M\ÕiØLJ\²“ÄíE´…kVÙyÙ‚5v\+_$<Ô3­}-Ùò%N¼§Ÿq߇•<÷ž·iˆ;x‰@î Äsÿ°9ñ8[0S»<¬(ÈVÐ¥*ÍÖx ÙŠ”öÂßróþmžimqÅ–/pbÍ,Eäú§Q¬êÄÝ®äÝ’€T]m]ÚÈšìø“*ÔÔ H^Êb·ÄkßgZ[º°åÀ„sbáÄX唾KQÖ¹ÊnX+x´¿ áÄ·pâm61ö>áÄœøøºê¦wи)…O+µ‘öẢÏÑ‹m—³áËkI§[‡$"œøÔN,"‰;«í‚³vPª{df7UtÞE"N£àÄ"‰âÄ…–ïÁ•Å…ÎÄÁ»ߨBD81'^ûúz‘åYk›ÐÕV^àø”ãõŽÕfmXwo"t'Ž7xÄZ¾Gúü¯äÄÒî_"‰¯çijNwSÊR7è®ß|g¯zèìßÜ£t¨ãS¾óK«mÚ°râÕ›Yv¿¯Ñƒ0²{!áÄœxéüÜn”Û¸Ü1¾?HwûGu[‡l¹¥'©»Ëïe»ÑÅG~ÀãÓÞì3õ¯z¬R›¸mj÷ÆÏw{_'~$÷=*”FLqâG¸5:'áÄ7wâÈ.»Ý3gꟲ[Ù­±·sãIÕvVk?Ðc`·G~¼ñ—×>>Ý{kÛ«àÀÚÿaü™¾¹ÙÝ8¸µ}ü ñ`/ôø0,°áÄ7tâx9\|þ2Xü¼M{®k 'RÙëìEkŒ¤=W:îy»Ÿ¥—;âÄ×;VÙÅ7 Ž3r*xèO¼Y=aáÄØ¬.¾½\¤4.eIâîr´lÕivÖp!n_ðÌZõ©Oc:9Rs™cµ±T½râ-´˜‹pbì¾D${9xdÙIä®jN¼dW+-t©ïEêX÷–U½½ŽO|±Z܉¯w¬R…ã*œªzåÄ""œøNÜn?¹`\¬¿˜^ˆ¥•FÁ'5RãW(Þë–¼4³ö8x|Ê[ \þXí¾À®ñWʉED8ñ¥ÖØ•+ÙV*;ÈxŽ9¤£]¼u|6{Êç:hœøp@µïX"‰9ñYtÕM=Ÿãs²ÏÓ´»ïí<òú-•OmðGÀ‰E81'áÄÓœ¸ÑnºÝäy°íZ¹IÛ5N̉ED8ñL'΃”̧&–Š'æÄ/¾¤“欣}jˆpâNƒ÷öôm·!üJNü°³'Æp9\Ê€ƒ}[7>ýÎrb"œ¸.h+ïBŸu&Ä"œ˜§fv»§ÖF]\jæâ僛U-¹udÃê`'áÄsœ¸»9Êt'~T[£‹'æÄKbYõtâöþdíeÖÏ'óøN¼K»îÜLJ'î·sïÖNv –)[`'‰1¥.;Ù™øžÞµÝAv –™dïÜLJ'Þn}†3ŽˆÓ(v™æ¨ q°PAˆÛ5x©ö -¸±'>\ã3¸"N£Ø÷Ò_¶ü,UÀV˜$¬‚‹Œ'~ç>¡D8±ˆ'¾Å<ñËŠ¸î ky}sdžø¹/µÀ®=>rç"‰ED8ñéרmï|ßê¶}çœX„ïSU¼ê:µ‰pbN¼Ë‰qðQörbŸ"œ8}Ré…ÞèÂspóN|v'áÄCNü¦«Ün=µ²¡Ûm-R:&"œ˜‹ˆpâ NïæÓîˆ>wcçöêióÄ"œ˜¯z=­Ñôw8¥‹p✸Ýà=>ƒüá¸?ìí,‰1VW.®²nb)¾'^½qψ¡ŽñK'Ä"œËáž7{‹üßG`çä—%våRºçÛ·‡'"œxk'~„;¢OqâÇX'váÄœ8ÛK¸!¾Ù!#Ÿ©áÄóøå÷ï ØÅÇc'Fª.2¹9µv¯ãEîüQݬŽ‹pâ£/R^:79y‰8b÷iŽÆ·5>©œŠ&Ä"œø¦NüXÞNSDœF±Ù¥¿H)ZD=³{;sbN|/'áÄÇtâö¸`íD£†­}ç³ØYW-‰9±ˆpbœþ”nŠW„ŸÞ‰÷:MÜø:?áÄœx³ºS®'>´OìÓ>«ûľîÇ4~§Q¸ô'"œøNïÁ¾TpÖí²žÚ $²V#XÍöÐÁM„sbN_Ü_§¼äK 2‚ë—·ƒîvzçÄ"œ˜‹ˆÜÚ‰§4x·¨ öÅœëÄ;B‹pbN,"‰kÍ,S+…³jû\\Qâö„4!áÄœXD„×8eÙî‘úŠ'îÖ{h $‰9±ˆ'Z`·´`.RjÜ®µ(,°‹?–v"œøzN Ûp—5v;&ëÄ"‰gº¢Å‰ B¼T !"œÀ‰Ÿ‰'pbN,"œÀ‰9ñîEÇ|Ð#,Œ?´cˉœøèNiB\°ÔïŽaÄ~¶÷¶ñ¼‡Â¯¿ÜÜ;Õ8ϱ=ÔN àÄ¡em/¨-uj{n…Ö¾å‡;w4¶Ç‹/¿Ë¶–[j3×[»¿[£%\÷ÿv{ɵÛ9GºÑµ»éuw?i4¹‹·ÉslƒÇ–8ñvNœÐà.q©å"[0ø‰~öªìí »œ,Ý[{ÌK÷¿‡Æ»¯fa"ß±íîãȉœx '—ÎçY±øœ_°qpÞmd$ñ›-L\úƒÏ}ð‡ñ¯í=Û³›sر­mÜȉœxò<ñÜIâ®6µoÙÝô.òOÜÖúìCÄgCS#™(m‘?ƒõ„ر½ä2>N œøNœ5ãÈLpÊœ–&üf 8èmA,/I­,\Ço|UØÑ‰o{l91€ïÖw"Øð¡q8¢í‰5šNd\°¥È"°öõýÚÁœòrD^š‘¹Û¬;¶œÀ‰÷tâË÷Òbv½CêØ 'ÜȉO·…=¶9ÂŽƒpbÀMç‰ED81€‹ˆpb'¾nì)ôÚ•!^‹}WpÝ$'\ЉÛ>Œ/¥_£ïÄz£Ý×`ï¡ðë/Û2¬7H¯ÅÚºÁP91pâ“9qª+p{G®ÿµ»wׇ7[£?qj´ÏR;ó5´û»­¾âû;Dî!øº<ÿzdW‹¥W?»{‹×"þZÄE9~èº91àRN<²•Ý”j5²ïôàhŸ=){û¦K÷ÖóÒýÇï!²ßrv›·†½ÊkQØà#>s?t#R81pâ8qÙ;»[Äg›ºó¾‘a|x·/g £mIa‹òƒ¦öȹÛîlå,'ŽK•×"øZLtâÔW\N ¸Î£¿Æ;N ПX4r¯'N̉…„Éí_ N œ˜‹ˆ½91pbN,"œÀ‰9±+Ññu„oØ›½ÛãŒ\81€¯ëăRû;¬4†²‡ öa;qö N7Ë÷^.ü'N ¸¬n\·´±\£ÖsûÕî†Ïå.ũǽüÅnû­ÈÎj–aÁ]ú²ýÑ"v»ûÈ…8ñZNœÐ`gßÔV -üÜ×¶0ˆìK\Øð,ÞV9¾Ùr|¶{ß‘ 'pâUœx\:ŸçGƒÛÅ7Ç òûŒ$>àÔÖÁ‘ý F̲ûXÝR‚d䉜x­y⹓ąÍÏÚ¯awÙñ;yžO]Rö)Z™ÚÏoDˆ0ráÄN¼¢gÍ88±ü§ÆTë¬bâñÇK, sÃK3Ê#ÕÓø#N àÄ«;qj¯½¬*2UÜ(œX£éDvÀK©eaNw'žãËÔ²Ëûµ‘‰öC\81€oáÄ—ßÚ—cuX„8q´Ií‰æðN7àÍŽ‰ã œÀ‰ED81€‹ˆpb'–xuÁ”-£7®ŠÖÆA81€Wœx°íCª»ÙJc˜"¯Çtâ7í,æ>4]N àĹ®À&\Ͻ´Ú·ü°aYw·çZ‹âì€Û]™»ãï>»ÔAÈnR褦÷™pb'ž¼•]doçÆFs·ž𛟷ñY‹_NîFî$(¸µ)óÂ>"œpq'—Ηû\÷ì6Ê òÍFÝ‘DnÖØª­+¸#‡+R¼1îijŠCD81àôóÄs'‰#›ØÅ·ùmÏê²~ž÷̼Æ'Èã{Ïbûw'pâ¡bâÈLpüŸÿÎ*&pêƒ5)Çåĉœx;'N5|h¬W‹Lwk¦7È8¾4-(ÐEŠÂ‰®|G¾ ¼YH¹N ¸µïÞywƒ®Ã§n™L^…8ñ!œøåœëñw߸F£1íÒ„8ñáæ‰ED81€‹ˆpb'>^qp£Qqp‹¾›rÑ‘Æôa×/ '\Ó‰·üÔŸe`Ý; ö©ØØ‰ù¥bîÀ‚÷¶ÍŸÁÍ—frb'râçÍŠãÝÄ–ô«Û’l©±Z¼=Y¤ûòÈsLíͱ´§]°óÒ!Jùûêîç¹Aª;òÈшü0Ø0ÒsP81àvNü¬ÅÙÞÚ½{Û÷RÂRwÞèïÛ¸óÆ‚Ã(ï£Ñ}ÊÙãÖ¾Ïàæ|Ýc|•×;ñ+ñû_o8ñ᜸Qz›Ý²¸±‹[ÊiâÞù<1ß0¯1ÙÞzcp6±}@²B9m'^šïn&2þõ`âÑtâì΅‰ל'Žlùß ®p­|Pˆ#sÁÉÎZÉA¹,u !îN'?ÏúGÔ¿6þ ŽFÊ¿ 1'pâiNÜž1 ŠiݳEå'•rñòš¼FÊ,'Ž<Ê’GJ Rß4¶95'îþØ58qȉƒ‹çR“ÇíõOívk ¦v·S–”¥ô4µn,>˜î“MyùÄ£Q°äì߃vœp;'>BSa‰´˜p4ü9qb'¾‚ó˜Âa¹á«5ñN àħqbáÄN̉E„81'>Û…ûòeú‘ëûÙFoñÉ^báÄN¼QUëÄ>¾Û”™6¼±G¶»¯*ñ"œpk'nÌ2¦z¢=O[¶o¹´™\|0µ&_g÷ìˆßO÷׃Óºmƒ—v­kïá§ ™pbÀ}8+ Á=Àj›qŒ ¬¶AÚÈæyñ†ÇKûAÄ·G©mHÑäÈŽ!"œp'—ÎçùÑàTk|«°à ?¼Ûà.ÙÍ2º#Îø¦¶ç¨í“©ÙYZ„.5OœœàÄ'81À‰N pb€œàÄ'81À‰N pb€œàÄÀ!œ¸u'î''81À‰N pb€œàÄ'81À‰N Ü…ÿV»¢‹7EàIEND®B`‚jbosscache-core-2.2.2.GA/src/main/docbook/images/SharedCacheLoader.png0000644000175000017500000005224110660354341025370 0ustar twernertwerner‰PNG  IHDRs½¥gAMAÙܲÚ cHRMz&€„ú€èu0ê`:˜pœºQ< pHYs  šœ†IDATxí] œUcžm¤È‡v-¦"»ÔÈR–†,¥¦’¨TÒJ)¥Q*mFi&[I%ŒO†Œm,YÆ’%ʾ0Ë`ÔûçÜyç=s:wº÷Î=Û=O¿ßtî9ç]ÿësþï–”””$øGPì•!öV⟻4 ŒÛ+ã¤/é €Ó®$i@°[*Ü%ÆŽQ´÷”›e  °b™†4øGPâ)°XxXÄ“¿,‹ö‚2’£­Ó~‡¾VI*e þ2 ôË}Çtp£x>SwHӠˀÒ/ÃPHЉÂþÓ0Ø!Fe ºcw»ÿŠ”u;de[®”~Xpø‡Ã_¶Ê€QÙÜv¬A¯_ñ"Ø€€ü·C”~XØêTì`Ëô—Q0*[лÛýW¼ð— QçÉ/?È€Ò/  F,l•£²¹íXƒ^¿â•Ûè/9UúE`a«S¡bøK1ìà—QÙ‚îØÝî¿âåÒYg™Á–+¥_¶‹½{³Ä޳ÄÊ•ƒÅ¼y—èøgxGEL|E4*›ÛŽ5èõ+^$¾Ü9m[hë(SJ¿,lqîP²Ç.Ú·n Õ©*ÒN¯!ÆSEÿÃo9]¦Ò/‹¸ qq°X$[{# ”yxÂib{£²¹íXƒ^¿âEbÊš“ŽDÚ:|ÑÖQž {J¿,â ,@\ yœ‚9V»qÊcÓ‘iy²iâ*¦QÙ‚îØÝî¿âEâÊ›“à¶‘VÚ:Ê…æüíV>„ 1‰É˜Ø¦»rb-üá7žá\¤AZ»ÛÃòíçy8+g¶W; ’nÒ@ñÂ=y''~}N[GY’²«ô‹ [:”˳n¾ù¸†è×¯ìœ c:þö¦ò•ÍmÇôú/¼)+^Ñaisä<1Ú:ÊK$²©ô‹À¢\`QÄnňX€…,ç矉ŒŒ>¥«L,¸Ôr‡LÏ«7ŒQÙ‚îØÝî¿â…7dÃK:J[G™¨¨<*ý"°°@ìv4;þ‡ôIDAT¬Ø¨°02ýå—'ŠOŒbxß•ÍmÇôú/¼/7F}·ó7me!^ò¥ô‹À¢X˜»{LÄ XHA°Šbüøã‚Ò>Ét¼ºg<ŒÊtÇîvÿ/Ü“/è"m]°ùo— *ý"°ÐWbذb^¼…±ÅÀ¸(æblÙ¢öÏ0¦ãogŠQÙÜv¬A¯_ñÂYðŠÎÙ°êm]ðdLéW@»;`Ú©lR¹e£]»ÃD«V‡ÌÅ`Ã=%7*[лÛýW¼pO¤ž:u¥­ ¯’©põ(ý °0"vD)b™íލ‘P²Ç.Ú·n Õ©*ÒN¯!ÆSEœÞ²’hÚ¨²¨s`Ñ®U}=Wœu¸6ŸÒ¼’Þ~ôÃëm>7¹šh{x%Π¹Û#nwFesÛ±½~Å‹øÕxÈQ8»A[_~™éL[ú*ýò8°øí· Ñó¬–"ùèjbËø$±'3IÓß²x†wHƒ´ÿü³ÔÕHÛ!‡¡öBFe ºcw»ÿŠÞ“QÚ gxâG:{ÁŽEÒ¥_hè…«ˆ½YeÁ„\àišÔ­¤çðDBˆx§a›1ñæ›å•ÍmÇôú/¼%§±ØÃ}hëØfoÉ]¼ížÒ/ „ªzto¡+QÞ3D.PF¼ W^ylsb+My¼/ïQÙ‚îØÝî¿â…wdvö N·<»fõÎM[Ç6{G†Ê³?N¾SúåQ`±~ý0ÑA°Ä’rôIDATÚˆ$RaV8äÁ°æe8IT¶™Šf%oFesÛ±½~Å ïÈ*í†3¼ð#­ì‰—Ÿ)ýò °‚ÇÄFÌ›0ƒ†Hï‘e8µ`›yY¹Ìm3*[лÛýW¼pÆ™™eÁ|O»áŒÝð#Ͳâ‡{¥_XЉV5#È‹2P– a›¡³¼ŒwFesÛ±½~Å o Ú gì†éo;äDyJ¿<,|p°èZ ±G,K$±g„e›¡³¼ŒwFe ºcw»ÿŠÞ´ÎØ ?Ò9Þvȉò”~yX`ké±gWÑèP1`½.°‘–e›¡³¼ŒwFesÛ±½~Å o Ú gì†éo;äDyJ¿,â<ü(¸~l³Êï:ŒÊtÇîvÿ/,b•s?Ú ?¶9Vþ¸™Oé—…ÃVl³3!M7•&ÖºÊæ¶c zýŠÞ´ÎØ ?Ò9V{ãf>¥_~œhÃ6;3 ËM¥‰µn£²ݱ»ÝÅ o Ú gì†é«½q3ŸÒ/ , ¹ ]nŠ2P–„f›¡³¼ŒwFesÛ±½~Å o Ú gì†éo;äDyJ¿<,@?nfÂ6{ÃXÛ¡@ß~{»ØºuJL Õ¨lAwìn÷_ñÂZVß~{Šøá‡1ñ9V¹£Ý°æE¬ô —Ïtׯ>WúåQ`„‰-c±em´«C°5®[[z³ÍÎ §kË–ñâ²ËŽU«VÓ§_“Ã1*›ÛŽ5èõ+^XË)x\³æâÚk»ŠO?M‰ßÑÊ&lŽ/èå3[Ç6[ËP´üO¤ôJ¿< ,@l$††â`±H¶öF('ò¸uÂ)Ûìe+,ÌwÝu…h×î0]–¤²Xì­äw`"yΘƒÇ2MåÊ•ÄÅw¯¾zƒíCÚ |ùÍÖ±Íþ·yáô!ÚçRw`'´ßIº2E[ˆé€Œqnæ\XíÆ)MG¤uëdSI¶ÙŸŠöÁÓň]EíÚÕK‹Ô \ ,‚,Œ¼?õÔfúÙC{ödÚ2`7ñô›­c›ýi錄ŠçUêŒç:P!ÆÇ0ÛtcWNl …?üÆ3œ ‚4HOBÅZÛìe+.^*}t¨èÖ­•%˜ŠbÆßHî‘F~í‡ÒO»…÷! â$=PW¨>5¬e¼7F,dZãµeËCDff?ñ矋m±3´ÎØ ?Ò9V_äd>©+¾’0,šdlx‚?üÆ3¼“é¼tõs›ï¿ 8÷ܶ¢aÃÄ%—tŸ|Ü7‚ôIDAT2ótŽ…ç_}5[´m[v¸C*†ÕUF,ðNþF½‘Ü#üJ?]Þ–äç½$ˆôAÙ¡òc2Ó¦ ÄηiM¶ÇúÙnøÑ>'º­³KNÍåJýð°0w‚÷ö5Ðõ駯Í›7½{wÒ 1ÆšqŸ}mÆÔ ~þüó"$Ô©SSï§T ««LDÓVY–tœ¼ºGÅ kÝÙ_Ä“x‡?C|ÿý¼„Òƒhä9ÑÒÅÖ9Á7¥_Ÿcá1X‡2²ø KMí Z´h$žyf´n%óFy+X\üñ ]ŒyøÛš–^§KPm|QúE`AC¡†¿þZ"ÒÓ{‰úõk‰Y³RÅßßUJ , x>sfªžîÖ[{éùìT§Ë.`XÄ˽»WŽ2|ÖÎÐ X`âæ+¯Ø¿2ÄiYj}´uÖ²yPúE`Qê@ãAX?–a ~ùåì}èa²H—¨Ã#è£ÀpXíy99"77WÿËÉÉù; Bºp§È5¼C¼ß^P,D™wÈ¿Uì.*qìÅZ™Ù9"WaɃХ¸`»ÈÑžoÛ­å×ÿ‰í[sÄêåËÅêµ9bgÙäBiõgg‹¼m»KÒ ±{[žÞÙÞP›òÄ·»Íe‹ZÙkW/ËWoÔê”ÓŠÒÛ—-¶–VX,¶çi}0ÔSZa„?”á³6®F`Ѻõ¡úJ)ó¼ZÓÌOt¡­³—‡J¿,4“d/±½Z¾U(Ъ­VÀB¦ÃpI¢ F€á&°(ÜÚÓE*®¼®Ý^$ òæZÎÙ¸«8Ì»t±˜#?Tfz®ðÏ»r&ëåeåAì©¡ ²Î¤¤¡B…ÄÚ¿œÉÉ¡ú“3DsŠŒ’I’*ÊHOn*[vVš¹ì$±z[I)%íKJ[-t¸Q¼M¤¡Ü¹y¡Šcø_¶Gʯù zhm}åV ™ßóÞŸ¶’¶Î¾)ý"°œñ@(ÃVÃV†Âbõ\>Kôáôãw¦–KIóU)›–=ÆÛ–§éŽyãöQT¤E²C`bÌÚí"?+U{—"r´/û¢ÂBQ¨ý ÄJßíÒ\sÑ.±|(yªÈÓ|÷¶Õ¡2sÊâ ‘77EK“&òµ,»sÒµßZž”¹b»Vo^ò$‰1Ù»ôžï\z4I“Åö’ ‡ÞŽ‚|1ÏS³Ä.´©PËo({çÆ1¡¼iË5ø"ÄÎì’º†®ÕÛê3ÊÕú†àÌ®l=ýܼ’HÞ‚èþS¼°6´ï½w³ëûà˜å‡÷ּЄ.´u±Ó.4 \çôIDATúšÓ(ý"°Ð,“³Äw³> Ä0†Õ°‡UÛ ,VÏÍÏ}xÄÜßHEçUê"±VcÄö’‡ùËu';tí‡bµþÕ?Tl-(ÒAœzQ<|QÉ»t‚BäêŽ=E²ÌÉb§ªHûU ²R4gž’¥ýÒ~ëÑŠ4QêË‹¶‰ŒÉ“Åòä*ËõºÓDFÆP°”þ+È)šì¤få—<²*[‹~”Ž~ˆŒdY7ÚW Ñʺz»(У6É „Jë‹à‡âEpt>R9M´t´uÎ˸Ò/ Í9ϧëŒ4hÕ®H…Ì›èÃ#²Ÿ‘^•²Eàù,“ì“ñå¯ý%''‹íO/3e²È׿CŒ)y'ëÁ5mõ6mŽÂ¶Òw©iiº“ǻԌ\-"PR¦(õ먻(_nHÁpCáV‘ª¥OÉØjÙª‚<9”¢Ev­ÖÛ> %—Ã7•X”Z¦ìÂP“¡AšPû’‡¦‹É7)ébu"3f dÙ´°%"åÓùÏ6ÒÖ¹Ç3¥_šrv×m(Ъ=«çå= ÂðHyý7¾SÊÖß•ÿbwŽH†ƒOß(¶oËÍ3І0 P¼k£þ.mùV ,‡"Úâò]ʘ,±1{£ö‡ –%±‹’2 0þ+Úé9Zº`¡"ÚpEîj‘µ:[ì*Þ%Ò]ÐÚ•œš&RSB`gòFÿÈׇoRDnÉÈ…UÙCµ¡œÒÚP"Ic²µy›¡>cÈ¥ ·dˆDgB¥™#û¡x‘¸:o”½ ý¦­s_¦•~XhÉ}†ØÑ†XBV퀰X=ä‡G–éÎ7DÃÈœŸ9•Œ dl 3Èysóv‹Ý¹¡¹C³´Õyy"Oûà ¬èØ]â—o+“Ћ—eÊ|úê¼íâó’y1»S‹¤Ã™kó-Vk«IòÖ†êÂ0ÉÖµcô~M^¾QÛß$[do\Št”F ö†‘s*ô²‹e¤%TöÖœÕztu!À!Û·ZŸ´!Ój‘˜åZ$¦ÿ”áKLD'1 m7äYé…f¦¼Á”xµ¡@lbeÜäª"eWXÈzƒ<<¢”-6š€šp©— 9e}h$u¹ÈÕ'n†"²\á¼·êïRE )Sy~É$Lcž$­¼}ÆP±MNÂÜ™#†–D&6uòj±K›g1€£Ì0J‘XŽ! ­ þÈ6"úPRshâfø²“ӿЭ%K\C}ÖÒ–`¢m«1‡#I‡ZÊt(ÂÙ_)—¼úÛöÑÖy‹J¿,4“ä-æÄÚc(›X7¹ŠµL䃰T$¿Ìæ+ýáôIDATÔá¥lz?Ï%+ÖWšêBãß8}‰Me›[«x‘:/u+hWÚ:oʯÒ/ Íöx“IÑ´Kn·ÍjH˰õ‡G”Ïõª”Íìæxï4/ü¯ó‰ª/ûëmweWé…¯› b“*y¶Çþ3Ú÷ñ²~´Ã58›ýÏíª”Íi7ÊúÌP¼ð®qN4ùWhë¼/³J¿,|éÐì Z»€êÂðÎ&Áf]‰xöú¨”Íìæxï4/¼o¤­t1ˆÏhëü#«J¿,|,ì Z.;…¬/‘‡G”²9íFYŸ™Šþ1ÖRG‚x¥­ó—œ*ý"°ð °p"he¼œ²ÞDQÊfvs¼wšŠþ2ØR?‚r¥­ó§|*ý"°ð<°p2he¸œ¨_Ô«WK?Êý·j—_ž)esÚ²>3/üi¸ý"ó±¶“¶.Ql…§–Ó¡@+ƒà4°møê«Ù¢wïN¢Y³†ÂÏ«G”33»9Þ;MÅ  ©g^¹ÒÖ%’­#°ð$°p+hedܲ-ÿýïõ¾^=¢œ™Ón”õ™) xA`!õËí+m’Åıužn‡­ŒŒÛÀmòóðˆrff7Ç{§) x¡Œ¹•Ìó™ýô¡­³¦qbØ: Ï /„­ ª€…l—‡G”3sÚ²>3/¬º”3^í¥mÝþéëo[G`á:°ðR(ÐÊ z XÈöù)d¨œ™ÙÍñÞi (^ìß°KYã5~´¢­‹ž–þ´u® /†­Œ¨Úé—¡rfN»QÖg¦€âEôÞJ7ø,2:ÒÖEF§pòä?[G`á °@(ÛYÛq¶G8áŒõ¹W…ì×C†Ê™™Ý呂âEÅ ½”=^÷OGÚºýÓ(R9ò­#°°XìÝ›%vì˜%V®,æÍ»Dß—á¸ãŽGYß³K(Ím†1FûѼ‹T œL‡v=ðÀ•âCj‹víÓéì•6+gæ´e}f (^ÄÏØ;)ç^®Ël7ÒÓ{ ÚºøË™?l…-ŽÌì±á¢}ë¢Qª"íôbÜ9UD—–•D‡£ªŠ†WíZÕ×Ó ­ F¸6ŸÒ¼’Þ~ôÃëm¾âÔêâ´•ÄéAs/´Y93³›ã½ÓP¼ˆ¿Á÷‚»Ñ†pvƒ¶.¾2f¦³·mEÜúo¿eˆžgµÉGW[Æ'‰=™Iš¾—ýÃ3¼C¤ý知qoG4F†mޝ0Ò^93§Ý(ë3S@ñÂ>~yŸè¿i7œ‘#¿ÐYéE\:ˆ{aSÏÔóôIDATÇ*boVY0a¸Gš&u+éy Ú gì†é¬ô‹À".ÀK1±Áj¢f¤ÀyQÊrÂ@°ÍÎÐY)›ÙÍñÞi (^XÄjch7œ±~¤³Ò/‹¸8ñ,úŸVCÓÕØ#È‹%’Ø!V¥&Ûì •²9íFYŸ™ŠÑØ cZÚ gì†é¬ô‹À".N|þüKÅØ³«húW1`½.°‘–Q‘íúÍ6;Cg¥lf7Ç{§) xA`«]¡ÝpÆnø‘ÎJ¿,ââÄý(l³3B)›Ón”õ™) xA`A`ýG ?üÊ×¥_q~ [±Í΄4•²™Ý呂âEù2V§„|´ÎØ ?ÒYéE\€…'Ú°ÍÎLÂRÊæ´e}f (^XÄ ‚h7œ±~¤³Ò/‹¸ , ¹ ]nŠ2PV¬JM>¶Ù:+e3»9Þ;MÅ ‹hl…1-í†3vÃtVúE`7'î·ÍL`,ØfûŒR6§Ý(ë3S@ñÂ~¾q¢ý¦ÝpF~üFg¥_q@˜Ø2[ÖF»:[㺵¥7Ûl¯‘PÊfvs¼wšŠöò<Ñ€„¹?°u8¾ —ÏlÛl¯Ü+ý"°ˆ°€òá 1‹E²µ7Ò@9‘Ç­NÙf§”Íi7ÊúÌP†Ï^ž›q"ÞK»"¿Ù:¶ÙùWúE`W`€dŒsC0çÂj7Nyl:Ò ­['›JƒÇ6Û£h ¯R6³›ã½ÓP¼°ßR§‚p…Ý@ÄÓo¶Žm¶Gþ•~XÄXÀ  Tˆñ1LÆÄ6ÝØ•há¿ñ ç‚ ÒzÁ±Ív+›Ón”õ™)  Ÿ=¼ö‚;ÝÚ gdÉtVúE`a«S‡0`ÙÖ$cC*üá7žáÓF ’úØæø ¥lf7Ç{§) x_G¢W‰ž†vÙò2•~XxÒ¹'º Rÿ”²9íFYŸ™ŠÎ8 É9ûJ™RúE`A`¡ ÝÐ(ØG¥lf7Ç{§) xaȸ°ôIDAT¿©K¤mPe@é*…­2 ”Íi7ÊúÌP¼ ó ªóc¿í“}¥_Q‹[ÇŠÕˉå«G‹m»3Ëãâ‚™"'{¬Èß•Qæ9˜h•O¦ß^PRNñ"‘—3Zäm_$ôw9cEnîÄÒ¿œœIbw±}¡°‘¶vÉ€R6³›ã½ÓP¼(_Þ­l–”i»hëʧ¡¤¯Á¡“Ò¯ˆ€Å‘Þºtٜ̼z›¹éMBïS‡ˆ¢Ò/àðù ·öÑÓÏÍ[¤Ùð…“Dж—CòÜIB¾“õÈknapDeL^KùuÚ‰²¾}) xN¾ÂÛ,©“´uáhÇçRF‚zUú°(Þ9,’{‰ZÔ`çÆž¡ûÉcC  hšHÓ@A¨Ð&"{W( Q^¾ÂüþzúŒ­%à¤hŠHÕÊHɘ"ä»ô왢¨p‘((ý— pP×ýVʶ¯£ãg) xamCʳYºìÑÖ…l>m1é`!J¿"ùËOÒAÀòmKJˆ¹HlÌê#ÖæÎÐïwåôÒß§¤vÔ¯©8€–—O‚‡¹9³EqÑQ¸k¢,·ì» QXˆ?Y·µAð£Ãa›ƒÁK¥lšZðŸ«P¼°–½òlô•¶Îšn´e¤ d@éWÀb›,:Šü"+âeˆå©ˆVô»Ä"‘‘ŒßÝÅv-²Q^> dCäÕ,ä³Ðµ·(°@Hh+žð™—äBʱ«••ëP¼°Ö‘òl–´u^Ò+¶ÅZ†Ý¤‹Ò¯€E~"ÚÇnÙ‘%"G›Ä™½U‹6ì!’K†ARRÕ<Œ¹¹óEyù$°H×"EzÄb쾋¼ùš1ÈÅÅø“uóê¦à°îèåO)½»ÛP¼°æcy6‹¶Îšf´ ¤‹”¥_‹â]£Cà!µ—ÈÉŸ&²³ºè!äŒIbkFtdlÔVq`%Gö  $¥ ¿•“O‹òæX Í!òòB+Crr&ŠÒ$Œ\hö™Âì(esÛ­²~Å ký¡­³¦‹_tít—J¿"`VÁ¶† šI"5}˜((ž)Æ Z‘6Ȱd™ÈÕW´9Ú’TË|ZyXdå«É›˜šš¥æXÈFÊkÆÖEt¨¾“)¿tëîS@ñ"¼¶´Y´u¾Ó;‚Œð2nm”~E,B É M¤,*»‡Åþk>ç ³ÿ¾°M¤Qt2 ”Í}Çô(^쇱ڬXóí¯=|O»ã}Pú°ð~Ç(|ä‘×d@)[ÐݺûýW¼ žxMOØÿˤÒ/ ÍÚùŸ¡ìƒwy¨”Í}Çô(^xW^¨Ëä_e@é•­2 ¶Ke‘ôIDAT”-ènÝýþ+^ÐyùÕy±ÝÞ•]¥_¶:*w•À)Þ(esß±½Š”K§äŸõGÖ”~XX0ba« (e º[w¿ÿŠÁ1ötìäµS2 ô‹ÀÂV§âCYw‡R6÷kÐ[ xá]y¡.“7~•¥_ŒXØ*JÙ‚îÖÝï¿â—_Ûí]ÙUúE`a«S¡xW œâR6÷kÐ[ xA¹tJþYOpdMé#¶Ê€R¶ »u÷û¯xcOÇN^;%J¿,lu*N1”õx×x(esß±½ŠÞ•ê2yãWPúE`A`Áˆ…­2 ”-ènÝýþ+^ÐyùÕy±ÝÞ•]¥_¶:*w•À)Þ(esß±½Š”K§äŸõGÖ”~XX0ba« (e º[w¿ÿŠÁ1ötìäµS2 ôË,äC^µ£àq<ÿHƒ8Ê€ûn•- NÓ®Qì—!,è<ãè<©´á•ÊÆ?wi@ù /Ÿ¤ i/Ѕ׌ìܲ·’ÿHƒhe@Ê×äšíqTx‘þRV£•q¦§]„ Hùñ¤l{­Q’XT*O,2 åÇkrÍöX˜e@Êj,rÎ<´R~Ìrå…û$/4ÂØI,*'òc”)þ¦S÷¢ HYEΙ‡öQÊ'eÛk’Ä¢âPqb‘)?^“k¶‡àÆ,RVc‘sæ¡}”òc–+/Ü3b±Ÿ¹³W%‰!Ó“Ä ™IbÆæ²Âœù|’»8ILË.ûJo•O¦Ÿ©åÓ ÃëIbR¦ö·!Ièï´ßïQcµûùZšÌ\-ö|¶vµ2(¨k˜Ö>´e™ÓÈzí4Ö‡z&ie,ÒêB^ã;ÙžÒ¶ Ï†vŽÕÚ5cÓ¾ušÛàÔ½—•Í Ï6xàHYuJ7öW•Í’y¬lˆ|g•O¦§­³Ï6Jùñ¢NX”,FWd$‡¬S‚2Q¾ïš$–Ê —/CsÞ(§÷Š’24 ÐZ»o2:IÈw²y¨¥‘ïJóêÑoß6vцTz\­Ú)Ë”õÈëŒWT}ò™¼Û"ŸÉk/ `ëtë·l•mòŽS÷/¤¬º¥+ÆzÃÙ,™ÆÊ†à]¸|Ò¾”Ú,Úº¸ÛG)?^es, NZ*®™ÚW¸Î¸–Z¤@û’Ÿ½°ä~p‰ÕðI(Ìz^^¾Œ5¡ô}4€¡×¥•ÑQ+£õÍ™—¼ƒƒ^¢)á"-2€¿L­-ò]i¾’6gnPõOÓò n´i’v/ë°j§,³'êÓêÔ;”÷ò]ymÑóiퟩE/Pú`¤Ÿ[¿%¤cMŒôIDAT?Ì‚Î{:u¯É€”U·tEÖ[žÍÒÓÐÖ‰%´uQ-—'°,¦iÃPüAë”Ãq“6ä …þ¡ls¥CÕ¢Hױı–—O:ìÞZÞLMP340Ò±$ï>ï4`Qä;3°˜¦µÇÜÆ¹0À3™6\;e™ý%ÈÑêÒˆ¹£ä”¶³¼¶h }@ÔE*7¯è;þ¼æDس HYuS_Pwy6 ïÃÙòòIûRjChëân¥ü˜åÊ ÷šâX)öŒ`1MVï•йZþ>-CÎl¦Ù(/ŸT6)ò PîÝ"­|ùN‚Ù©Ø¥Ñ ‹´áÚiU¦ž¶s’˜S,dûäÕØùL¿jý±ÎšN²­N]e»¼ \lÁDy2 eÕ)ÝWOy6 yÂÙòòIû"û(¯´uñ³“’¦åɘ[ï,4űR8 ½Y½«ÑÚ~¦†¾›”|·.`ro-šQ^>©l½´ˆBk–‹¡ú21i³d2¥Ì×_søÆ¶NÑ ê5¶QŽmöÑÊ)¯Veö@Òús»ŒX”Ó–ž µ¹%­1¶Í­ß^V6·”œõzàHYuKWd½åÙ¬òlHyù¤}¡­+k³%Íãq•òãEý&°,JJs´c5G;ºÄ‰6Ñœ¹tè}à\50Q~è¨9ø¤žIâN :,òIe+'°(9œÂ[¾ðú>n*ë&€ˆDü¦Sl¯7md$²ætÏ § à×ú>ÿ|{³””nÏW¯^í/³Â7lØà‡={þ­ì×¾±ÝtÌ”`Èìì•Ù†Á®Á¾ÁÎQü' >_ä‘5—׬YãO³RvèÐá3ÏLɹýö¹óóß;–Šé?Å$ÏȳD•;¾8:#ãŽÑgÕ}óm·ÍšÜ¿¿Uf»û–¨4Hô~XøX@8ß|ó·~Ø.£b®X±bà¦MO\0räˆ%Í›7ÛŽ÷W]5ø>(jAÁOõ]¨Ù?:eÊ€wdQ‰—^Ê=ýÆo˜Ó¶m›5j¸öèñÇ»èŸþ>`ÕªûíììyèFË  Ÿ 0|×®oïÜùø7¡œ•+WÚóûá³Ï>m±xñ£.¸àüMµkTxÊ)'¿:cÆ-Ó^=ïD™øWy<æoòÑK2ð믿Œ™ÒVÖ¯_ï§Ž;¼3uê”[asöîÝSfy3ììììì™—ú¶D¯[ , øþùGÍË/¿ì‘O<áõòáï¿ÿªöÜs›»O˜0~^ûöíÞ‡Ò÷íÛçá+–úî»o———ï¢W0ÒŒ4 Š `>Ä¢E Çtï~æstàoç×ó©¥Kïºö믿:b4€Ý‚ý‚Û_Z¾÷¾NX$°€²áKà½÷ÞMŽFñðupß}÷^uÙe—>Z¯^Ý|Y dù ÏwC˜2š²˜Öû O‘Gñ’ÿ-®’›ûb—‰'Ü~Ì1­·zè!ß_}õ{6lx<õ?~¯M=°[æHF4ù™Ö[rM`‘@À¢¢ÊCñÊ+/Ÿ:mÚÍ3ðqðÁµMMíµ!¥÷õGôIDAT3sépL¸ªhùÌï-å'?Èheà—_~®³fÍÃ}0á":u|ös" (ORž,,ÊŒwJÁÀõÇhðÐC«¯8pÀøiÕªå'£G_—ñôÓOõdÈ’FÄ(+ü¸ò€9Z Ì—¢-ÿÄÇùçŸ÷dVVæ0Î…H\žWTŸ ,, £pákäwÞî8{öm“ºv=c Ì9çœý_œ?ü ­1-ÓàPü+ˆ\nÙòB×ñãÇÍoݺÕÇúÝС×,{â‰òƒÂ¿|uR' ,,"f¡,,üµ6–‹ >,óè£ÚñŸÿñÕ5×\}÷úõëzcF¸9=ïi(Þ•Ÿ.¨‹èäWô}¨nÝ:?üqoÝrËôéo½õæñä›wùæUÞXXÄ,ÌýñÇÛZßqÇ¢ë{ô87ÑŒÓO?í¥™3oÃıW&³¼ðÞ}™øä“[ÍŸ?o¼Œ@^xáOÜ}÷²k¾ýv×aäûüñ3,,â,ŒJPTôgÿþ÷™sÆŽ³°M›c>†8iiý|ðÁ•i»wÿ¯‘1-Ó€Qœ‘ââª>ÿ|Nʸqc´lÙâSlD5lØÐ¬'ŸÜt>t–|p†A 3EÜ…Yq¾ürç‘Ë–e íÝûâõuêü ¬S¦Ü4»ñÁØ™ÓóžŽ2ÀN»ØÙ²OŸË×`ˆã„:¿ÍñÞ~{k'Ò8>4&÷¥#…íÀ¨xXû~ÓM“gw\§­Á~õÕ—ÿ1¦åï}–4!Mö'Û¶}t Î êÒåô\ KbÉø½÷Þ3„àQvö';ñzO`A`á(°0 .†F0D‚uñ8åg `C) ÏÒšå…÷ûÊ6²ÃnºcÆ\¿gqD“¯¯½vøR, ÿ믢ê¤Ù¾4#Mì¥ …«À¨à˜ä‰v0é“?q®IÏž=žÆIˆ˜hfLËßöÒ×ÛôÅ3+W>0Û`#êwÒI'æÝzkúÔwß}§yçmÞ?žf…Ã.ëÖ­½Ûã+ ËZ±¼[ÿö[áAæô¼§AMdøè£ÛÌ™3ûF€nìŠ{ñÅ=†íøÿ÷¿ïIä~³oþÓk  Ï ³AÁF\Øëì³ÏzcÇݺu}vaã..iõŸñ1ó—÷eyˆ!ŽÍ›Ÿ= »Ý6mzôØ+fäÈK²³ŸîÁ!޲´¢ìx‹¾FãCŽžzêÉó`t±Õ8v4hàŠ‡~¨ïO?ýXߘ–¿½etÈðüÀâ<°b<· ÃôIDATॗ^²Q‰“O>éµY³fÞ”ŸÿÞ±¤[xº‘6Þ¢ …/…Ù|ñÅçMqDs¯^nÄÜ ¢†Ã‘^}õ•S°E±9=ï½eˆ‚Ì>x¿Ým·Íš|ê©§¼0URË—ß%÷|¡ŒúU/,,X!dûŽãß;tH~·~ýz?a’Æ£ypµQVÜøý÷ßUê§Q£F.Ƽ¡£Ž:rçu׺ÏðÎ6±NêEû´ÅâÅwŽ:ÿüóžÄÜ LÀÃI‘¯¿žw"ænì/?ßûS¦0Ä=Spþv€Åy8—“9ÄáOžRíå#1Èœ ÆÎ'L?¯}ûvï7hPÿÇ+®èû&ç}ÿýw‡ÒpÙk¸ì¦/N½çž»¯ÆÉ ;ãŒ./bˆ '‡Ú]7Ë÷·ì{+XÄàT(8T|³ |óÍ×Mp45fþ×­[ççŽ;¼3iÒ³·ly¡+¿jý!/بNèüxˆåÉ«V=Ø|Íüæ½?xJ>¹Ã'  F,â,Øéó•W^>õæ›§¦ÃIa“û23—߱㋣iìÜ1vfºýYãÉ'7?lØÐ,ÌŸÁŠ qãÆ.xþùœ”â⪚ÓóÞ|#¼Ï‹8; ½÷…Þia߂իWõ8pÀ‡zÈ÷­[·úøúëGߥ‰þùGM§Ûäú°YÚÝw/»æÂ /xC]»ž±eþüyã?ùäãVA¦ ûN»O ° °`ÄÂAÀ)¬o¿½µΆÀ¸=œÛ9çœýß… ŒÅ±ØñTn–ro½õ4´MòôIDATæñÓ§O»åøã{ C˜ óÐC«¯àÊ:Sêˆ=2@`á S¡Û#Ä~¦ka᯵ü±‹ŽÇ™ØXéšk®¾{ýúu½ýõ—ƒýÜ7·ÚŽ(– ƒŽ8õ"L²Å|HGtK.ƒT/#’¬:¸ãŽE×÷èqn6¢]ºœž‹m¡·n}ë8D;‚dœ¢é+—ËÊʆ¥À [JJ·çÂáhÊaZÊ@Åe€ÀÂCN…]qN$brá3ÏdŸ‹=Ž9¦õ¶FîNKëÿ V*ýHm€¬7Þxý„iÓnžÑ©SÇ·ëÕ«[п¿UkÖ<Üç—_~®“HrÀ¾Ð.øM,,øìøòËG.[–5ôâ‹/z +M:w>þÍ)SnšùÒK¹§aÃü^kÆÇS¯¾zÈ=˜Û¦Í1á”ÚÜÜ»pˆƒÎ×oÎ7‘ÛK`á§’ÈBȾEï$^|qË7Ý4yÖqÇuÚŠI‰—\Ò{6uúú믎Hš¢/K—ÞumÏž=žÆÎnY´há˜Ï?ßÞ,QúÈ~D/ÿ¤™·iF`A`ÁˆEÈÃZ¹òýú]±»€¶mÛæCìÉðì³ÿ=»„úÅcˆ[¤O:åÖ’ß­_¿ÞO¤­Äù,œÌêmgâc;í—#‹p*TûÅO4†s~óÍ7:ã ¬ÓN;õeœkrÞy=ŸÊȸc´÷kÀao=¶þbð†y$E7ÞxÜ—_~é4žÁBÙö“î±­!y%° °`Ä"Áeû5¬]û襘›€C´š6=ú ¨µqã†^¿ýVxÐþŒ!V¤D3‡ÀÇÏ—W.æ‹,Y²x¤\ýröÙg= àÃI $Ê“¾ó‡|X$¸S¡"úCäӼ߻MžuV÷͘·Ð­[×æÌ™}#Ž·jŽÇI®‘8}ì!cÄ‘ÇXÀÆk¯½z2æ„$'û^Æ ~4hà ìåaLËß”YÊ€¿e€À‚À‚‹ËVZàHðë®ugË–->Åj 8ü‡~¨/@¶#¯\¹Òž¤¤$qðÁµÅR×pF{I`¥ Ò"v6úºòÊAË$p ìäÉ“nÃ9*âð·ã'|N¾B,ìThhÌ2ðÅŸ7Å*Œ^½.܈mÇ$ŒØ/Â<‘s:p—1¹XƒíÊ/¾sÔÎ;Ž2×Å{Êe 1e€À‚À‚ dËE᜿ûîÛÆÛ·Öüý÷óÛcÃ'l;ÃÉ0™çYÜÿ}ƒá豋äìÙ·Mºå–éÓñÕ#GŽX‚m«ùŽôIDATÀjÙ@€¥™æÀ[ÂÀ°–¤b#[µjùI³fM??úè£vüç?G|Õ¤Éáß`ëkLœÄŠ,]EtâÀký^¥Jåb#X0üÞ«E%þ-‰fì5_ Ea¶kQ—<˜\yËzäéœ7è‘)DOþÓº“¨Óð0Q©råýÌO9ùä“^ÃÆb8[¼G²/‰WœÛ‘¸@ˆÀ‚À—ÀâŸþ>àé§Ÿê9jÔÈŘܧKCŒ¯E 1`Xáð~“2õð9À ½4ü¼úÇIWˆ”`ˆ C?Ãn_/.»PëÐ5U²9VT«Q«t’¬³Ÿü’_®ûùruÚ¡±>÷@¢y}&Ü©/yÅ~)ãêUGÄv%à!° °ð°Hés6+þÀ2KF±Ÿ6ÂJØ€‰ÎÍ=çFÚ;C{l.†½R.¹~ž8®û¥ÚRÕÃËè6aÃÆ`\Ð'–÷*? ,,|,à´2rÓ7¡:þ¬Ëô]¥áT×JúþÉ].ç ˜¨mdt¯÷‹ú>̨§ãsÆñ‘Χ3v-Åæ\×Ý™­G"°6à®­JÞÕvèµë¢ƒlìÂ]NÁ™Î«ŽˆíJ,ÀC`A`áK`atXØV;"bç“Ï(kÖN*‘ÆÔ|­Z­†8ô¨ÖÚÖÙçˆÓ/ºFôž®o‹-¢±}4¶™6–Ïßwޤ¡5 —æë`wòÊ7õZ!ÃØº.±i[ÝCŽÐví¬\ ̲Œ ÞŽjÓYt¹x¨ž1YÓŠÖ2xb9p¯ò“À‚ÀÂ÷ÀÂÊâ\ lI}õ¬‡õ¥wØ eÕª]/¬‘–ÆWì‡7ÕÎ8Y?·£Kïaâ¼!Sµ/Ær‹ißôIDAT ýLŠë—<«ƒÌïà°‹µÓ´âK¢?PÀ™+pðØKÛºcÛoœï‚-¿OìÑ_´9él}“+DÊ J+éÛÈ·<®«¾e8¶G¹ˆbTGBSY–WÛ•X€‡À‚À"!EyÆg?à ^…/Äsݨ|FuÈ‘­DõšE>¤±ÆV!4Ýøè6¢Yò©¢ýiçé_8ôªçà›´Ã°Bgˆ\uë*1rÑ&}¯ªç€íÂ3^,ŒØI”×7¾‹ ä`xlñËè Ûn”à3œ ‚ƒÍpðö‡8³ïõÚ܃tÀ ™9¢eQ¿ñ‘¢z­ÚQË €ä[yCfy@mà´ûŘ¥Ï‰[ÿ,.gËHY¥O,îU~XXXDâ|1‡§ƒbKel~á°úÒ½Îg÷ÑǸhÕQU#º! wE¯(ë º 5Gu”8¬i[=ÌOÛÒCsdë`»,bœýì´ z¥×ð[õÉ{X €U28á'}–9íTsR˜c‚ÍÄh°¥9¾ªlÐÏÛ6íÔwrÈÁI£8, Ñ8\ŒÕ# „ñ~œVŠ/sã馒¦pÐøŠÆ;¤1žlŠ2PVèdÓ_ô:䩦X‰6è'šj«Ð6´mE›qð-æ¢oÆÓLÑ÷Þ£o×2€\Ù“Lû—ždН~D®pˆ"Rˆ„&WŠ ± ã º4€yŒ>”‘|F/=Êð `2xÆJý`·©«ßÑOs$íì¼J¹ôª#b» ðXXXÄa©&œ.œ2æhŒŒºãIè""ØMáøsŒãDT€쌈cºñÅŠ(Idaq5IO: ^ãCœ¿P€F8BsèŽ?ërm.ÎÕ:XÁì—[¤ÏÉ>ï1ý:Ìï(ÂQó^,e„<±¸WùI`A`A``¯¯M|Ñã+~Ö;t‚I}ˆ4àK‘“!3Wë“ô°¬K ­èyÕ=ze¸˜ŒŠ0ý ç^¡/?Ä3NÍÄ*‚»èsFhp†m0ôƒ£½!ÁDAœ¬ çŠÃÛjTGÿš‡ÃE$¥êÕõ!Ÿ*UÐ'Çâë<†Œ_û•ôgx‡½FÃDÈ‹2P"(u .+Ô6À©l¡mh#ÚŠy.h;úG>á$Zô}EŸÑw¬þ9ÿê›EêˆYzt «ð†Í]§kŒËzA?ØkÚš÷õèȼÿ~¯¯0Št®B¼øìt9N‚    §ë‹mN†ßèF`A`A`Agïg侮j“$a ýfÔýÒÄÛBôIDATÞ%›·‰ÑsWëaþ÷lKtÀU,f®Ècµ9 cïÙ*•¼Ÿ½*G ›¹\ š¹VLÚ Ž¢Ï|~»¾yS(_ŽVFž˜±)ô¾ì»PÙc3óÄü׃áè–   Gmµ“•±. wEd@G§rêËÜ´Q4)n’ÎMg‹Ì· DoÓóÐûd1)·HŒè—¼Ï¤Ç.3óuð—±*cŸwÈÛkñNîÝÄ\ ;äMò´"úǼ´ß‘ʇBñMtDG; oÐË\´"­+Ò@ÁnÑ«%¢C)xÐv9}¾@ûÛzÖy®˜ù|¡X¤ýenX[ƈiZºÌMÙ¢µBRCùÖdéï{.Þ.–¼R(ff†êh=a«È(y× ïr ´òB™–²%"'u'RÇÀt‘   :31?3=¦g‹™Ù»ÅLmxctæVÃÐD¡ÔU³DF ½¦Ý”¦ç ‘P¤aîâÉú³>«40Rú¬* 9ËçóDG x4Wú®wæN‘©ŽŒ\üÔ0ba ,*¢ÍK`A`A`Ag¦G)†ªƒ‚J=gæiC!ÒÑ[‹é¡¨†t† L(`!š~m™&F¬Ó@D è(ó.inɼ Y'¯’®½J:Gë ˜ž€$ ° ° °(užÁudó×å‰Ñ÷äkшb1Ó61lxª2†lQ„}Å” H“,FoVt“s'ú¬PÀ¢ç­bâM!ÐÒqBž•À¢÷ŠÝÚ=6ÛŸ*§¢Ž”ùËÒ’À‚!€k    1czHtŸž£íï/ NÑÅèìâ’á‰BÑß4‚ ŸºÃêD6$ŽN Ñæ%° ° ° °`Ä"Áe€À‚À"ZpP‘ô îTÂ}ÅòyÅ¢~¢EE€B´y ,,,,±Hp ° °ˆT¸[÷¢ôIDAT$=E‚;?}Y³­öDQ,,*¢ÍK`A`A`A`ÁˆE‚ËE´à "é ,,,Ü©0 `OÀOt%° °¨Pˆ6/# .Ñ‚ƒŠ¤'° ° °Hp§â§/k¶Õžè EE€B´y ,,,,±Hp ° °ˆT$=E‚;Fì‰ø‰® Ñæ%° ° ° °`Ä"Áe€À‚À"ZpP‘ô îTüôeͶÚ]!° °¨Pˆ6/# .Ñ‚ƒŠ¤'° ° °Hp§Â(€=Q?Ñ•À‚À¢"@!Ú¼¾M›ý ä™}G‹ÛŸù–_ÙD”ýÈÀœ§¾Ý.% 7Íš5ýrÙŸþQSê ¯ NÉ…/…QAöìù·òÛooí´dÉâ‘x ]»¶T©R¥XZóµjµâУZ‹¶'Ÿ#N¿èÑkxº4}¹s×f1ýÑÅ¢~&ð`´ÃXšW¬ƒÝÉ+ß×Îß úÞ°Dô¸r’8±G?ѼÃi¢î!GˆJ•*éÃf9Æ}Íš5þìÜùø7‡½fÙ½÷Þ3ä£>lcÔ þ&˜pC,,|,¬ç¯¿Šª¿ûî;~ø¡¾Ó¦Ý<£oß>ŸpBç7êÕ ÍѰ2ÒÆgT¯)ÞT4m²èØí"Ñ¥÷0qÞ©Úc†2ó!qý’gÅ”Uoëó;8ì’8ÑŠF*æ=û?qËÚÄÄ{_ÒÁ€©÷ˆ‹FÞ&º÷«†þ¢ÍIg‹&-޵ëR.h0ÈãÞ&Mÿ¦k×3¶\uÕàûæÌ™}ㆠ§~òÉÇ­ª­äŸÏ(Ü” ‹„å)Õ/¿ü\ç½÷´aYôIDATÞMÞ¸qC/D9n¼ñ†9ýû÷[Õ­[×ZµjùÉAø›Á¨[†›Íï«T­&šn|tÑ,ùTÑþ´óô¯Î®—Ž=ß$.5Gô›´T\uë*1rÑ&1~Ù ˜lÕCÜXá’ñb¡È|c#_Éuž‰˜Ãc‹_þC37lSzW×e<-®¾mH›r·¸tÌ|qþÕÓ´UI׋S.¤ÎVÇwG´ì ê7>RT¯U;"Y1ÊŽØÓ¸ñ¡ßuêÔñíóÎëù"éé3n¾ÿþû?÷ÜæîŸ}öi‹¿ÿþ«ZyòÌw^“ ‹À‹H”s8¶oÿ¬9fÒ¯_¿®wfæÒá3fÜ2mÔ¨‘‹ûô¹|M÷îg>×±c‡wŽ8¢É×GEE~#RrP݆š£:JÖ´­8ªMgѲÓ¢Ý)=4Gv±VNK"0Î~vÚ=ŠÒkø­úä=¬À*™+oY¡GU†ÎY+F,Ø(àÇ,}NŸcrãò×t@3mM¾þU±{8ÒÛ6íXš3ón±0ç'qÇ–_ôI°p¸‹_þS,yõ/ñþ¥yÿhÅ"óõu0dœ³‚ßHx‡4H‹<È‹2P"<(u ®ÛŸùN¯m˜¹ásU øâG¾Ðæ ÷äê}À<ô }Cä}…ÓGß{¾]Ö;«ÿ8Pwj¯«ô(A§”Þ«ˆZ×UÕöqxóözD Qƒj5 ;Ü ?«T©üo£F wsLëm§vê˽z]¸Q€×ùóç_¹òÏ<“}î;ï¼Ýñ»ï¾müï¿ÅU"‘G¦!xð“ XXXÄA0ôòí·»ûðÃÚŒ<ùä¦óW­z°?""3gÞ:å†&Î>|Xf¿~W¬¾à‚ó7qF—;®ÓÖ–-[|Š/VDI*W®´''Æ´jUPó#oöW~õó¿ñ›_øÃ/þq¥ÜøàÿùÃù϶®#°]?…cQ8ùžÈ÷xö¸ÝÞ>¸Ïò퓎pÔe?êžìö¸Þßë'\»ÿ¬Ûû÷vŸåû"*ò§má9šÜ€gû<Ûïˆgñ ÍÛÂó¶ð\àú<ÛùAG|ÞŸ·…Ó¶p*òë \_ä"?@TäÙcž;ì´;ícžëò\·-µÅ#?๮(\/¡¢( üÈ÷nª¡9HtÛgñ9Ïö:â»wrØý„}Ðù»wÒmDàù¡©´Å‘ïñÜ¡È÷º~GðÜaG°ûÝÃî™ÀñìñAwÀîvÛ§÷<ŸÄ3¸Mg€ÌóÒ6ží£=¨r³{^áÙ>·ß¸Aþ»wÌîŠÂq[è#‡wÚ=‘ïí}Ôn }Ä)ðGw‚ö´…¾ÈnÓ9a÷Nnš¼#ž‰üéÞ³£{ž×òÞ®#žÜÉÍŠ( Ü Ïç~È-‘GJ®Ã-¯Ü›GG27—nDÊ­:?Ô4_E›ïyîæé‰Â±À ! ‡hüÇvP>çq6 å<ùW@[8E`ÿ•Û?ù^G´…>rˆÀˆ|¯-ôE¾—Ï_š³×ú(aþHä{¹÷¸ý#Q8îˆä=ž;¼¡óÆðèut_¾Žã¼·"òƒnû´Óî ü»ßÞßö÷DAìïìïü©(<çØ>»ßãØ>Ï ò!ð§ÊsŽëí³m^èòÂËuXö@ŽE±ÏqÇ<Âó}ŽëåO±û½½Ž9®‡Ë¡ÁqÇ<ôì™ÈrŽïî³mŽïvº'݃^§Ý;áÎŽöÎDv p}Ž;fÙ#ží ìw÷ž±ì÷¹Îí‹ÿùŒ=þhOhs|·}ô~À ÷öžŸ~û°ÿm¶ûѳg½oîxÈ~ãßþõ_û‡¾òWþÍß}|ЕÆÿ»tg®ï¬×kaÇ«Õ BEѧý+îþú”¯¼ý¹Ÿû¥ßý½ß´ýƒož=ÿX–¦Šb³­À¶BËô Ý7tÿN©cê±ç\»mCÀ÷<¯ã¹]ÇÍ{º«-]]s,¸NŒ^´ce(:P ÛLóv¶™¢†i `›©mÆÈÏÚÂ×—eD®¢‰c%y˜zh›ñku®?.mr¿Ýó¼–'Õ5Ç2=ômb®¡;¦áÚ–o™žmùŽ8v`™ž¡;ºfkKÛµSˈPò8Vâ9™mÆú2Èy+¹Éc (ÇJòÔÒþ:šg™¾cGŽ L#0t×4ä4Û P¹N×ôÝk[¦ñ=Ïkyøòo<‹£Êa$ HPhGa–ôê“ø+ÔNàgQ“¢ ùW²VÞ=?r]àp@Îü,ð“(Ì’&ñ: W!HCEaGë8Z宋Â,Ù›ÍóR禮eW(ó& èûë(‚i £€Aðj‚0 a¬¢xÅë¤H£xE0Vq Ãú~«0„Qt½ÙóÒ0„hÕóR׋&) @Å«8Yƒ0 @FIËó½ø /ˆŒ(Ö²D[¥fYI ‚Ø~à„!HÒ$‹£Øó| ÄkàÕ@+‚v¸rA¦6„:„>„ë5t—a_ìÕ™Z}»yô½/p×ëuþi„0MÓOùwÜýõ©^>÷³¿ø¥?yôõ¾y|4øŸ¼0-ÏõBÄ‹¥9kòôêRžœ” i@tL´çžçžÙXQ¥ÑäòB ÇcEŒ¯P£|–¥©4šH£‰"Ϧ“¹:[¢xA±3N†—cD8V®P"¡ÕY4GfCy>hêÌ@s£álx9E„cE•å±,Me2VÔ›Y„êÈiŠ2yÉ3y“xf¯xdùG‘–ÒpŽÚ9¼†hÏÿ±s¦=rç×GJ^äÓ `äUâÄ‘ ÃŽG–xìòXtDQE‰\ÎÎôU÷ÕÇLŸÕ×LÏì’4)‰ËÙÙƒ¢h+’G‰Ì‹ #æÊA@/´ êžÂ¿~ýÔ¿j é9œ/vvwwîí=ÚÝ}xÿþÁÎÎárùñÞÞ£ùüÁ½{{ÛÛû‹ÅGËåÇ‹ÅG;;‡{{ö÷³X|tïÞÞ‡÷óÅÁîòÁb÷pûþr¾8Xì.vwæû÷wövæ{‹ÝƒÝåƒùâ`g¾¿3ߟ/»‹Ýƒùbg¾·Ü;Xî,v÷»{Ëý½åÞÁöryg¾ïàÁbÿ`1¿w°sçÁüöÁö­ùû›‡Ëùî|y{9ß9\î>4éÚ¹¿Üß?\îÌ~°ýٽϷç¶ç»ËÃí»÷ï_üîñÁ£¯>~øŸ‹ûy.9¹vö•7/Ïs­µnÛÉl¶•çúûÅóT'¯K=®«Y¥7u>§“¶Ùšv·ºÉÖd¼9nnnÞ¬›®i§“îætv«oê²­ên:»uóÖû“n«ª»ºéºé­­›w¦³Ûíx³ªfM³UU³,k³¼.«IÝLËjœuÝt“nkÒÍêf\VMÝŒÛq7ۼݴ³B·e5žt[³ÍÛãÉfY5Ýts:Ûšt³ª©uU–uUÖU¢kš4Ãj«)gÓ¬¼«óù¤œOË»mÖåéíñäÃÍ÷ïvw¦Å¬I'ÓfóÎÖ¯oMÛÍrx'–e¾_WèrZOÚÙ¯ãñÝ;ËÏç‡ÿ±³ûù'Ÿ|ùÉ¿ü[Ù6o¿w¥(õãÇ¿þúëßß@õÕWßñ>î8¾Óxî/þü/•ð÷÷?ûì_wæ{›·ïáq†…¸ŒÆ %X.™ôògCËu÷sy@Õˆ2C&1ú¯@ŽK]„)Æ#Ž1%X0&8ó…PPÎ¥àBĶ€çAJ¤”>c!D)‚@©BˆQJaL1ÆæHcŒq.…PbSAˆØ¶ëyRþD‡Pʃ R*€#D” 8—RJÆ„Ðô˜è÷û J)ç’RJB(HéSÊ!ÄJFàJ9ðˆ”>g¾”>£ BL‰ ‚ˆQÕï[J1çœRL‚)¥¤ä”R=¥BÀ´H)9çÇ<ßÊ#Ÿ©BIw ¡(‘–åH"D0âJJFS™”þS§¹. D ¡® cB0ÃÃãœ"„,«/¥D`Œ•J)Œ!BHÊ'NûCª„Pœù”rFçAvÌóÇyž­Ã9çC!!HJ©” ”zžg¦6BŒgL0ªÌcʨR* X ¶ë )}Î%ÆBL‘ÒWJQÊŸè01„cÊcL˜òlæcL! „0)B¥J¤çA)Âcžoã1ã1„cúT¤×ë{4K7! !˜‚ˆ1aú¢”bLB„JaÖŒ)!Dåûaï†í¹„³À¼eŽ gÔüø¼Îˆ G#È„OýŒ1& !|_öz=Ïs8çB0BB€1û&¾'<\ðà›yJ$F¼üíˬßCÐSRŒ¤ˆPJ¹RëAÇ®‡!"RÛñlˆ •bÇE¶ãy€@D bRPJC)c!F\„”ù }§·a,Çõ @ èÇv-˱¦¶{vàz€ L]ö6”0 1r<ÛñlˆaPyµ¯únB7¤ºj¥Wotö`ûX'×Þòíõ ¡‘ŵ¾zƒö­¹¾sU¬_ˆÞ}½Þ¸Òö¯Å×Þeï]Gàâ¥ër²ÝvÛ|°÷ég¿ÛÝSßxãWæ·Ñæ:ÌfÚ¼kxßÛxîäË'¦“n±X~xïþ(Î1Wo]½†¸‚Lz”º„8xÐõ  ‘ƒ}À  Tõ]r¥g_µ¼PD ÛÞÀ•”HA„žâY:ޱ¥Üue9†Íl ŽyŽæaGé0F<Ï 6\צsNŸ8cL!Ä® lÛuÏìÀÌdǘš›ºvmýÆ „cÌó¼Á`àº.¥”s޲, cŒ1†º®kÛ¶ã8¦ñ:„1áyp0°]PÊ9—OÌóà¹|ùJ¿o™– ËrÇØRJM§f‘7u˲LïÏ|A`Œƒ ¸üÖÕþ†K‰â,µÀ±F‚sì:fêÖÀC1FŒl{`ÛŒa¨Ë—/õû=ã4\Ëê;Ž…1üþñó¨ Af[ж F*Pùë^ônØo]ºzfuíĉSgΜ»páŸÏ¯½º²zöÔéÕ³çÖÖ^yíÜùWVÏœ[Y=»zæœ)§WΜ<µròÔÊê™sçοvåëB‰±ïz£g¿qñòꙵ'OŸ8yúÔÊé³çϽòÚÚÚ«çWΜ|éÄ/Ïž[;½rîå+'N®®¬ž=söüÊêÙ“§VVάž[;¿öêù×.¼zéò›½þ €Xî4EòÂó?2hsüâ‹/Ì®ú8¾ÏñÜ Ïÿ¨©Ë›7o6í¤¬ÆL…þ(e~4* 92%Ãaå£xä8ظú¶w­÷Î…K­3ˆQêçÈHÅZø#)BŸ©@p_1?`2 Tá¬H“$aŒ…aH)ÕZcŒ)¥¾ï3Ƥ”išfY"Œ±ïKJq£Q$%ã¡R"ÏÓ,KpÓ4–’GQ`g&IF)¢‘”~§YV Dò\€ò\SÊ}?ÎeaY–ù¾äœ*%‚@ù¾ Çò,‚@QŠ•Ãa†¾RÂ÷%¥4MSÎyžçZk„RJ)•e„1–$ Ƹª*SCÿ™ÅtEÁp‡aA ”q<´íAÃÐ'¥il®­£‚ ˆ¢h8‡Ãè‰PQ€ÑhÇ1!Ä4 Ã0އQpN}_ÆñPJN)N’ÑQ<&'RrS|_šˆ¢À$Ä´7õÑ(RJù¾oú Ã0ß÷M~Ì@Çq†!ç<‚8Ž«J»®]U: }ÆH–%„ ²,Žâ ˆèˆPGÄQíÍmšÞÃÐÃÑ(2–{f9ZGJ)Ÿ¦}8š±ðˆ(ŠL³8Ž‡Ã¡¹’ç9cDJ†¾É¹©äyúÿŃ1‡„c„PEæâ3ã¨ü˜Ñ¢€1bfïK!˜ÖZ‘¦©ÖÚ²¬<Ï}ßÏó!4Ì4O’ÄX"ÏsÏs‚@åyjÌÉ9‚™ùž$£0ô9§œS3IÓ4–Rš\¼ ¤”æïûÆaÆq<Â0LÓTJ. Cßq¬²,¤äf‚QžY–yž—ç9çœsnNµÖì¿Ù¹²Þ8®ìì_“—¼ÈsA`  ȃób Ï83ãŒbÙ¦”xY–,ÛEI´¬…´lqm¶ºº¶»oUÕÝ$%RÜÄÅÚ,Kâ.îl²+G]n“]´;/–.. ]Åâ½§Î=çÞï|÷T]»ÆÃçr9ÆXWW—Öº§§'­„PbÛµ6ÓBžãäk½lÏvž6T;'¤Ý¿_yö«ŸtyêWBÆ!ây6 óy'Ÿw|ß÷}wwM±›`G>ó\â:ØÎûyËË[žïÑ:ÕǶm×mŠ„±Ú˯+ÌÏVÚ¶kY–eÙŽ|¹Éö¸ímY­JçÎ^ú¬ùÒ¡†wsל¯¿¾s÷îýÉÉÉ‘±ÑáÑ‘¡‘ááÑ‘‘±ÑÑ[c#c£C#Ãã“·&ƇF†‡F†§n}çÞÝR_oO6÷Ç#Ço08tóν»SãƒCC#ãCc·&ÆnMŽMŽŒNŒÝš˜˜üz|bjtl|àæàÝû÷&¦Æ•‘o5¼ÙØtÊñlÊeúJsËÙß(||âög'·ü/6Â/g çgn¶>hYn[(^Zˆ.ÎEõ~¾0Ú6£óqðå4m¯{[¼çAÛåҩƃóËWþê—mgZ;®dr–ǹŒ´˜êëU¸áà•Je{{Þ2úÅ—:žóòÒ?¼^ˆ‚¾¾>e´0å"Ä•(ÕšJA¦Ä“ÄÓÄáNδvgo˜ò0Zzs„y< Ü*)f‚`Έ 3—¤¤9JƘÖÚu]Ji¡PàœÃ’1fŒcŠÅ¢”@•ﻄ „<)¹ëÚ®ksNmÛ2Fa즂„Ó(*RÊ aÆ„Žã„P¥RŸëúÎ¥ãxZaRŠaÞ¡3FA”bè‚$%—’>’÷÷÷bŒ1žçI)9ç¶mk­…ÆÀ…Zk˲¢(ò}_)Eª[@§ŒJ1Ì’Rò|>š1ØÝ­ IDAT褔HoaŒ)¥Œ1Æ¥cŒ Ãв,¥”1†:çœSе–œSx:Ætš&çN¡2F8§B°B!”’'Ï Š1’pÎ…Ð#HJCÃcŒµ–Ž“7FV†Ç‚¥ÉCR M),¥¤ÝŸw°x^PBÝšÞÎS`²N8çiòK)«Dò€ ÄûøøSÉŽæ8\ô< &­¤é§v¤#B0ÆƾëºÝÝÝ¡(Š<Ï+•J®ëÂÐAÀ9G)¥c`´½½E¥„ëÚ–u cߥ”@ÈK¢8Œ})9xD>ŸK4±4@Ì!Ƙþ ÖH¡"íûnàiþ•¦‡b±èº®R 0Ïó …‚ïûAh­BZkÇqàδvjm»Öæ}ß}bìƒÀÅôv¾óýÚ9a¡Ü—<ûÕOº<õ+ç’sɘ€y!‚ñ}?™yvÔû¯¶C¥œ`A>ó}L‰¤”ï>2Æê6•x=,+I0ÿ¼É3!Œ1¡d(Eˆ‘²óÄsù™¦ó§›/^hÙØ(C~p%Ž+q¼WàGrºUÙ†ßåí-ø±¼ºrçîý“§Îž8Ñôé§çl›^më>yêì馿Á‘ͧ-¬®¯-.=)ooVâíåÕ•JoWâï5^‰·+ñfy{£¼¹º¾vÿÁ½ —>?ñÉGÝ¡€ëæwvxk®µ%¶:âb.žÈÇÓN¼Œ·ïµÅ÷ºâ9~ßëŒwÄÓÙx^ų¥øq)&qhÅNçBkkç¿¿öþßÿËÕ£Ÿ:íY×v¤äŠ{·zu‰º ¼€Ñ/ÊŽòRÃÛo ¦P)g\an"Äb\j¥$çÈ‘žUÀV?É÷z9yõ«A÷SRAc:´0ó¥!2`\Ãê/3éQxTj­ !Ƙa¥”^ ¡HáœcŒóù|OO·RBkY(„ah”°ÐrNƒ@KÉÈäœVé1„*‹€ ÂÞÏ ¶m !\×åœA¨ã§“ŽLk Í2F`ÖZЄY 7,É !ð.aB¤”c,Ë,B)…éow­…¤À¬'!äi-6朘Ho'¶æóy)¥R N6ù¾Z)±½,K“'éhÇŒ ‘ À‘b1‚¡‘’×B´¨QJ&BÈ’RÂè3F”€›…` Øžòüÿ–Ú< ¥¦µó‚‡+ÉÕ-µ±”2‚0 10B0)9H²c\~¤<àŒžçÁ c­5¥{´“ªŸ$ÕZ&r^¿~P²Âó<¥”ëºÆ¥T†œsˆ¬!Žã`Œ€ƒ$î‘•1* 1 À%cDkY~ÔÚ˜Äêð8ðt£bìƒÅ&aê~õiŒI” š¤”ÂŒÜ÷}ιmÛAÀtQ·ÔÚv­Íäj„°.¦·óï×Î i÷ïWžýêg¿òÔzGm¬›ÞoýZmGr΃ JPJáJ½cZ;ßy}MŒº‡ßý<å©zç\  œ‚%%ê‹Ö¶O>>…½‹‹KqW*•ryc«²½¹UÞ(onn•ËÛ[[•íòöÖFysmc=Ñë›p\X\2 £¾•ÕÍ•ÕÍòV¼]‰çž,¯¬Áÿ®o”7ËñêÚÖÚúöv%.oÅÕ;+•8^^YÛ®ÄåMG}ØÙÝU0Ìù¯,½wråÌåJÓ™¸¥iË?¿Ñ×úmxváæåYÑX6çÖýâÒ¹e~ä!ý`.l™ÑWâ{9>¶Z¶ZÏ­45NùðÊË¿:÷ë߉L»ymÃù!Cû™èŒ~Qv•—z3*h(D|®O™ ‹˜+„©ä"à<@^Ù×q¾7ßeÚ[/¼ýftµ-ÌdnH‰ÛGÄò01‘Ï&#NeSæê"êpþ”#±m;Š"@·„­uRJ Ì‚ $*%/Bßw!#öýa«ö=aº7F ÁJ¥B-j„ícc õ0 }ß·, ç\)K”R–Ò„íƒ kàÞ„`Qh-ò€IüáyHnÛ6P/° MP €`ç‘@аLæ  Í[xœSàÂÓ(‡j;ßã\Kö²9ç J©”œâû.çœäº.°JuålTíëéÆ¢ï»€f\ךÐum@6 ŸZ^ ®ZŠ¢(, Cˆy …!äyŽRJ&„H×ÏOÆ:§ÝŸôUwÔv÷n§Nï{Ü ?ö2RªHð+ý¤K¸oy|ßÅ °K°ìKÓ”¬=yæëÛ߬­oo–ãÙ¹¥Íxeµ\‰ã¥åù…å¥åõ­íïÁèå•Õµr%ŽŸ,­Ý»ÿ`eu}}£<>1uô؇¶ãÜúâDóá_¼¸q²!îú|óÌ‰Ç óGŽ9¯¼úÁ/þ1èî$~^‡œü aCF>x0ŽãäÓÑ£áø¢<·å¥†Co„‘RF"â ø” ùˆ)e.#Jú($no®_:ùè½Ïÿó·´©‰^º0H‰tÁ¸OU!á†2M‰d„ Æ9§Œû„ùA Á/cÅbÑqœ¾¾> ž9瀞ƒ ˆ¢HaÛ6[®kKÉK¥BQ8N°5$?g“Ëe)Å¥R h)%t’s^( u$ Ãb±˜`^¥‡kwÌ)ÅahJ¥$@JIÐК%ßh­MaŒ)¥4PdRB^Gu²â;Ž„°Ý×…$¹+ZK¨{lÞÕE–€!(¥ ß”|EUª 3ÆÂ°@)u]_J¹‡<|% Š’’†šP©|«a:K)!v$cab 2Ê×€|êʳûa÷."¥¤Ý_]ɾë‘F®í>þP;OKífwÚbÅ9㇠øZÆ|cŒᜠäîO#D­`±B乌®«¥LÂrQ 'PÃÜò¿ÃjNÁƒ€±¶m°µëú!¥ |P/—Ë1&ÇqOJÙ×w½T*I©Á»Áûdµ$ätí@ß8çah´–)!%‡Ü ¥Ä~a" fèÀ: ÿŒ±B¡¼>äɤµS£îYw Øê•( lý¿–úòìW?û•'1ÈÚ‹Œ±´~aFÚ]ÓÛ©_ªóÁN?«Þ1 =‡òTç|J)çLKHJFa_¦Û:øFÃÔäݹ¹…G}ûí7ÓÓfçç¦ggÏLOÏÎÌÌÍÎÎÏÍÌÍ&§g¦¿}ôðáãG3s³ O—úúÿô­·ÿçúa+ï¿÷þ‡““ßÌϯ--•oß~8=½ÔÛ;tóæDÿÈÀÀ­±±;ccwÇn O O Ž ONÝ{ðíÌýo--¯ÏÍ/MNÝ=úáqLÄpéfÐî~üOÿ6öFãòÛgãÃÍOÞ9eýú?üí_¼ù¯ÿpí챩¼<{¡yüø;Ó_5ÞÏ4ßè<ðŸÿîå?û“×ÿüOGÿû@|þl|ôD|ì”~å7ïÿå_‡] ;ÔPºýZ‡á¡7¿£áýÂ0ú9//½Ýð_a$u ÃÂhP®"ϧFEÑÀuû<·ÐÓá6Ÿl;rèJÃø'Ç­#ïùgN÷å³òŠa\æ}BEDi@‰âT &…\`Â|Î9¸a¶··777—J%Æ,„Àßd³Y×u‰^»v Øâ04¶m]¹ÒбßÛ[„lÚ¾¾R&Ó•Íf\×îéé¶m«µµ•R ظXh¹½½½££Þ\Îd2ÝÝÝ]]]a&ÜÛ - `½­í«ÎÎvÈÈ„ `ØäbŒAz4¤?ˆ0P6› ‚€UsÑö¨Œ ÆDr*„úY)ÑÝÝÙÕÕaŒ‚쎽7Uk1kÂÆAŽ&È™Édr¹œm»SHF4&$„yÒ:ØCž[ºIR,´¶me2]==Ý–uÍqò;vf”†!¼K—ü©ªIaÛ®*¢$K2É•Ü-Ouñ.KZ§ìɪֽçÐÔ`Ç=?Ðï³È;$ËáyÞÕ«W;;; !¥LD”r„ì®ÖêçGÊ#ª96p¤$ÁKûÒ¥°G, R…1ÍçîîžžžØŽ€Lªb±ØÒÒbYð!ËlWW|õ6+‚ ‚ˆ1ŸzÈfsð†(Þyhkë¸xñr&“¢" VÍ~æ5Ò 0°Â¢ú2!¤­í«$‹0ÇžûàõK-¹Qz²{ „€=1HîJ°~šîîb{ÀÐðzFÍþí<ÓŽËòìK?û•'ÝVë³­iò<‹Í§Ùó³ø>cì¹’§Ú Á™<",od{ìÇÞøðჾù_ö®,¨ªc]ç!/yÈ©[•‡T¥R&OÉÁ!1!ˆ "‰ñÄ«â1Nˆ£ˆ ‚Ȩ(‚¢ˆ²E… ÈžØk^½æyíöN<ÉIÕ©Üs8 ®a›{n¥êÞª¤«k×bÓ»×ßÃßÿ×Ýßß~ýõƒÐH8 úƒ@( ‡B#á`8ä£#ða8à†C#wGïÞ¿w÷Þ¯ïvÙñÊܼB^P­6×Ñc'ûûƒ÷î=zôèo~ÿÓHOÏZ¹rýŠë–/ÿÏE‹V$%½Ÿ˜ø^Bª„„‰‰«W-Y²2>aYOïàÈèÃ…Â÷|=ýå'Oc8m ªóâg%ËÖ`){¾?xöÇìJV©^qÆwã+GÓåÒ=×dìê³\鯬6 ‹ïÔž½²k[þæ畆±Û懇O•û÷gÝËØû}N‘²sOÝ)JG;I!v ±‘¨@3:#åûŒþý°Žßxxª¨8W’Y^d0%àD1Jœ¡ì.ÁæP¬VärCóÑÃ_Ë–*£±¾9'SøüRË®`.8AÙºQŒä1ŒÅPšÀhŠ€L, #à¦, íî¶m©6l´Ù‚ ñ¼xíZsffVRRrLLìÆ›Î;OÓ´®ëÇ577—””¬Y³&>>¾¤¤„¦iQ)Šr:|ðÁܹs£££çΕ‘‘a³Ù0 ³ÛíÑBƒ·nݺèèèèèè·ÞzkÞ¼y ,زe‹Ãá€ËW8>NÉ€ëpv»½µµõÀëׯ߿[[›¦iš¦±, áŠâv»Se— ­­­³Zí“àEñŽŽ[55ã8$˜i#E²Ó|I’$!?ð£G–––;×ÓŠ?s!ˆ¢vw#' (^Yy:?¿à\u­Ýæâ9C)W7Ž¡†R»uÓÈCLqÛ¦x§õöö’$Y]]½uëÖäää 6444L‚*ˆ‰'Ñl}ýÅêêšÎÎ.XŠÐt´[Ï×Ô‘£©în'FàÇè'ÖÏ/›™ÇàÝ´1Rúi[Š"Y£§‰OÈgºFy‚ü'ÀRX­ö“'+7mÚ¼aÃÆ½{÷µ\oëvbð:!! o¯øU䀅Ӓ¤išéîF*+O<˜qâ¡~c4†Rˆ‹€ÊÖ–åå±±q«V½WTTÒÖv“çExaÄÆ›,xû­·&$$ÆÆÆÍ›÷fJÊŸ¯‚D‘lg‡íÄñÊ?­Y7ÞÛï®ZsªòìäUJ;röÌù­[v¼¿zí¡ƒùmm7't²³Á¢Áâ@}DQHý¤q?uêÔþýûI’„*Ydxd.x¤z€õër¡ðígÎTççtw#’¤¸\heåéÂÂâIeŒÏTE¦¦ª9”‚À¥z||åû¦åOîf¿#ÊóïÖOdy"cÓ¿.¸‹¼š;½'C8NŒûÒý,Â^ýxo‡­¿y&ÉEĤlÎ8£©›Õu®ºÖëí …BápØï ‡GG‚¡‘@0 „£ᑻ¡ðh ½{ÿ±xïþÿ G‹ŠíÙ›}«ËÙÕ…TUÕöõC¡oÂáoC¡ïFFþ²wOþ†õi;R3w’»#5sÇŽ½ééY;wîÛ¾ý“´´=û6oÎX½z½(š¡ðýaÿH x×í髨<‹áÀ#‹†µµm_†¼ó£±ò³?dåû>Þ3t¥aìÛÐØ_ïµ_mؽ4 ),ÓŠÊÂçγ…¹+@ë—?øƒc÷FÇÜÆàñ«8=V^ó¼ãòöOÚ3öh·:´¨e8V’€T_‘Óï0ú÷0ž*9Zªêš¤È8Ipv# +’p8qÅt‡Í^wöê‘LüÂÑG ƒ·T^ص}°»kD š¦1Ф8H$(š§H–&Ch’†.Ml{{çŒ/¿øâŒ«W›¼ÞA>ûìêîÝ{âã—¼öÚÜÄĤÇܸÑβlKKKAAÁ®]»V¯^Ÿ}ýúu¸åmµZ£¢¢ž~úé×_}ñâÅ111+W®$IÒ4Mè2©Š]]]/¿üò3Ï<3sæÌ+V$&&Ι3gíÚµ,ËÂ=e‚ Œ¦iÚáp´´´\¾|9555&&&%%¥¦¦.£R…¢8,,IÒ==}V«=?¿àÒ¥ËÐfÓ4ãr¡MM_ÛlM3Æq û$ >MEà€$Ç·¤UUEdçÎ;vì@QTQ”HÃå$‚œŠ'­AP.*вÕjÿôÓ½+W¾{èPžÝîô¸{qœ´v9I’f€7ìž >ÁK†U!ÈW_}UZZºvíÚE‹­Y³&//¯¾¾ž¦!øþ—<hš±Û‡Ù³'óÊ•Ïa¸lyñbㇺ˜a¸í6E£†‹$ϯ—#¥SÞ8n®~Ö‚ðóò™Ò"Hnúd4Í †¢xggWAAѲeË—,Yš––^Ss¡­í&”C)’¤)’Åqò×’‡ey»ÝÉq‚(Ê’¤Üºeݹ3ãwÁþóó©~H‚·%‰Ï‹õuiiéÉÉ+bcã¶oßQQqª³³K’†á–,YúÎ;‹bbbãââÿøÇ™øÃÄÄÄ64\¸ƒaÄõë­y¹IIɳgÍMHHÜŸu°¡áÏÉ6›£¾®ñàÁܵlX¶lù¶­i%%Gy^dYjœð¼(Š2,Ž H°4Í0 “mÙ²eÙ²e†‰¢ˆã¸ p­:’~Eê?°]²¬2 Çó"Šâ}´+!!߯·£ãÖ‡nyï½÷N—,«Oì·ø„"ÿ 5BEƒWHGóX’|RžÚ²}ù?‘äùwë'²<‘Æ1ò±7>9DÎç¿áÂ_d¤Ô¦d¯Ñ€Þq‹r÷½Ge›½“8M¡vÇ-I‘iÀOQ»»»Ÿþù¨¨¨¶¶¶ááaAjkk«ªª žÅbIJJÊÍÍ¥i:33sáÂ…±±±_|ñ…ÅbÉÈȰX,+;Ι3gΚ5ëæÍ›“äNˆŒá±®ë,ËZ­Ö3fÌ™3§¶¶ÖãñLÚ'èØDŠ¢š¦¡(jšf{{{JJÊòåËËËËÛÚÚ —.]j±XP5 ƒã8UUþ<99¹¸¸r²Y–õx±k,Ë2L¯ëº ðh-Y–Q…eÙ¾¾>’$= (ŠŸOQ4Y–'™¯F(Š¢iü„'‹©ªÊ0 \Çq–ZUUÓ4)Š‚¥&BEžç à IRÅÁÁAbâðHZÕu]Ó4·Ûa˜®ë<ÏCö¼¢(,Ësçñøt]çyQUUŽÜn7M3ªª²,oµZ}¾^˜?IÓ´öövI’ ÃeY’$XpY–’$¹Ýn§ÓY^^ž““ãr¹dY†Ä]×1 Øl¶´´´¸¸¸mÛ¶577Ó4]]]]]]ÝÚÚ*Ë*EQ¶Ž(ЦéŒm¯×K’´ªª¢(Þéóù`!¢išÇãü(¯×«(ŠÕj5 C’$Ó4Eáy¾··r-ü~cccTTÔœ9sH’t»Ýn·>$ ëZG’´®ë†áv¹\, ©Ï@EX‚ $Ia¦¹ùzee%‚ ×®][¸páÛo¿½wï^ Ã|>Ü/‚ ¨¦¦¦Y³fíÛ·OÇÃóüºuëV¯^óå—_ÚlŽsçÎÅÅŧ¦¦R¸yóæ¦M›óóóÏ;o·ÛOž¬\¸páÅ‹eYv»ÝÐ#Ö*Ã0­­­¾ SUÕ¾¾>‡ÃñÆoÌž=ò¤á‰{]×u]‡ÚdµZEq»Ýš¦É²lš&L yÏ’$)ŠBQÇqð¥N§*þܹsgÏžÝÝÝíñxZ[[_}õÕÙ³g³,kš&Ïó°9 Ã`Æ0 âá4ž A`ÿñz½pÈÁjµš¦É²lqqqjj* Ã"↺FNx•@–9ôЀ<{&B &9 x…‰áÀÃäÚ0,8!üZù@fù8/€Içjâ0™ÉÿÂnCL!ŒM%’ýoóì5Ÿ ‚à8^»MQР€ß <¤jø' ËËHvrâø©þáÑ‘þápÀ?2<ðù|wî ¡¡ÁÁÁA¿ß ý~ÿÐÐÐ;w†‡‡C¡P0ò ùC¡Ÿ÷veÅ™#ùÅH‚=SuÞçípÿ/½=C_?ø±¯'°uó®œýEgN7Î-;yâüñcgNWÖ9qäpYÕ©úùeÙY…±1Ig†Cï †þÑþÛC§8NtbëåòUñÊÇßeåŒeç•”}üX_yÉÑÈØðà7,Wúçm™Éï—nÙYšþÑMýývïØHð'Êe–ÝÎÍú¶àУœœð¾,"=½då ©û"ñŠV9Mg”¢Ã…SñÓ¿ŸÔñO婿…~¯×ÏQeI’$YçyÀ]î×€é×}®F˵ÂÝÔùFóѳûR:*º>>==Ýb±@ü¡ª*MÓ?9Ž×^{mÓ¦M‚kšÆqÜÆçÏŸÏ0Œ$I^¯š^§àIÐ÷ôôÀh]‚€•ò¬,Ë7nÜhiiq¹\£££†Y,– ÌŸ?ßáp¸Ýndžñù|ðö ·Û=88¨( ÇqÐðO²[[[ûûû ‚hii±Ùl%À3t]w¹\‡C–e °²÷ÝaQfYÞîlw;Ó3ãL÷N·¶Ú-I²” *Шжˆ ³ @1·˜0µ¨mŒ HÎVUTÎ9ç* ª((ª(B‘y¿?Î6;3½ßî~½ß_ž‡‡§(î{Þν÷wÂ=‹e2™2™L$566Âõ €¡í‰„Çã)•ÊúúúE‹yxxP(‰DRZZzñâÅ“'O2Œ_ò™L&ǃ± ‰¡¡¡ÎÎÎ d2™Íf“Éä#GŽ,X° $$FV«««';@PK§ÓVWW"ãœ@ àp8€‰!÷p]]Ä©K$‰DXÚyù IDAT˜ŒËåB„Ïû÷ïÉd²@ Ðh4X,V§ÓÁ MMM€ÂÅb1€¹~A"d2"_!°§±±Q.—+Š’’’††>Ÿ/‰( ›ÍÖjµµµµ ŽÌNÅ`0JKK===}}}Ÿ?ê ‡Ùàr¹W®\™?þ¼yó²³³©TªZ­Æáp|>Ü€ÉdÖÖÖBVrÆf³A!…›Ínhh;+ dP0 &näa0T*U.—‡( ¿@ ÈËËóôôtwwm–N§ƒÒ‚B¡$ ("‘ˆÃá@.dÐ@ä ð‰Á`€!UR©‡Ã…„„|ùå—«W¯®ªªÅL òûñÇ—,YòìÙ3>Ÿ/‹Q(ÔòåË===Z[[ß¼y3{öìÐÐP,›ýÃ?qâD\\Ü’%Kbbb233ñÍ?%6ñøÀÓÁÃŽÔtäèiÛ‰“òÓ'Å?ݦ“1v¢ ‹®Ï’±xH·}\ÀçÞ»E¹xJzé”4=Ù|ôrñ|ÇñãÄÔÔœµkxM L6Äf4“¨bš@Bæ^øñ܇øé#ŒþH‚L:s&‡Fã‰ÅR*™ÂaÃÂ' …Ç3oMÿéP╲Ç‚¬ñN~éù ÏŽ¥¿¸|UT("QË^TîOÎJϸ‘{óMæÉ;v|ùê _Àf2È|S,’ÈT ƒÍåòao&‘HNNNoß¾miiìK&“Á¶¨qýúõaïÞ½nnnîîî§N‚MŽD"Q©T‘H„ÅbçÍ›çããóòåKØØºEà€•ÀÀ@ww÷;v{ölxxøçŸþç?ÿùèÑ£%%%|>ŸH$æääÄÄÄlݺÌö$¤¾¾>""béÒ¥§NzöìÙ•+W0Ì©S§|}}¿ûî»ýû÷ØJHHHHHØ¿ÿÞ½{#"":ôöí[…òòò‹/îØ±cçÎYYYwîÜ)--+,ŸÏçóù þþþõõõÅÅÅÑÑÑÞÞÞ~~~(W"‘ˆÅbÈÏ@¥Ry<^YY™¯¯ïäÉ“ƒƒƒ«ªªh4ZeeåêÕ«¿øâ —úúz€&?ýôSFFFlllBBBrrryy¹H$‚ŽŽNOOOHHX·nݺuë>\SS#—Ë›››oß¾½oß¾~ø!%%åÀÅÅÅ€«H$Ò™3göìÙ³{÷ŒW¯^=þœF£Q©Ô›7o®^½zíÚµqqq/^7ú† ®^½zõêÕ´´´„„„K—.½~ýúÁƒÇOMMݶm[UUØ­‰Dâ‰'öìÙwþüyh²¡¡aÓ¦M§NÊÈÈØ°aCttô¥K— 7bzzz@@ÀŒ3&Ož|úôi  ù|>—Ë…¥¨¨¨)S¦„††Ö××K$a0®ƒ“áÌ™3»wï^³fMttôõë×Éd2—ËÅ`0 «W¯NKKËÌÌŒ?}ú4 mðâÅ‹ÔÔÔíÛ·8pàÌ™3oß¾ …áèÑ£Û·o‰‰Ù»woFFF“Ëå6›­¤¤Ä××wêÔ© ê<}úôÇܼyóÚµk/^¼ˆÇã'<?þøãÅ‹A í”ؘÁ*/•J9ÎÛ·oýýý?ÿüóØØXð8+ ±X ´fÍš;vP(*±Xœ••PRRÂ`0?~¼xñâK—.a0˜õë×Ï™3§¬¬Ì`0¨Õ꺺º ¬\¹…BF:^YY™““‡Œq8\]]]QQÑÑ£Gß¾}K£Ñ”JeCCƒ««ëÌ™3i4šP(ljj:sæÌ®]»"##/\¸º™X,f±Xׯ_?uêTZZÚîÝ»O:Ù9 Â7<¸jÕª;v¤¥¥åçç³ÙlFC ÜÜÜœœœp8œÉdzùòå´iÓ¦OŸ~êÔ©øøøÍ›7Ÿ9sæýû÷B¡´Ç¸¸¸˜˜˜ððð[·n‘Éd±X\SSóèÑ£C‡¥¦¦¦¦¦¾yóF.—c0˜äää©S§Nš4éСCÍÍÍà"’J¥2~QZþ»0úCÀšxþÅ8Îÿ6ø×‡žƒ'š6ñ–‰’ß|äóÛòùØ?R©œL¦Þ¸~[$’(Z‰D¦P¨d2…L&S*å …L, …|‰D$“ɤR©R©”Éd€¤år¹T*Ídp/_Êùñô92‰N!3nÞÈc1yj•^!רU-¶pmôF&ƒg4t©Umu«B®Ñë jU‹€/U*ZZôfS¸*jƒÎoméIµr™F(^¹’M¡P´b‘¸¾æ§hÓµû´'¤ZÖdf=Rê8³ðé€PŒtt#Æ®aƒ¥[ßÖ-U^¼(KK­MMäO$noIÚ3rìxç¡Ãì}ï­]§jBK¹l.—˦±t ‰‚Äû£?Ò?Ò¤Ó§ÎR©t±ˆG£\Ãe0DÍxñ’ðKÓSn½¼‡7é[ðæ«U=¾± °©¤4?/?-ùÇe»†ï:qûaAãÎ=GÒÒŽWWWˤB•RÊápÞ£°B‘ŒËå3 >ŸO¡P¾ýö[—7oÞ´··óx<¡P¨T*ñxü… >}ú´¹¹¹°°ðÀáááxýú5•JÕëõb±øýû÷AAAS¦LY³fMLLLzz:ƒÁ=±”Ðét‰tùòå[·nÁÝ.·oßÞ¸qãéÓ§Á´p8p¯‡„„Lš4),, ÇËd2 åååõûßÿ~õêÕ`—âóù`çf³Ùååå6lû"‰DúùçŸwìØ1wîÜÒÒR¹\Îf³¯]»¶cÇŽ;wî¼xñâ‡~X²dɽ{÷ˆD"•Juss›1cÆÒ¥K|H"‘rrrBBB<ˆÁ`êêê233CCC=z”ŸŸ¿bÅŠýû÷¿~ý²‘øùùÓh´‡FFF¦§§+•J¥RYXX8{öl—çÏŸƒeD"±Ùl¹\ŽÅb}}}?ùä“… NÑžB¼J^^Þš5kîß¿_UUUXXþÓO?I¥R ãïï?iÒ¤ààà[·n„„„ìÚµëñãÇ #111((èéÓ§'Nœ8vìØ“'Oh4Z^^ÞÚµkÏ;WQQqýúõÕ«W?zôˆÃáèõú²²2WWW°×>}útÑ¢EK–,¹uëÖÍ›7׬YsïÞ=ƒ!•J Â¥K—²³³ñx<ŸÏ …ãÄøÅ, »5ä±IMM ºwïž\.'“É"‘´ÐÊÊÊÐÐÐË—/C€@ P(K—.½~ýúíÛ·OŸ>½~ýz2™ÜÔÔ8uêÔÒÒR“É$“ɰX¬——×ܹsß½{vå/^¼|ùòõë×GŽ9vìØ£G„BaAAÁõë×›››u:]cc£‡‡ÇÌ™3 BMMÍ•+W6oÞ|ë֭ׯ_ÿý÷çÏŸûö-‡Ã¹ÿ~llìÝ»wKKKŸ?~óæÍââb4-—ËÁ®\TT”””tþüùææfð3Ô××;çãã3gΜªªª7n„‡‡»¸¸dff„;wîÄÆÆž?¾´´ôíÛ·Ë—/ôèÎÉÉYºt©··÷‹/.]ºT[[ ‚ñöí[ooo''§²²2¡P(‹!àäC“!ûúô°ÿSbýGœýwŸ9΢Œûkô[ñù5&ÐüïøLˆÙßqøÈç·åóáã,‡Åâ0™l&“)•ÊÉdòõë7y<žD" …R©\,K¥R™L"“I¤R±D"’HDàä‘Ëå°wÀo(¯VkYLޕ˹Yg.0èsëæ&ƒ«RiØ,¾F£ãr„«W­%¨R‰’ÉàÊd ®E&“ …b:ÉfñÛZÍuë¦ñ<]¥Ô 2©D)ŠsssY,–I­R7aïÅo£¥“¥f(w&k“ðl>’V|$­ðı§§äTÕ!íÝÈÀ˜Y"+¸|åAJþÂ9öÅ,Ú¡}Ò”DÝî]mñ; û)éOÖn2`ñO*‰8‚6ŽBIùhþHÿ„&Íú‘ˤiUb!ª’òeR¡@ `0µ›·žZž’œ´ýú¶ޤo>‘´r׉mûÓã“¶¯Ý¶$(:,$.(dgܶ+Uõc'’_X{ðàÉ—/_ëuš½V"‘1Y|¹B+‘ÈÄb±J¥b±Xß}÷««kii©ÕjÕh4`w$4kÖ¬¤¤$ ÕÚÚ ‘ÓYYYþþþ~~~=’J¥z½F»¹¹Íž=»¬¬ å€AK&“Á\… Ì~2™L§Ó©Õêššð3™L‰D" e2ü&‰aaaŸ~úiXXØ~ššš|}}¿øâ‹5kÖ Ñh©T*‹A«æóù%%%aaaYYY§Nb2™d2ùرc×®] }ñâEKK ‹ÅZµjU@@\á0eÊ”øøx.—Ëf³g̘ñûßÿ>00P$I¥Rooo(i6›¹\®¯¯ïŒ3ššš:::°X¬““Ó×_ A®{öìqrròõõåp8t:}Þ¼ysæÌÙ³gyÏž={ÆŒ Ð. OOÏ)S¦DDD€õkÆŒ³gÏÎÏÏÆËËËßßÿÍ›76løúë¯=<< ¼ÁÉÉÉÕÕuçÎD"z’ËåªT*@pòäIðA‡††Nš4ÉÓÓ³±±‘D"q8Pf`݉Dùùù .ôöö¾páBff&‹ÅJOO/,,<{ölppð£Gø|¾\./((8yòdfffttôgŸ}æááQUUÕ××ÇãñÜÜÜœ±X¬ÉdÂ`0^^^AAA$I,—••ýôÓO‰‰‰K—.ýâ‹/"##?~íââ2{öl"‘( ]]]]\\’’’êêê~øá''§ÊÊJƒñêÕ«Y³fmÚ´ F;;;Ož<944b ¡O0LGG‡D"™?~HHÈ›7oNœ8áåå\^^^[[èïž¾ooo77·ÜÜ\‹ÅÂf³}||üüü(JGGGUU•“““ŸŸ7 œb±X©TR©ÔÐÐÐO?ý4 €D"×d2 …ÂÖÖV@°}ûvWWW e2™*++çÎ`4<<|òäɉ«P(¾ùæ›Ï?ÿ|Íš5»wï )**‚Ý”Æ7Î;·¨¨Èn·óùüÐÐÐØØX‰¤T*ß¿ÿí·ßVVVnÚ´iÊ”)aaapè··÷Ž;P(”Á`P©T` Z­–Ãáh4>Ÿ/¤R©H$’Éd‰„B¡¼|ùrþüù±±±'Nœ R©0} …R©d³ÙçÎ +++ãñx2™L&“±X¬§OŸÂôÁ`0pFâÁƒ8nÑ¢Eß~ûmyyykk«\.§R©...¾¾¾ f³™ÏçïÚµ F[, õóÏ?_ºt 9+++F£X,öôôôôôç ÝÊÊJ.—ëçç·hÑ¢S§NQ(”ÄÄÄààদ¦öööÖÖVð)©Õj>ŸÿðáCàÈÈÈO>ù$((¨¾¾Þf³q8ww÷¿ýío`¯¨¨˜>}ú×_Íf³)ÊâÅ‹'Ož¼`Á‚òòò¨¨¨E‹•••µ´´0Œ€€€íÛ·—––æääDGG/Z´¨ªª ÒÞ‰D™LF"‘|}}=<<‚@ K„-‰Åbøüߢ‰GþŽü†W|Èÿ?¡ß„øÇJ~ÈG$ýéô‘Ïÿ©T.‘È$™X,…q‰D"‘@&“Ñh”›7or¹l¡PÈãqÀI ¥R釆g‰D¶'6› ‹Åâr¹b±˜Çdgçž?‘Ãáq8¼Û·ï0™lµZM"Q4 ‹Å‰%:ŽÅbi4*>ŸK"7³X .—Í`0„B1‹É‹\¶ÛD”IU|žX"VˆD’ëׯóx¼VÚ€º¾aKSJkï!î¶½’”C´´CÇÒër.fm߬@¡‘Û°R‡Øû{ }'9ñÕ¡}•‡’ÈGökÑìMTmÜjL<ÀKJ²6ÎÔLÔ r©L"’¶ ÕR÷clôGúGštáÇ,!‡iÔ+•RŽ^-Òª¤ ¥V(1°…]÷7-J+yÃ% "Šº…­¢Öà˜MœâüºŸoWž8^àë¿kËÎÛ\ Âä92Ý:vô"ê=®£Ý¬É¥R¹^gÔê r¹R¥Réõz‹åäääéé ^Z•J%‘HH$RrròŒ3BCC !ÚR«ÕêõzNÇf³322¾ýö[À¬6›Íf;99¹¸¸Ðét‹Å¢V«e2lÒ0‡á3À>˜áíííR©ÔÅÅÅÕÕ•@ ¨ÕjN§R©”J¥V«e³Ù±±±_ýuHH7 l6;888(((99™F£iµZ™LÖÒÒ¢ÓéAAAÁ²eË Ö­[WVVV__¿}ûvÐ?~¬×ëëêê-ZnîÈÈÈ/¾øÂÕÕU¥RÁ!Ë¿ýíocúôé®®®uuuF£‘ÅbMŸ>ÝÓÓ“Ãá òúõkWWWƒÇãøá‡¯¾úÊËË Fs¹\—?þñ111"pöìÙ³fÍzÿþ}oo¯\.×étÇÉÉiÒ¤IóçχàZgggÿÊÊJƒÁ@£ÑæÌ™óÝwß¡ÑècÇŽÍŸ?ß××N¥ÌŸ?ñâŇær¹ƒÁ`0h4»ÝN """|||àÌʼyóþô§?Íœ9“N§K¥RèèU©TÊçó=zèé陟Ÿ¿yóæ×¯_GGG×ÔÔ¼yó&"""//B¡äääDDDÄÄÄÜ¿???ßÇÇÇÙÙ¹®®®££@­³³3‡ëííÅáp³fÍòôô„ðŒ€€€¨¨¨Ççåå}óÍ7žžž æìÙ³`­¬¯¯§R©‹/ŽŒŒ<~üx]]]XXØ_þò—¨¨¨ÐÐÐÀÀÀ¿þõ¯õõõõÅÃÃF£‰ÅâéÓ§õÕWµµµF£‘ÏçÏœ9ÓÇǧººzõêÕŸ~ú©““DûüéOúꫯV¬X±:S§NuqqÁ`0]]]8ÎÓÓ3 €J¥šÍæ÷ïß»ºº‚²¡V« E{{;H>‡Ã‰ÿË_þ2oÞ<‰ßkµZa"‘áíí-ÊÊÊæÎ ×K–,ù—ù—û·c³Ù4 ´¾ àñø†††mÛ¶-_¾üöíÛ+B¡P‚ƒƒ}||Íf3Nÿꫯ<<<¸\n?Çûî»ïüüüª««¡g¾ùæ›°°0P“‚ƒƒkkkA³mkkÓëõB¡P§ÓA¸¼X,ÖjµZ­V$iµZ£Ñ(öíÛ÷Ýwß]¼xš €[¯×wvv’H¤eË–­X±‚D"W©Tb±8==ÝÛÛ›B¡ŒŽŽ¢P¨ààà„„<¿sçN8`4 ŸÏ÷ññ‰‹‹Ã`0†Éd®]»bå=ºeË–ÈÈHˆ¢ŽÅ`0íííx<Ž5766ÆÄÄ@D"áñxîîîøÃÖ¯__[[»xñb''§ºº:d‰D¢Ñh$ÉÕ«W—-[¶qãÆÛ·o999¹¹¹µ¶¶Òét??¿Ù³g«Õj›ÍVSSãåå5uêT6›-‰ÂÂÂ&MšäììÜØØ8þ|ˆ}êìì¤R©îîîsçÎ…{‘ 7lذtéÒÂÂB¹\n±Xt:@˜={66 °²) F£ÑhàO•J ¬u0ïà_Ê_!?xV¥RÉår¹\¬22¿Æä7ärþw…ÕjõŸ—ô‰ªT*µZ­V«'¾ùÈç·æ£žøQ( … ~´Z5‡ËÈËû‰ÃañxøM§SµZ­J¥Q«µz}kk«A¯oÕjõZ­Žƒ›ýK4¿@ }ñâe¡P,ˆnß¾C§ÓU*‡ÃQ( eãÆõ J©”Íd žËcIX—!‘ x<Çc2Ùk£74ãHr™šËŸk×®ñxK‹^\ט½vScÒ!ÚÞ4nÂ>iF&åØáºS‡wÍ¡¼.èb2~‡Æ¬Ë»‹t´«eħö/ :8ÏuxŸôäQÂý¤C¬½éc⌊V,’©”RµZ¯j°xgÏd}ˆŸ>Âè„ È¤óÇωYüŽV&hU‰´*±Z«Qh FÛ8­ß±ïìÃÂjl3 ‡Â D|}ms£¨°ùþ=ôÑãE³½÷Äï¾Ï"EïÄ m}pÿBª3·µË¥ ©DÙÖÚ©ÑÅb)ìL&ÓËË+,,¬¶¶b”- ‘HŒ‰‰ñòòÚ¸qãû÷ïÍfsKK‹Édjoo—Éd¦¤¤dúôé .lnn¡¿¿¿³³smm­J¥jiiÑh4."K‰ÑhT*•2™L¯×3 —    ¥Ñh ƒX,V©Tmmmr¹<33ÓÙÙ988…ButtÐéôàààÝ»wggg‹Åb£Ñ(•JF£Éd‰D·oß÷îÝ®]»²²².]º´gÏžºººU«V=xðÀh4Òh´ÀÀÀéӧÙ}//¯ý×]¸p!@ þì³ÏÀçåæææææVYY988(‹]]]±Xlgg'ƒx>ŸO£Ñ"##?ÿüs///'‹ÝÝݧL™²zõj …Â`0¦OŸ>gΜÒÒÒ®®.‘HMöõõ2eJTT›Íæñxß|óÍŒ3P(”Åbáñx...3fÌÀápEEEÛ·o÷ññ¹råJAAÁ† Ž=úðáC‹%‘H”J¥T*µZ­ß|ó···T*e³Ù~~~Ÿ~ú©§§'™L–ËåГà‘ÉdzôèѼyó‚‚‚ÊËËwîÜyøðáøøx2™\VVùäÉ­V›’’2uêÔÐÐÐW¯^‘H¤9sæ|ûí·õõõ¨$ ÀËË ‡Ã Q(Ÿ©S§Ö××8pÀ××wÑ¢E-0þ|ooïÊÊÊâââ””ww÷›7o>yòdݺuGŽyýú5™LvwwÿòË//^¼`Á‚… ¯[·îåË—ÞÞÞýë_ƒƒƒÁMáåååääT[[ÛÙÙÉb±ÜÜÜüüüjjjÖ­[÷—¿üeæÌ™óæÍ‹ŒŒ  Ú´i“P(T©Tnnn%%%}}}2™ÌÕÕuÚ´iL&s``"é===)ŠÑh„4‰Äf³ñùüsçÎ999-Z´,¾J¥’ÇãõööšL&ƒáïïïêêJ$ív{CCƒ³³3„0 €Ñ³f̓ÐìÙ³'Mš´xñb*•Êd2O:mÏÉÉ©¬¬¤P(!!!µµµZ­V(zxxÌ™3‡H$Z­V8øëîî^UU0yò䯾ú*,÷]'„ IDAT,lÙ²e .ܸq#NoiiikkƒíY£Ñ´¶¶Bm%IKK‹^¯—H$­­­¦®®´—ÒÒÒ ˜Çö Cuuµ§§gbb"‹Å‚½¼µµU¡PDEE9;;777 ÔÔÔøúúFEEáñø3g΄„„ÔÔÔ¨ÕjƒÁÀb±BBBNŸ>Çãu:“É´ýäÉ“ÌÌÌùóç ƒ±bÅ áñxAAA¾¾¾«V­úì³ÏÜÜÜØl6¸‰þô§?EGG¿zõÊßßîܹd2¹¯¯Ðj[[›V«Ý³gÏôéÓ¿ÿþû¦¦&¡Pèëë QOO‹Å‚³d2Ùl6WUUùùùÍš5 brÂÃÃÿð‡?øøøÔÖÖ†††NŸ>ýÕ«WV«U&“ùúúp¹Ü¶¶6ƒqéÒ%ÈPÔØØÈf³Íf³T*ýòË/§NÊårår9@g¥R  |†E†c‚<©…àYXŠEÁÙn€SÀab!ýOè·âå¡nÊ_t­V &€àìSþ‚Ñ'`"øÈç·æ£V*´£U*•J¥P©*µÌ`Ô Eœ;wo±Ø4—Áå1Eb—Ë~ÂTU×—•WWVÕU×4TVÕUU××ÔÖ³Ø|&‹Ãbó¹<‡Ëg0Ù,6—/1˜Ü‹—²/\¼,–ÈDbé­Ÿòˆd’H"fsYL6ƒÎ¤EǬÁá±R¹H(æ1XT—F¡7ÓYd:“BgÒ¸|‹ÍÝ¿ ×L’H•t‡Éâ0˜ì«9Ù‘°§Ý¤Å4ÿ´i+z_}ßa^ÊAѱLüуï'Ÿ‹]…h%ˆQ߉Ê^½B_Ï5¡ê£ºOȸ±÷Ѩ…oîÁ¥'S÷î’'§Š÷î'íM¿·~K ™®TÈÄmZ¡±EihcógÎ~„ÑéïiÒùc—¤l‰­­½U.5hd-jykk«Þh1vâXêøä^A‘›wîÝ·?%a÷Æ}‰»’÷‹ÛpÂÏ»_`Òœù¶íÍP Ø•|{ÿ\¥Âb6Ú”…±ÍÔÚbÒjÍ"‘J( ær¹¡¡¡ëÖ­#f¹ììì¥K—Bh Ä`€­T*•ªTª®®.æéé¹dÉ"‘h6›Á—êââ‚ÇãÛÛÛ ƒÑhÔëõ°|ÃN3a¶ÜÑÑA"‘‚‚‚ k^¯§Óé2™ 6Ëììl77· ”••©Tªººº¹sçfee( ³Ù,‹M&Sgg§L&»víZXXXyyù‘#G.\8þüíÛ·WWW/Z´èÌ™3b±˜J¥ÆÆÆàp8@aÄÑÑÑp`ßÛÛâ°!éG@@€··÷Ý»wM& EWWW‰4<<ŒÅbÝÜܦM›F¥R ÅÑ£Gƒƒƒ.\}‡††Â«H$š={¶››˜Ã;::À¶=sæÌÉ“'Ï›7ò»ùùù9;;×ÔÔ¨T*¡P8{ölwwwH­pëÖ­yóæ………ÅÇÇŸ;w®¡¡Ãá¨T*‰Dhpp°©©iÑ¢EþþþeeeL&sÍš5¿ûÝïf̘©!Ï‘Á`hoo{ÞåË—===,XP\\¼{÷î°°°””huHHÈ™3g(ÊÒ¥K?ÿüóeË–Aî''§3f466¶´´…B//¯iÓ¦‘Éd“ÉTZZèææöþýû¨¨¨?üáÁÁÁ8ŽF£}ýõ×^^^(J.—?|øÐ××7<<|Ó¦MpÔÚ¾{÷n??? Óéoß¾îîî“&M …Ä«ÐÿïÞ½³Z­d2900R+ž9sÆÇÇÇÃã°°°©© B;ðx|kk+¸àgÏžÝØØÁHsæÌ™5k@èëë#þþþNNN‹¥³³Ób±€ÉS&“=yòºèùóçb±¸¥¥¥µµ•Édè\±bÅ‚ ¨TjoooSSS@@@||<‹Å¢ÓéË–-›þüßýîwAAAµµµƒH$>þ|îܹîî‰$)&&fΜ9õõõV«•B¡,X°`õêÕ$©¿¿‡ÃùùùyxxTVVnÛ¶mêÔ©PŸªª* -D"ƒÁ·#—Ë[[[M&“B¡€q×h4f³™Åb]¹r%::úêÕ«4M¯×s¹\‹Åb0´Z­D").. ÌÉÉ …F£Q¥RÙl6¹\¾xñb???õì%!77wΜ9/^¼`2™M¾hÑ¢«W¯R(›ÍÆãñNœ8ÑÅÅÅwî܉ŒŒ¬¯¯G¡P'Nœ “É0Íýýý¿þúëÚÚÚ .,^¼Ü),+ `Μ9gÏž¥R©k×®ˆˆ¨¯¯ŸÐL&‹ŽŽþüóσƒƒAŠfΜùÍ7ßÔÔÔtuu‘ÉdoooðB ‚Ãá¼¼¼¾ûî;8bûý÷ßòÉ'^^^¯^½Ú¾}{hhhMMÅbinnž?þÊ•+Ñh4¬'!??Á‚Û·og³Ùííí, 4¨ÆÆÆŽŽX£À¢µZÝÒÒ¢Õju:^¯oiie2$è…ZZZKAÉ L¬R©€ùD’² PþOé·â˜[§ÓAÜ}`M&jµŠ}¸ÂÿÝ:ÿ‘ÏoǧE­Ò«UzF÷ 7µN¯ÒéUF“^$ææÝ¹Á`R8\†PÈW©2™lý†Í+V¬ _±dÉÊ¥K£–,Y±l劕k¨46…Ê¢Ñ9,6ŸÁäRi *ÅdñHdúÙs—Î_¸$•)$RùÍ[·±8<›Ëas9Y®TD­^‰Â¼Ç›ÅRA¦2ˆlËgÑd&›Á HdjÔªè&,/ˆT"‰F¦Ð®ædKdÒ›ÍL ߋۆޗAKIc%íc¦¥V¥ì|´k£µ¹iSшœ÷*3¼=˜"~zw€Ú„t¶J9¤§·ŸÜY“º›™–ÊKLæîÙOʸ»a«žÊP©TRS›ØbTv¶3Å¢ÓaôGúš”sæv‹Ôäèèë6˜­cG[[ßP«©§³¡‹»ÞSõ{3®ÞÍ›ÿ¢¨©©ª~ù¼!?Ÿ¸c÷ÍÔô—^(q4dÿá×Ûo2Y£}öÁöVSW§ÕÑ?ÌfKoݺ½fýöíÛ BYYÙÖ­[srr Ôn·ÿüóÏ›6mJOO¯¨¨€óFB¡Ð`0TVV^¾|977·¹¹ùÝ»w)))¡µµU,ûúúN›6­¢¢òàòù|»ÝÞÛÛ +{OOÅbQ*•åååÅ §µvîÜyãÆ µZ­P(îÝ»wøðák×®µµµuvv–——ÇÅÅß»w‹Åæææ&%%Ý»wO,wuuuuuyÕn··¶¶:t(((ˆN§ççç{{{æåå±Ùì%K–ÿüóÅ‹?yò„Ífwwwwttèt:“Éd2™”Jåùóç=<<‚‚‚Ðhtqq±ŸŸßÏ?ÿl4—,Y’œœ¬R©^¾|ééé¹råJ¸):$$dÆŒ`¨‰D3gÎtwwollD‡Ã9;;Ïž=®vƒeIII]]]@@@pppEEÅ‹/,XpåÊ …‚B¡Øl¶J¥’J¥¯_¿Ž‰‰¹{÷nCCCaaafffEE…Bqwwÿãÿy…y<ž««+8÷ÛÚÚx<žÏôéÓáÜjnnnbbb^^^aaaVVÖƒjjjzzzètºÏÌ™3ËËË;::Øl¶»»»››‹År8 eûöí— q·ýýý:Îb±twwC`ý÷ßôèQð*Ü»w¯¨¨¨¡¡A¡P¼~ý:666''‡D"ݹs'..®  €H$J¥ÒÅ‹C`:¸J?ùäHµ^UU5sæÌÔÔTHÐé«?~¼k×®sçÎi4š¾¾> “‘‘„Åb!¾|ÕªUyyy?¾zõjqq±\.·ÛíZ­öêÕ«—/_V*•v»]­Vwuuuww›Íf£ÑØßßßÙÙùäÉ“%K–ìÝ»·¸¸òNÂÉ`‹Å¢Ñh°XlRRRxxøÓ§O•JeGG‡Z­v8z½þÑ£GQQQ>Äáppôüùó, F¯]»öرc/_¾Äãñ7nÜØ²eKYY<.•J ž={öþýûÄÄÄ={ö$'':tèÒ¥KÕÕÕb£wíÚåëëËd2Q(Ô•+W6nÜøòåËÂÂÂÕ«Wggg———³ÙìÇoÛ¶íîÝ»4 ƒÁþ<44tåÊ•eeeïÞ½ûþûï§L™RTT¤ÑhD"Q|||PP‡s8x<~Μ9žžžD"Q,/]ºtÒ¤I>>>8îÙ³gkÖ¬¹~ýzYYYIIITTTaa!‰D*((¸wï^UUÕÏ?ÿ¼fÍš~øÁ`€Å:55Õ××·¨¨Èápôõõ ƾ¾¾––…B ÉüSËxÏÀ­×ÞÞn6›;~…þ·ù¡ù´··ÿZù¶¶6ø¯Ùl6 ­­­` ùµòùü¦|,í&‹¹½«³³Ëb±X,–ÎN³¹ÃÐnní¶wR¨ø;woášÑB¡twwˤªC‡NÀEƒéé?¦§ŸJMÍŒ‹Û½pá÷ 8‰Å`ˆ$:ƒoÂHdÎÆ5“2Ëι¡Ö´Heª»÷6a LÁ²¹:ƒµnÃúÇOŸ`›q( º¤ô]#ª¡¤´ø=º±®¡þuÑL¾¨øÝêÖÕÔ¾e(t3ƒÉ%Sh7oÝ–+C]]éåžDÒ¡LZÒ>Nʾ˳gý>ÚTŽØ[ ÌÙ ‡Ó¦` œ?ʺ}e˜IFz»­´§±ìÆ’„½ ’ÔtùãÍ ïRŽ´S9mFm§¹m ×Øce‰?Z£?Ò?¡I™ûÏ)yz‡e ·ÝÚo±õuõÙº© uô¦ÃçrËêð¦Ê&Ý…ì┃7nؽcûþ 1ûÊ;uö ŠÐË#•([Ò¡§?ÝCiõÃ6ëh¯ÝÑeîè¶Ú#C‰ÊÉιé·RSS/]ºÄårÁ¦"‘H.^¼äéé¾víÚøøøÄÄDȳq÷îÝ#Gޤ§§ïÛ·ávtt .—ïîî³bÅŠÕ«WŸýúõ®®®[¶lÁ`0 eïÞ½kÖ¬y÷îÉdâóùË—/_¾|9‡Ãb±™™™!!!+W®\³fͶmÛöîÝ{õêÕ¦¦&“ÉÔÛÛk³Ù,‹Édjmm}øðáÚµkccc8––vòäÉ£GI$“ÉÔÕÕÕßßo·ÛÛÚÚýýýcbb <==½¦¦¦µµ•H$ÆÅÅmÞ¼¹©©I«ÕVWW_¼x1))©ªªêܹsÉÉÉ=‚@6nܸbÅ *•ÚßßO¡P6nÜ]__ ¯_¿ž’’òúõë¼¼¼ØØØ¼¼¼šššk×®A‚—•+WFEE%&&æää Ñh‰DòôéÓ´´´åË—ƒÅ²ò­X±bêÔ©ëÖ­ƒd2±±±Ë—//++3›Í cÛ¶m‘‘‘4­³³S«Õæåå%%%mÞ¼933³±±Q"‘ôõõÑéô 6DFF¢ÑhÞ´iÓ÷ßF£­V«Ýnûöí’%KNœ8! ív{OOZ­îïï§^¯§R©¹¹¹6lزeËùóçÁž*—Ë…Báµk×6oÞ¼yóæ­[·fggóùüîîn“É´uëV''§ððp¨ÆÊ•+=<<6mÚ„ÇãÑhtZZZddäÙ³gÓÒÒÞ¾}k±X„Bá½{÷RSSW¯^pÿþ}ÐWÍfóàà @ؼysyy9ŸÏ‡´6 fp2Òb±Èd²¼¼¼ëׯ³Ù쎎›ÍÖÖÖÖÓÓ344444488(“É._¾ìïï±|ùòU«V9sÔ‰¾¾¾;wî$%%íÞ½ûÝ»wmmm $f³yxxØh4^¹reݺuK—.MII¹yó&x¨ËÊÊrssããã7mÚtæÌ™òòr…B¡Õj;::FGG›ššnÞ¼YWWW[[‹B¡ ëÀœßÙÙiµZ- ‡Û³gØéU*UNNÎÞ½{7mÚi|ÄbñÈÈHKKËãdz²²¶mÛwåÊX+ Å»wï²²²’““Ÿ?~ãÆ„„„`0˜žžž²²²ŒŒŒ¢¢"­VK&“ׯ_¿bÅ </‘HÝÜÜ‚‚‚(ŠÙl¾sçNbbâêիׯ_ÿðáC`.‹ïß¿ ÀñãÇÑh4“ɋţ££aaaéééà‚CÄf³uvvŽŒŒÀLíþÍßúÙl6›Íökåÿÿð™`eû…~­|gg§Íf³ÛíÝÝÝàÀéêê‚?ÿ[ô‘ÏÿˆO¯ÍÚk³övÛúþ½œÝbëî´uw Ȱ@ÈÉ»s…jÄãñXls—Ån2ZºõôÒÅ;Îß¾|éî¹³·Nž¸’ºïø¦{ªªšÐh*ÏÄbih4±©‰ŒÃQqÍäf<åH扜Ü[m«L®K¼±8‘Dc0¹$2}mÌÆ-ñ;×­]±rÍšèõ«ˆŽ\¾b㦸›¶D,[¹~Ãæ¸¸~~!oßÖÐéüæf/as·óîê[Ú™R¸cWóþCÔ½)¼Ôý¯W,%fîChkåkáÝkÄc™Í’ì'L"dìm>y}óæh3‹.£)%™µo?{[{kjYÔÖ lùhWÁØÞnïî´Y5MVÖGý‘þž&eÏ1émÈ 2hu Úúz:úµ*[Q19zýÙ¹Aæ/>™q¼6÷: ïǽ7qmx¬² «Ê¹Q•tðþ‚ˆ ߤ EÉ…¯H]VÄÖ5ØcµõÚ,½=Ý=½ýö¾{ßH·½Œ7x<^¥R wuu!Òß߯T*!ÖòÀí!‰d||Üjµ ¸¡£­­ ÀìßJ¥®ò"‰‰vÖÁÁÁááa‡Ãa³ÙÀdÒß߯V«á N+ÚÀÀÀèèh[[¤ëjmmµZ­ÃÃÃCCC*•ŠJ¥‚ ³­­Íb±8ŽÁÁÁîîîžžžÎÎÎÁÁA­V[WW§R©º»»ÛÚÚ AÍfƒ$ t:Ý`0ôôô˜L&@qÌ*•ª½½½··wddD,c±Øšš…BÑÓÓ™ÝØl¶R©„¨«³³³»»»¿¿ŸÉd’H$™LÖÛÛ‹ H[[“É„4a`ƒ·Ùl===b±½P(ìëëëêêr8B¡N§×××k4šŽŽV__ÏápÚÛÛát&XÎ$Icc#Üu' oݺµpáÂ3fìÛ·ÍfOà$È@¥RáJ6µZM§Ó5Ýnü… b±¸±±p{zzh4šN§ëéé±ÙlÀªR©:;;ûûûÁñ32™ âIÆÆÆÀ‚ÈãñzzzÌètºþþþÞÞ^8†h2™¬V+$]áñxd2¹¸¸ òÓ§O¾üò˘L¦ááaµZ )™áŒááah —ËmiiéììT©Tp´\,Þ¥Ñh`’GÄf³iµZ¸¸œ¡ÝÝݽ½½XšL&kµÚ‘‘ÈÓÜÚÚÚÑÑ144„ (z½Þf³9½½½v»}ttB¥áÆFc±Xúúú†‡‡á`mm-ƒÑét}}}V«N‘Â]3*•j||œÉdÂýŽ*•Êb±Ày8`¦âÞÞ^«ÕªÓéh4“Éär¹Å &ƒA¡PÀŸ­­­jµ²b#¢¿¿¿¯¯ŽE‚x#200088ØÛÛ féîîn•Jwr8ÖÔÔD§ÓÛÚÚ:::À6,‰à6*Ð%4 àl«¼F£iooG¤µµµ¥¥E©TÂm/ ´··k4A†††À¬›’’¹víÚ³gÏ’Éäžž+›Í† ä´Z­PmpÎH¥R›ÍÖÞÞÞ×××ÛÛ ¡ÍÍÍp^ꎎHH¢Óé á®@ @¤»»[(j4š®®.8ýŒF£[ZZä]•¯··W,C¢:>ŸßÞÞn2™,KOOÙl–H$ÐÕR©´§§g``Àáp´´´€šM£Ñ, ˜::: ª---ÃÃÃö_¡žè×¾ÿµ2ÿ|þ+44d·ÛGFFìv»Åbs8€-à}}}½½½ããã€?à3,[ðêööö®®®ÑÑQA‡Åb•A±±±ÁÁA›Íº4ìî°B§ŽŽZ­V£Ñˆ H?ôÃÈÈ@ `ï‚ [­Ö¸òÚµkYYYT*Õn·“É䈈ˆÏ>û,%%E£Ñ€Ìb±@‡O舟Az²¯¯ÏápÀxAGõõõVõ·X,6›mhhh``PŽÃákkkíééo£Ýn‡A¨:<<Œ ÈÀÀ€ÅbA·Ûí‚ôôôhµÚ±±1«ÕúìÙ³›7ofgg‹D¢ááa:¾dÉ’¯¾újÛ¶m<PWW ´±±±ÑÑÑ¡¡!0^B§F³Ùl·ÛGGGAJFãèèèÀÀÀøøøøøx[[[KK ŒÚØØ ùŽŽŽ±±1»ÁÁÁ®®®‘‘‘‰qììì4™L###PíññqIA†‡‡ÛÚÚ ÃÐÐ$m†¾QÏÀàààøø8à°¡¡¡ááa`e2™ u ãuuuÞbµZ»»»'úÐf³õ÷÷ƒÀ€ F«Õ ðz©···½½&)|Ⱦ§§´VG`† ˆÙl†ŽêïÀ\Ðét]]] Q b4ÛÛÛAŒAµèëë3™LÐXè@°h †ÎÎN-x‹Ýn—H$EEE(êõë×ïÞ½‹Å „׌F#Ì}VhÔsll $&#Ô°µÕja X»æÂìëïïëëë3›Í0‚6›mBÍ€§@Â'vYNª;LÛAFGGÁ…±‡zÂzj•Ãá˜èdè–Jõ„ªöÿB¿BÿÛ|þŽþ¯åáðjx#Tæ¿Èÿ#Ÿÿ7>Ã£Ž‘þ¾¡¾¾¾¾¾¾þþÞÁÞÁ¡¾Ñ±A‰DtãÆ-lñ}c3®‰Çr Íâf¬¤±Q€ÁHš›Œ¨¹YF¦(›ñ’êjÉ»¦g/ª žU¼*ª{W†*«À”–£PÄý‡Ž^¼r£½³ÏbÌ:ŸûM­¨j*«À¿mÄ$²‹ã¿G±Þ•bkëÉTº¬™ÀÅ`ÙÍ>‘$A¡Ùõ t,ŽûÎó‡‹_U7 ˆ ¶èá“gF³uÔÞhôwc·¼ÚOÚwˆ‘”Jؽ‡™y¤zÏnìÁ \R-!C¸÷¨8é0gï>rreß>Qz6þ@Ý–D|Â~úž}¢¤4åî#ÜØý˜µ)—mE8z¤{|ÄÔŒ Ã]‰ü#ŒþHÿH“ˆc±ä IDATNº"‘èú{LJGÑÑáþdéêB*ªIûò“R^ø¦G,>=ÏgïÚeGç{Ç,ûa~ð²À ¨µ›ÒÒŽÞ»z£ôaAc›ixpFÆú1ûø¸cGzGÇëÈÈl€'€È‡ß8AÀ 0‚ °Ï ýBGFFFØÁ¾>Á>Âå‰ïáük``óDMÆÇÇÁP N`_À7`yx5€5S.ìý€ÔGGG'fÝèè(ø<<< vÖ‰WwwwO@IXøÆÆÆ&ZÛ90ªŽŽŽ‚kÕÁK¡O&F¡£££¤¤äêÕ«‰‰‰¥¥¥r/[¶ìÊ•+<Ïl6ÃpLtˆÃá €ü‚f¨M(-###€þ'´x5ÔpâóD¯‚5ñ x újâ0j€×¡ùÀ 8À)4››»{÷î/^H¤’’’õë×GEE]¾|Ü ÀŒþĈƒæŸ'†x¢04€æÄB+ ‹`¬A5™¥ àh;À èC>Ш ‡êž9ñ"è‡Çª ÕÕnb:L4 ô.)®‰®°X, ‡^êë냺AÁŒÀ=222<< Ø}bíqbÔ äÄ}Ú ùp–Á÷}}} 6Ž>4jllL§ÓÁƒ *cccÐ===-moo‰Dr¹˜ ÐEƒWÿã$êééå øCÿ|8÷'ÆôÍ Ezb4¡g ò æ}h˜¨€ø` þĘN,)]]]Uú¿h}Pÿâ#ÿøý'eþ±üÿì]ùüOøŒ#È82>61ÅFdAFÆÇG%ɕ˹oßT¿®©(Å>©~ü ºà ê჆ü§è‚¦‡k=ª+(h|ô¨¦¾žSZFxùªñ嫯·%˜Ò2Ü»Rì›’†¯*÷ì=”u>×dîï²d»ñª¨®èMCU5éi~ÕÓüêçÏß?~\ýæMó«WØ/PT<ͯ~ð âÁƒŠ—/ÑÏž5Þ¿_þèq¦‰_SK)~Ûø´àÝÃ'¯Î^ÈQë ÈÐx•}ñ‡õwV¬Ã%¥c¶íEÅ'O^ >MÞŠ’t’³ó¸`ûQÞ¶4æŽý”Ä}´”4Æ®ÃÌ]ÇyiYÌ´“샙ô=û©›SêVÄ-ÛzgùV„­AzÆÇíƒÈ(2fµ·HdaôGúGštöâM¥Æ46† È‚ Û»:†ÇìÝã]VD¦B,6D"C$¢…°ß t¨ø- ±R&WŠ$-|‰YoB¬}ˆ±GFGÇd³ ´[G‘á!éC&vÀ[`Ç+£Ãá˜ØÉÀ8^Tø¶º‰-Aعa“ƒ]ðÃýùƶ‰÷…¾­~ÿö®ü)®ëJóçxjjjÆ3eϤÆ53Uq%ÞJŽãÄãŠÆqÇc»9ž8Ž%YY ÙŠ6–^¡!YŠˆMÈH–mm$¶¦YÔ4MÓ4ôÞýÞ»÷Ìßô©McáÈɼS¯¨îǽ߻ûùî¹§ßÑòNFDøÉ¨p©P6›…µ’+…AS‚ö|° ‡å˜KÈÆ'®kS0*P1N:By–ɆÒL&‹Å2™ èÌÏ«(I)¹vÜ&D„ŸfNNNÖÔÔlß¾ý‰'ž(++;zôhkk«Ëå&Lïzƒ:ÁÙ‘©37,øwÓT*¥( N¥AÝ4M‹F£0—Å÷BÓ,®ÚØp]xßBDØ-ÀyÝétîÞ½û•W^Ù¾}»Åbééé¹uë–×ëEJìa¤”è c(âÀÑï÷£÷Ùª ë8Z¶8EƒAô‹~'Àk+ïj˜B¥ÓiÞÉ ÌlKÆq¿”à }„QÁñF¦Ê€Ñ˜ËåàÇžEÆt:‡ãñ8:‚9ìO(¤;k:o@å2ͺ–! FËSuÔm0~žn”§‰D"‹4ÇãqTPÓ´X,–Ífq"ij÷ˆžh/0qÇp…7¶1@CiѪ\A™?kJ$¼V€Çb1 ¯¢’Ø£bÜÂ=ÇáXÅû× ïòòr(ÂÇdÑoõõ¬u(‰”rddU.))¡¿d1hÇæI´¢s‘J$ˆhzÚó‡CÕUÇêMÕ§?9Ñí°u9ë.Ùm=óGýåçe«¥Ûjù¬Î~Ñlît8ºm¶›­ÃÙÐÕx²Ûáì4[š«kOUמx÷÷eG×BéP${ä˜óð‘«íìÉSìöN‹å\mmûá?4™jÏÙmMµ]µ5_œh¸l³v××]r:>w:/:õÅ‘#§íuí'»N~rîèñúÃǬþài”õ˜^{çðzé÷{ßÚqí¿×»mW×Öm7Þüpàµ}c¿Øë~©lô¥·~þÞ×Ûûæ{×_z÷æ/·_yã½î_½õù›o_yë¾|e[ëyà‘'­[­º|”#5§iRˆLfÎ3e¼7ÚB)ùõoK‡Æ<ÙœJ¤Œç2¢¬¦)Q,Ki–“OdHËQfÄrlq–H•D‹1m)E9IáE-IhjŽ´˜TÃJf>“‹de2C"%(ÉAêu¬³”×ĬŒ‰¿…^aó­Þ8Ë1Ô ,ñ/68A§R)fà"ét'È t`l „µ‰‹ ÒÆ–`Ðq(]f™($t6~üDy} 8?€°tf?ÜO&“`<(ŒªªËËË@ºÖÛ†õ†md¤¼=˜=ÅÑ>l9OÕ+xdŸ7"NMOOãgõ¼‘Ð/½«4åI-lú~ä.Ã_f`kÚÒØf šÂiЕ(<ÎÁãñ8¶8 BÀK„Ó'“I—ˇø©©)¯×‹ 课älŸæ½<·-jÍ-Ì] 4nm¸4`T`«]Œ ™wáࢂ<¥éÍ¥”çðÆÁÞ³1·Ó›ð…\Œvá2ù …B ‚Ùηj;Š9…*kš–H$Ð5Øo€•î£Ñ(ÆX>æõmΛ nX¶¸‘þàˆ©<·6Z÷aYçþÒ÷,SO"bÃÓþrËÞ}5e{ŽW0«r6·v»§üÉ i’29òx£Ç«Í–æëËËëÊÊL|PsèЙ]»Ìûö6ìÛÛpøð§»w›vï¶8pâàÁSååŽ}ûlee¦ýåÖò ëŽ>ªw6;›¦gQ*ž£$%‡f®UhùÍžºÞ8öƒŸÕ<ó‹†ÿ|»ñùwNmyç?Üvúû¯Ÿzüµ“ϼVÿÜ«Öç_­Ùòóº­¯úéÏnݺû¹-_ú/û[o~¬v¨¹39íYm!ŠEd6EbÀ=lÐhC ¥ä7ïü.^‡Ã~¿/²}ÁМßï‹D"á\¡ÈB`!2¿°0·°0 χÃÁp8 /„ÂQ\áðB08 Îá †æ¡ù@(}?h±÷‹È½Â)–¾˜àÈ8 Á@ …ð£çÁâíwÇÀ1p6N0Äψ£Ñh( ƒ‘Hdii©¾sp"‘H8¼„üs!ï̼çÎÜÔ¤orbÖíö­¼¼+® ÏÊëÎÏ;;9øçÃÞÙÀÏÜÊŒ…˜ú¿^·Û;9雚ž½ã™óÎÎûæ‚óP0Y .¤æs³áÜ@nb6;r'u{2>0ëw'ú'R7§2}S™¾‰LßDª<Ñ?¾tk|áöxhx,02æõŽÍE<Þå@8‹G–¢Åˆ7à÷ægçýÆ ï )”’;wz<„2 Ú|>ß`qwÙl8ÅÒ“‰‰ ¼Õntt¯‡ÃÛÙ œ‹Sl>¾N9ïF¸0“®ñé¡1ïkvx|ntbÞ5ŸMxB߈»ðšuO©ÁÈÈÈØØ˜Ëå56h´!…RRQQ÷%÷ôôtttttt455µµµu‘– ÊfÃÙ¨´¶¶ž={¶¹¹¹©©©©©©¹¹ô œ‹³ÙÆ•cà8>Nggçùóç»»»ñ2ûÎÎή®®îîî>×À¹·8¼,·´´`enkkkoooÝ ´µµ®ómmmÅéèèhooGÆ––ÄmúôOçšÎžÿÓÙÏšZ»›Û.´´_<Ûq©µ³§íÜgM­…WWskÛÙ¢ålooïèèèììliiùúë¯Ýn·×ë5h´!…Ròþûï#R€Ýnß¿¿Õj­©©©­­=^DlEÄZD6N±ôÅÄn·ÛívÔß1p N±ñ°Ñ~7p góà;v¬ªªªºººªªêرcG=~üxuuu1|çþàXŠˆiƒ(“ÉTSSSSSc2™,‹ÍfÛ(j¡/‰Ùl¶˜ÌN«½Álsš¬ŽZK}¹®Úd¯ªµWÕÖU› /[É\»6~UU•ÉdÚ¹s§Åb¹xñb¿A£ )”’ýû÷ÏÎÎööö:ŽÊÊÊ¡¡!>بO[¨ˆl6œu|ÅÖ‘€N¾‚soqЇö»cà8›n¸ù‰Døk1|çþàÀC:”ïè@ 0??ÀI^Þ‘ý[kU¹^ß\À7œóçü!ÿ|x> ‹]¡@0TäÉ>Ÿ/x<ž7n|ôÑGN§óÊ•+6¤PJÞ}÷Ýñññ7nX­ÖÒÒR¿ßϯAXS6ú€Í†cÈ_‡l¶qeà8Οó—^þÿo8›Mîy-|>ßÇìt:¯^½:==mÐhC ¥dÇŽn·{``àÌ™3•••ýP¿ãe³Mï¿ÖeÂõe³ÇÀ1p6г s|çþàè_M½êþ†düo#uo%—ù2’HÔ¤P…¦ M“bÍ«˜ð+>ý~eeecccoo¯ámÈšRRZZÚßßßÓÓS__¿gÏžh4Êoc]SÖŸ–…²ÙpŠ¥/&ü}öužkàÜœ»Y^ï¦ß ÇÀÙT8"0RR ßÀ¹Ï8œrý~\_a~#B¡’YMÍjjNh9¡)R¨$5¢u®bçÈ Á`°¢¢¢±±ñúõë†S‡!kJÉöíÛGFFúûûëêê***ÿö8R‚h`—ÆQTðAJÉq¶„!B/ZQ”ååeŒÎååe+Ax?ÄÚåG!8—£6p "ZZZâX"¢…AX‘)-òá6(b¥B¤4¡ [(uì—ûÒþ†Üa@EV´o\æ U‚³iqxâ ]´Ρ‚è›Â”8÷GŸ’Öb+GŸfu£•ƒ‡?8›gM¾†.¦•qF …[êCórX«»Ä‘D9¡)$’Y©)$U"•(+5•(©dU¢”šK©9|]‡FsüÚÅÅÅŠŠ ‡ÃqõêÕ¡¡!ƒFR(%»vír¹\ƒƒƒõõõ~¿Ÿˆ4MK§ÓÏ \_ãñ¸”’㥩ªÊ1½r¹\4•R"b3OQÌ vAP.L ™ LDÆAzQ¤B ¤Âö’.F`£Ñ(b˜I)9x˜>ø ƒ>P%‰ðSÄJA(;0³Âõu‘+ -úõhÕ~ÝÀù.p aõà…"t–ÎKº“³©pô€«úš£I“.¤%‹ÐE7p6X—~ÐJá”TÀHô÷AÇ‘˜?8‡;š»žÿ Ï›(Öø«RÊ|ôS¹‰ßŽ&EFUÀžs$r$@£ñì9#ÔŒPñU#´vy¸²Á`pïÞ½‡£¯¯orrҠцJÉîÝ»].×­[·êëëËËË™FS>0ìÓRÊ`0822 ûûû=¢(l¢@¼èd2ɶj™ãMDÑh4Nê­ªj0äHÑŠ¢Äb1ŒÂ`0HD)qqiå¤Åb Ýˆ,„Èd2sssD´°°àóùü~ÿøø8Ç^ÆC¹$˜®ú ðv3á^%r%-Ðß\µd˾*±sq {§PC¯ù/¹–.×Ë:†çþàȽ®Ï¥ÿºjð¬"|ÎfÃѧ,œõëŒý"°ê³ó pôSXèöÉ2o ÃfŒˆøÔWJ¹jðhš]¿fÙîGZZɱÆ"VI¦rYþQ*—UIÊ"åᧃF;Λ7o?14dM)Ù±cÇèèèÀÀÀ*§Žl6‰Dd^ššš^}õÕGyäÙgŸÝ²eË‹/¾ØÜÜ >Jy· ˆìþÁN|w0gxæ¤R) eÊŸ"‹bqqQJ¹´´Ä&êT*Óx"‘X\\ìëë{ùå—ÃáðØØXiiéã?þöÛo#Ì)­4™Äb1˜<¡(Ïï‰H_Sn!„þÔfrý†ZsIÝï œïGJ‰:ŽL(¿2êmŸ«A±“MMÓØ•ÈÀy€8zM–Ëå²Ù,//Ùl6NsCa`,i:OçÁâ!ð‹ŒÈ¥išþ J¡(Š^à‰<¢˜Hé?8‡­ÈD¤ª*êª ÝÊf¬t:­æv»=[B°&Tð†p©dNh’H‘"“ËfTEš"…¢©‰LZeT%§*Q*“V´¢80À !*++z{{ÇÆÆ mH¡”ìܹ4Nóóó”_ ½^/ÆÖ¥K—{챇zè©§žºpáÂ7¶nݺmÛ¶‰‰‰X,–J¥îܹ3ðÔÔ”ËåÒ4-N»Ýî™™™H$’N§GGG3™Ìòò²ÏçCÒL&Ó××çõzý~?(u*•òù|ì933333‹ÅˆHQ”ááá@ 0:::99)¥>wîÜÓO?ýðÃ_»v-‰œ?þ«¯¾º~ýºËåJ&“gvvÖãñ ÎÌÌhš–Ëå&''ÀíÛ·ãñ¸Ûíž…a[UULT¨½šÇĆ­Õ¬æúôªNø,ì§ œ{ˆƒÅGÀI&“Ñh:ŠA¿COðY(y>x<\ó œ‹£iÚÿ²wæAMœog:Ó™Nÿèè8­vÔ:Uk¥Ðp“Â"X*EŽ¢€÷P«µNuZ¶ÚB=¦ý»±3v*8 ˆ"(Bä !!9vslî’ìï§Ù_75˜Ùï̲y÷óîñ¼ïûìû>ï».wü¾åp8Ìf³Ñh„1+‡ÃaµZá=ÜéžÕ@ ì‡æø‘c·Ûa(Ê)v»{ãêg8~‚¡*˜œœ âOnÓÿr Ë¬Âf³–4ÂÃýµZ­`!`i0z½, \X¨Ø¡6ð•c³O¸\®‰I»Ùl¶Ø¬“v‡Ãa±Y ‚˜˜´Ûív×?qh7šÖó+àøñã‚ôööBPHxVs}}}›7o?{öìÈÈŠ¢V«õÞ½{<€ ‚ …bË–-kÖ¬áp8©©©‡éîîÞ½{wQQQOOOgggQQQqq±L&ùâ‹/²³³wîÜyçÎâââ}ûöõôôár¹))) KMM‹‹Û±cŸÏ7™Lb±øÀ'333%%¥¸¸¸¦¦æðáÃaaaÁÁÁl6ûÓO?íèèØµk“É,))Á0L,ggg'''¯Y³†Á`ìÞ½[(ö÷÷¦§§L&4e›æø—3å@Ïçµ1éþzVÎð.íɱ¹§H¹žžeø‚ár¹œ“—Ëõ÷,B111A¸»Ý>9a'ÂnóÆp:J¥òäÉ“—.]êêê …´Mkº¾ýöÛ)n´Ë½ª¤àñx‘‘‘ à7“AZ­V­VWWW¯Zµ*222))iÑ¢E©©©7oÞìííe±X+V¬øí·ßÊËËsrrV¯^}åÊ•žžžäää×_=44ô›o¾Ù¸q#ƒÁøùçŸ1•••l6;##ƒÉdFFFþñÇV«õÚµk,ë­·ÞŠ Љ‰‰ŽŽ®®®îí파|õÕWÓÓÓ+++7lذzõê[·n‘ÁSšŠIwÀ€ç-›^¡€îŽR²@z?„æüŽÝÎá™~:ÙóWè ›|:ê€ü•ê@šãΔ¦”ðˆàty„rz:ë¤7àt:¡mJ3OsüÂq<+6º?O¿?^噆ܦ9þåLèä¶Õju:‹¦B‘nÃ3šôˆ©ðÌÝWŽÃ>é^5ÚE8]ÿßv6‹•p8­fË„Ùâr8í+¹NÕUntEEEgg'íFÓz¦Ž9ÂçóÉ•: ¨ÃétªT*ÇÍfóøøxLLLJJJCCá^oŽp/¸ÁãñÖ®]Ëb±nß¾ÝÚÚ³`Á‚¼¼<.—”””´oß¾ .°Ùì7Ö××.]º400pÇŽ/^ [¾|ù¶mÛ¸\nnnnTTTKKËÀÀÀíÛ·cccÓÒÒÚÛÛSRR–,Y’››Ëår›››333wïÞ}åÊ•êêj‹ÙÔÔ4::úÑGÍŸ??::º¡¡aýúõ±±±×®] …ï¿ÿþæÍ›ëêêÒÒÒ>øàƒeË–mݺµ¢¢",,¬¸¸¸¥¥… ‡ÃAG‚+¡0èc4F#ü o½d°Œm™L&ƒÁ`0L&“g°Hsf£×ë!öcÊNÏÈÒá¶ÛíV«Õ`0èt:XFÆápLLLkìÃp¤ÙlŽã4Ç¿³Ù #­`¤mØl6³Ù ¿fb–`·Ûm6›Ñh4 08KsüȱZ­~…§oµZÕjµ^¯Èáž@i,‹ÕjµÙl§ 6 9þå€1¨Ì¡Ý+ÂqÇq0‡ÃfÉÈ'&&ŒF#Ø›Éd‚UtÉC|☠F‹Él3™ÍFÓ„Ùb·MLZm&ƒ‘˜t˜ ÆI«M¯Ã:|Âj³ŒÎIJò¡V«KKK+**ÚÚÚh7šÖtýÔÑÑÑQ^^~æÌ™L¥K£Ñ@h¿R©Œ‹‹‹ŒŒìêê2™LP3j4èi¨­­e2™,kllL äææ†‡‡WUU±X¬ˆˆˆÚÚÚññq·nÝ:ð¶7nܸbÅ 6›ÝØØØßߟ––ž÷õë×™L&›Í®­­•Éd555‘‘‘>d0‹/.((Öétb±X*•böàÁƒøøøÄÄı±±žžžS Ÿ IDAT¼¼¼÷Þ{Íf·´´„„„$''·¶¶Úl¶ÑÑÑÔÔÔÐÐÐÚÚÚ˜˜˜ÀÀÀ­[·ÖÕÕõöö2™Ìäääšš‚  0ëõzN§Õja‰=½^Ó!fš½^û(š3³=…|MdG>w“ÉDsüÎ N§ÓéÈ Ñœ¹Åñ,æƒÊlHQåKsüË¡Jï« îÚŒG¯×C[ðb½×étz®×ëM£ÉdÒëpǧÿõÂo^¥R8q¢¼¼œÇã‰D"Ú¦5]Gzüøñ¹sçNŸ>-‘H ´€O©Óéz{{cccCBBêêꂘ˜˜P(Z­– »ÝþèÑ£´´´ððp±XŒ HffæÂ… Z[[ÓÒÒ’““ëëë].—D"áp8aaa---yyyóæÍKKK{òä ‚ Ë–-Û¹sçýû÷ããã—/_^RR’™™™žž–——×ßß»páÂìììÑÑQ“ÉeÌårµµµÅÆÆ2™Ìññq©Tº}ûö7ß|“Á`ÔÖÖ¦§§‡„„477 HRRRFFƽ{÷Ølö‚ òó󇆆pŽŽf³Ù÷ï߇a#¨ µÐëõdªÑCÏYMÍ™YÙûšÑt™T z½Þ¦hÎìä|øæÍ›999¯½öZ^^Ç»}ûvVVÖçŸÎår7lØÀf³oݺ544”ýÎ;ï$&&ÖÕÕåää0™Ì«W¯"òøñc‡“ŸŸ___ÏápÀ¿?***..®¹¹Ùår E1 ƒšB«Õj4Ça Z£Ñh4­V ¥Tç£hÎËà@M§ÑhÔnQ¥ÔP7 G¨munã§9~ä¨ÕjFC¶8Žk4µZ­¥Í™[° ’IúgTöC•/Íñ/‡*½¯Â0 ¬þÕºkuq4në•Édeee¿ÿþ;ǤÝhZÓpìØ1A:;;ÏŸ?æÌp£µZ-Š¢2™L¥R™ÍfNÇårß}÷]¢ˆull¬®®.%%…Íf³X¬E‹EEEUTTðùüÐÐÐ?ü°±±‘ ±X¼zõê   ªªª?þ8 88X >>---??``Ö©éëëËÍ͉‰‰‹‹ËÈÈ(--‹Å;wî,,,ìèè°Z­B¡°¤¤dÏž= eeeYYYùùùÃÃÃb±xÿþýiiiEEE …⫯¾‚~ëÔÔÔM›6õ÷÷›Íf‘H´k×®µk׆……eggoÙ²A³ÙŒã8ŸÏß»wo^^ÞÍ›77oÞœ””TXX(“É ÅŽ;Ö¯_ÑŸ}öÙèèèøøøÞ½{?ùä“#GŽH¥Ò‘‘‘={ö|ùå—]]]‡Çq…B¡P(TnAõ ìm(œ>‰æü7_5&™)ùÄiŽ9J¥rzJxs~¦hÎÜâPùåT|*Ñœ¹Åñ.Oƒ.-q0 Óh4àFCl4Ç¡ÝhZÓõÿÞè .œ9sF$AW´T*EQT§ÓY,•Je0†‡‡ù|þãÇe2ÌÏ38ŽËår•J%‰úúúÀÔ$ Š¢J¥Æø”Je?‚ J¥R$ñù|AL&Š¢}}}ƒƒƒccc&“I­VŽŽ¶··wwwÃgõz=Š¢ããã"‘hppP À·] üN«Õb† Èàà ËåB¡p Ãzy†AЈÍfÓëõ*•J"‘FøŒ¢T*…E÷àUR­V›Íf¥Ri±Xd2¬%¢ÕjaÍðæÁú5 Žã(ŠJ¥RN§T*uî †AÈŠÅbÈ ­Vk0, ä"—Ë•J% Z¡îIE1 ó¬|ÎúhºhÎ rH7EQµÇP£ø3/j÷ðŠ¢Ð AsüÎ (×äN5…hÎÜâ†A%šÊ~¼äKsüÈ¡Jï«<ë|Ò„¼œÏËæ  Ãär¹@ 8yòdeee[[[__íFÓš®€C‡ ÀJ?ýô“P($ß)ª·7%…f‡*=•ò•3ýl½Ÿ¿¿8T÷ê>̶ëòõ¹SÙ•¯ÏŠ3SçC_—wQ]—w»ý÷÷çeŸœBT÷m®pÆÝ’ËåäMð’žæÌN•0wP2i*• Ã0/‡Ì*æx …'Nœ€ A¾þúkO§ÙáþRºÿ\8ZþWÀ÷ß/ {{{/^¼XVV† ˆ\.—J¥TæEÕlP½ÝÎ6ޝÍ!Uµòbï{f3Ç{%ûߟ•|}îTvåës÷RÏÈùÐ×å]Þísºfêþ¼ìóñ5߹™|áçBsüËñþô= ¾ZŽ9ЙˆaØØØ| ¼­­ÏçÓn4­é 8pà@ww÷£G~ýõW膵ިŠ•ÙQ5‡³ó<…ÍSTÕŠ¯l–MÅó•Cufáu͈{çës§âÌÔùÐ×å]T×E¥™º?/û|f›ý¼ ;ôäP¥žûLsþ{Uzèñ%ƒ…Cd—OòG&“I¥R¹\. !6šÇã Ñn4­é 8zô(‚ ]]]åå奥¥ðQ’ññq1…$>j¶qü•/K©T ÿ¶L&›mœ1 Q݇Ùv]¾Šêº|}î3e'/›3ׯËWþLÉ_çCÅ÷µœÎUz*>Íñ/‡*½H$»4a§OòG&“Áz_"‘è‡~¨¬¬„)†´Mkº¾ûî»ÑÑÑÞÞÞŠŠŠÓ§O xQûÇ©!Ï©ÙÆñUT½G¾rÈu‘I©Õj˜ 1«8ÿÐi6M³íº|Õ L¥ò‰ó?ö®ü¹‰c[ó?ç§¼PÀ}`yaIð¢}AIî+ê½â…P‰Ib[ÖbY’%ö}™}ßçÜŽ=QlòUb“è«.Õ¨Õú¦OŸÓÝgz›yåg!×Ù˜ÕnçU>t~fåÿØy楗ÏŸÃ㕞úý‰“£ý¦^é/nUÄׯà¹Ñ[[[ù|~áF/pW>ùä“\.·½½½¾¾Ž‹:ðÝ?^“^“A^æxÙx¼ÒÏ:É5+Ïdjw³Ë|ŽüüÑ<³–ÿ%”k&½{É5«ÞÏ(Ÿ¹äg!×Ù˜UÞYqÙòãUn Ϭ÷òâ_ð\,WOLJ’äù†i.„Ç]ÔQ¯×oÞ¼éx·p£8‰+ׯ_¯Õj™LÆçóݺu+ŸÏ·Z­Z­F̈š>vžªΑŸcÿªV«ç“ëåñ’× —P®Ë¦÷¿ª=rÍ*ï¼Êg^ù¹¨|þÑ<ÓM ÆT*•J¥2ë}<Ëã¥÷S»’s÷/>O½^¯ÕjÍf³\._¿~=§R©Z­¶p£8‰+W¯^ŵÑÁ`ðîÝ»øúÊûôœf}gýEñx¥÷ãYyð54gÇ\Ú^åpÙ䚗̪÷?Únç'Ç0hÕÃNvÂ0Ã0'°œÄr2ËÉ4/ѼDóÍs ËÏXN8=°üÉpø/Ž=ùÉrËsÇ?9;=üVOiöX h–!YdD’IVÀH–#9–ä–cx–YF<¼ûœê»¼ôåU½ð±ðY#Ã0 ¦ÄE^)½!,x.–ÇKïäÑKÜêàF΄‹âa47n„B¡ÅÃNÅ•[·n5›Í@ pçÎN§†a˜¦iÛ¶ã8–eÙ¶=ýÇqlÛ¶,Ë4M4#ÐuýX¼°, œ#ضí~Ežé^=#dYUU5MÓ4¯±PXÃ0ÜRšÎÏ4Ü ¸×Š¢Ìš/yçÅ’$˲¨SÃ0@EW®®]ÑìÓŠDz¬éøyÉuªf1«§Â‹ÿ‚©ª*Š"j5ëª0ÞÕï´º©a®rÇÁ’œ)?Ó¥1`N<Ž$€`:ªŠ’f تf[ˆ 0h‚ ¤ªª&€`Î)×€ff^›Îoîµ`Ø€7=,@ûà`8§ØÎa0ŒßhÞEKÕDHTV“umÀ–À´uÉ”x˶ÁPmÛrË혟T=Æhš6mnü±fjÚn5MS“¡uÙ'ؙڴYmã2ð¸‘øÕ-óc…Geþÿ’/x.„çŒdçã¿<<øk·Û]^^Ùl¶R©¸¯_A6´d·Xàï‰+Ÿ}öY­VÛÙÙ …BKKKív¦|&AF£MÓÇñîÝ»F£ÑjµÜSšÍ&ÎÀ–Ëeœú©×ëõz½q„³÷ÿžDÃóâ©ÏwÞ Ã‡¢(ò<ϲl³Ù, ¥R i …B&“Éårår¹Õjõûýn·Ûl6«Õ*ÎÕëuüZ­V›Í&–á¬ùñ’w^<µZ-—ËaÞÀ¶mš¦ÑËd¦R©‹Eœí""ŸÏ»ziþÓEÝh40æŒòŸU®ápØjµ°`†»ÿÚKï^ü¥R‰ ˆl6‹™4 Ãq]×yž·,‹eÙR©T(\ ÄÃL‚ÀWÊ™Ç\U«ÕR©tpp@D»Ý‡³æç$ð¾sâé6ƒfg\ª”KD¾ÖÚï+’ÊȆ2"¥!mk$Ùú€ ˜÷ööªÍ½Jc¯ÒÈÍB½Sjõ+!ÑíW[Í^©ÕÏ×ÚùZ»Ôê4{ûÕN¡>(ÔÅÚ P”££0Ƚ<Ñ)V:D·Tí•k}¢1¬´•Þ¨ÔÿÐPéM*õQ¥ŠaX&%¢["Ú%¢]ï êAµ;¨vDoPé *½Q¥ß'†ývm·Òûµ8Ú.Q•¡Dª² BoPÀ™Öz-vo¯ös*[lÔ«õZ,j³Z­¢ê …Zšº[þØb/—ËXµOm©\=Ñét°ëußÿªªª¢(Š¢` ¾óÕ…(Š’$ɲŒiTU<ð±ð躮ªª,Ën râ…Ë<àT,x.œÇ §òK3â¢xLÓÔ4ÍqœV«µ´´„n4A§ºÑg s,ðwÀ•O?ý´R©$“ÉH$r÷îÝV«eÛ6EQ’$ š¦@Ó´d2ùìÙ³p8‰DB¡P ðûý@  E"‘X,‡C¡P0 …Bá#„B¡h4êþ ƒÁ ^¸‰ñ¿oÌ‹'8#âñøÊÊÊÊÊÊx<îv»ÃáP–åÁ`°½½ýúõëH$â÷ûÃáp<O$‰DÂçó…B¡X,‹Å"‘–X,óûý~¿‹+ ú|>jÖüxÉ;/žÈ|>¾wÝhUUS©T<_[[ ƒ(ò‹/ü~¿ï4D£QTÙ1¥ÌK.,@̉›mò ÕŸäÅbñx<‰<þ<v»]]×Çã10 ³¿¿ZŽF£±XluuummÍçóƒAŒte áp8†Ãa¿ß¿¾¾Žú5?ni Ð`æÇF_D¢Ï‚ÑH0æÿüñØó@½SíM&´ä° ”[ò7ÿ·÷EèÛ›_†ÿ±¸ŠÝzâÿÇJð?WCn¯…—|Ñ»øÍ¯ü·×ÂwÖ#·×ÂîÅ­/ýË+Ñ»«Oï­<]z»ó8rûQøö£ðÇ¡{+Oï­Äî¯Æ?_{ú_ë‰þGÁÂ__°þÙÃÕ ×¬? ½|xõÈ÷òáúók_¬=½¿¿·¹ùÅêÍ/VoM$XµQ/xÕíµ0mí¡PÈUГ'OÀÆÆF­VÃ]×õiÿÒ]È4=ÕŽ1<Ï»Š×¤üǃ"ãx„ˉ_9ŽCNüïÙ+<Îã…SùùqQ<èy†Ñh4îܹø½íÎÆ,Üèà˜½¼¼Ül6MÓœL&<Ïã 4MÓétúÕ«Wßÿ}§Ó¡v¼º{]q!îǸdj2™PåÖ4Œq÷öRåþÝ]n…´^ëÌ‹Çkí—šÍæþþ~<§( eu]ßÛÛ{ùòåææf³ÙÄZ7Úí6~Å|âʪéû‡C’$eY–e™$I<û}ÖüxÉ;/žZ­F’äÖÖÖãÇG£H’ÔívüñÇ­­­r¹<išÆW]ºgÚS…ë;‘ʽ‹{"I’¸.m^r Š¢°´G£Q¿ßǸ íTxñ·Z­^¯×ï÷³Ùìúúú`0UU)ŠÚÜÜüá‡J¥R»Ý  ÃPåÞ[aÌùp8ä8 išfYù'ÞE!ï1 Ƕ~Nò\dvJßËwÑ×É_ !ÝÕò£$ØÛ}:C)iZ˱F–5ö=Ãè9ÖØãÌ røÉ†ôQ‚é™(?·ÚšOõõ ¯~iÿÇã:'±–\n쯮=|ß ûœÄ9ý.âî¼(`ëGQ”(Šhä²,£~G£>Ò»€KÐBF£Ñp8Dkœî¶%I¦UÙív766ªÕª¦iº®#ù´mHÛÅ{àcááOä>•ÙMö-<Âã•Ø‹ÿ|øóy°ý4M³Õj-//ûýþt:}pppÌFzáFÿÍq¸6:™LâÚèV«ÇI’D’d.—{ûöm&“EQUÕv»-‚kg^;º ÃÀQ±kwûþèùŸêcÜ9Í3êð¼xfDù|¾z½nÛöîîî›7or¹Üx<æ8ûEÃ0 ÃP…ã8,\xl®Ç4MätŸìÏ—ÕyÉ{*nÅ( ‰D‚çùt:ý믿þòË/,Ëš¦iI’Ýn×u Ž©ÞqÃ0¦ãMÓDŸƒã¸³³:“\–eñc8r Ï±Ýv¿®\½z• Üb¸¼¼Œ[ 9Ž€d2Çß½{×ï÷ѶpÄ‹ã8œ©Ý ì°«@ÃÂdçððΞ”ù÷yf…(Š4MÇb±b±ˆÃHïß¿‡¸h¸ßï÷z½é^V×u]× ÃÀ prKIÓ4þÈ5çyžeÙYó3/y½xÀ4Íýýý`0˜Ïç‰D2™ÇÓ xB ŽIe€"»¿âØ3¶8h 8©=/¹ðYŽçyEQLÓ4MóŒ}Ngðkš†S+Eù|¾l6›H$=z´½½M’¤mÛîP4#ѱFå¢Á£6•£dYÆaéYó3k9ÌÊ£)º¢h Ëf-_z²ös©þEâ›owÒ#}³!¾)’E J ¼«³?Ô©9ZÏ’ê>c”%¨ÈPäí=JË’jz,gIµÀYί{µDù±’틹‘r@ÛìÓNjl¢¦!EA’‚vhØe`—„Ô,!ÍtwØf`›-¶hئ~ ;ìÐv^±~êÒïûÎÛº³±5¸ºòíÿf ¡o_f+{~ ˜‘"ñ†`‚¥›†»›J˜Z0êÚ¶išnwC’ªªx´‘;ÑÄy Ъª:ŸœðJm‚'™ž*wj8¥B\½wßyçüî9¿{ÞsB€ø¿Sþõ<§“ødóOüÄÝÖóRô¼xØÿBÿ?ˆ¸[#‘ÈÊÊJggç7¸\®T*݆ÑÛ²Y^9wîœB¡`³Ùð0p€Ñn·[©T ôöö.--¡`4@@ŸÏ …ü~¿Õj•J¥³³³ssscccL&S¥RA®ú9°ù6Ž·WâÃÿLüz~îªÃ0“ÉÔÓÓ3>>>33sûöm³Ù EvŸÍf³@ —J¥.— ¤V«]XX˜˜˜Édf³ Ð$¤îñlu¾ÿWzÜnw8Öh4×®]c2™ÝÝÝz½ÇqX6@ÁÚëõ“! ±ÙìÙÙÙ©©©‰‰‰©©©ùùy>Ÿ/‘H&''¹\®V«z˜Í Î÷çžd1 3 |>_(“{«ëþýÇ·ß~;??›A=äÑQÇÀ%. IDAT …B†9N¨9Øíö¥¥%&“922233|ñP(¤R©fggE"‘Õj… þ ²ã[O¼‹÷þ„RÃÏÕc·:üþ õ™]èŸÿrëìÅK½süó_u/yq¶%$ôàR/>k ±,‘E.qãgħ¸½ÓÂ’£u,Z½‹ŸÈäö͉9ŠI©nR²<È]ꛕôÏ‹ûg¦¹™¼Q¡Š«·ËA™3,²‡DŽˆÐ:p¾ç9q>‚ \¸Ð ¸Èû‰/¡3"õÄDžu‘'*pÇ8®ÁÙ>ïüL³]?ôrÒ®Ç[A8ˆ?à¹wµïáïòÍ+N£/ˆúѲêñ"뵨Ùj. ÆÆFø|¾ÑhŒ7WE'''gggY,›Íæp8‹‹‹f³ymmÍn·+й¹9&“)‰à‹k›Z[ó%©k×®ÁfnXnwÝ,/pY[…ÿ_ŠwÜ>Åxµèß’ˆ·Ò¿­çåêÙjüVú7Ëåeéb^8&`4ÇÛ†ÑÛò£ò <¹pnn®§§çÂ… ЉI$]½zµ··—Ëåb¼^¿ßïÞà´%Z­V P©Ô††:¾oß¾¢¢¢O?ýthhZ|€Àƨ~†Ãaè¿F!—éõz`4q‡g41|Ì4‹ƒÃá°Á`8 ,‚x³†ÃÃ7Z ãM…âñx0  ƒã¸ÝngY`à|=@ÆË—/_ºtéþýûb$‚ Pù…ñF£‘B¡äæææçç8pÚo †S§NíÚµ«°°ðàÁƒf³p'!$ø- 1Wļ9 …ƒAh\m³ÙT* ƒƒ‡óòlI€ï ®p,s8„N (žNü †aÑháåË—ïܹ£Ñh í Ä @Ã0]‚X,ƒA£Ñrrròóó333[ZZNž<922RYYI"‘Z[[õz=0É Ÿ‹ÅâI0“Ï%0­}>œL °ÛíÄ`­V[QQQWWÇçó€iǤ¡剓u¹\(Š®­­E"¶¯\¹òðáÃ……£ÑHP)`éó† H$yöìYsssZZZsssCCCQQÑÔÔ”Ùl …MMMd2ª7ð»ñ'  +%æÚ¿À¥‡+îv»á+‡ †˜·`0èp8€.Þ“P([„áVuoì©ÿÁ°Q Aܘ/¬5Ù¿ºõàôŸ.ý¹wxD¾ÊC"lg„çÁ¹.œe =8ÇŽÊÌm§÷aVaQ9µ°¼V±ì°xBÛÚ“±¹š†ö=ùuÍ'§Nœùí®ß>"•ÒRRS¿LKÉ-¤;õt–Ë×™¥&ç¢ÙcðáÓr“ÞK‘Èü2*óàBûºØš×”î¨Y[|b‹oAC¸Äê—XýzÎYvC¸È¼&¶ø¤¶€ ËœkB+²„Å.ÿ Ž*Wµ1œiö/¸pxýÀè@p¾3ÆC"lÄ¿àŽp=ø¬¿Ã²ŸúòÎÕq–Já ºcx8ˆ"«Ÿ;òã\>çß/ý[uu…BéïïB3Žã°Ù”L&×ÖÖæååUTTP(NFÍfs[[ÛÇL"‘ª««!Çl³ÙÐ\0ÿà^\.x¶žž™LVŠaáIà.soPÞ‰¥Ñf=èòKÑC,íâM”øFz7Zª¹·æànëy¹z¶’­â ñ‹ñëÌÄß—¥Þ¬¯¯ †ÎÎΞž‰D¢Ñhˆ†w 2Ù쇷åÿ¼òùçŸ+ >ŸçÎ/¾øB£ÑX,–ˆÅb½^oµZ!Sb³Ù º‡1 Ãq\­V9r$99¹¨¨h``ÀjµÎÌÌP©Ô¤¤¤ãÇCïd‰DH% òù|ˆ1V«U£Ñ,..Z­ÖÅÅE†Y,…Ba±XPh«V«Y,Öêê*ômU(°ÇQ¡PÚãñ˜ÍfµZm2™ …\.W*•~¿ßd2éõzhém­á9ç@xÛ8Ž‹Åb¡Pèr¹ ˜=‡9ÀeÀ~£;wî°Ùl‹Å ÐdüºB£Ñ”——ïÙ³'11±  `xxØáp<|ø°±±1!!áÍ7ßÌËË[ZZòx<ð|Q‘HÄb±  u³Ù¼´´ÄçóÃá°X,‹ÅËËË@›­®®ÂµXYY‹ÅG¯×›ÍfƒÁJ¥RhM-‘H •µ^¯‡ü±s£}¡\.‡Œ8x¢;B¼^/P2–——oݺÅf³Q…‡’Ë÷ødƒËåÒétûöíKLL¬®®ž˜˜J¥:.''gçÎÍÍÍr¹\«Õ*•J€Åëëë«««z½^*•ŠD"Ø´—^ ,--iµZèÅ 3/‘H‘ÉdjµZ$Á…6aR©”ËåêtºÂ½{÷>}ú üL&³X,.— È@ žp¯D9Ã0A›Þ¸qC$ÁP\e8S€§}}}•••©©©Ož<=~üøñãǵZíØØXzzú®]»T*t2Y^^–Éd<0“Ïç”o4 LÂòò²×ëu:‘Hò ð/‰DÜnw0\]]Õét"‘H­VFèL—Ã`0°X,©T*‹ xâzQÄç]C·/]¶aWn=úíþå1OÅC" î sÑÇe9Ö…œkMHÍÕum¿zýíߤç”í¯œÔ¨¬yÁ½»öHKÍ*,Øïî#rMÃ{ïî.-©ê2$Uh†¦˜û«k_ýÕÅ•d–dÉá°U£ñÌ¢f˜+›“΀Ðèâ= g€«1?:Ë’é¦r¥Å£Gü³õO6Ì霾Á9Á“+Y¶™°u=â—¬|¾wŠ9‹eçx»g÷-®á<$ÿâ#¡3ÂGÂsvlÞb#8Ë/ÄþÐ=4$ÕX×CV̆®!¨s[° †ãQ܃¹•ÒþþÇ$©¾¾^§ÓÁ*ŽÇãQ©ÔœœœÎÎÎüüüÄÄÄ]»víß¿zzÚãñŒŒŒ”––&''ïÞ½»´´T¡P€BlFQÒZðÞétÕÕÕëׯ‹D"XKC­ÚÎÀ] ¨”(©ý¨ž­àË/EO|š0ÞD‰LaülëšÌ¶ž—«g«ñ[ÅÏ߆W÷Fº­âïËÒÃÝÝÝ-  Å6ŒÞ–Íòʹsçd2ŸÏ¿}ûöÅ‹WVVVWW»ºº, lQ‡(PµZ­Dªµ¼¼üÕW_-))™žžf³Ù~¿_£Ñ@³¥RI§Ókjj„B!†a2™ŒF£µ¶¶~ÿý÷'NœÈÎÎ>|øð—_~yìØ1:~óæM@Ì £¶¶vbb" R©ÔcÇŽÝ»wïÌ™3ééé c||üñãÇgÏž¥P(·oßV*•6›mddäÌ™36„B¡ÌÏÏËåòÚÚÚ¬¬¬ÜÜܺººÆÆÆ¼¼¼£GêõzØ=i4Oœ8ÑÐÐ ‘H$àÎçåƒá»ï¾Óét8Ž{7ZN¢(<•_¥RY\\œ““³gÏžC‡ŽŽÒh´äääÄÄÄ”””ŠŠ ¥R966väÈ‘ŒŒ 2™L§Ó©T*0™Lf{{{SSSww÷©S§233©Têðððèèh{{{IIɳgÏt:Édb±X4­®®®ººúÀæÔjuSSSvvvII ƒÁ P(éééuuu p™u:]sssyyùìì,ð;‰9áVÀ×ÀÎH•Ju÷îÝ•••h4êÚxR1º‘“ }ǵZíÞ½{wìØA¡P`¥¤Óé²²²Þyç …rùòåóçÏÓh´¾¾>ǧ§§[ZZ222jjj>ûì3‘H‡Y,VSSSkk+™LÎÉÉéèèxòä‰Ûí^XXhoo§R©ÅÅÅt:½¾¾~ffÇq÷àÁƒO>ùäôéÓ_ýunnnuuõÐÐÃá`2™ÅÅÅ4­²²2--íØ±cÓÓÓPâðz½ÀÏF7Ap8l2™º»»¡á$' GH4ê…B&“©±±±¸¸¸¯¯4Øív¹\¾¼¼ŒaØÜ܉D*++Ã0l||¼£££¶¶–L&·´´Ðéôááah øèÑ#VVVVSS“ššÚÒÒ266f\WWWZZZZZzôèÑÊÊÊþþ~Em6ÛãÇëêêh4ZUUUCCà C(ºÿ~UUNg0õõõ---½½½P»ˆñA‹yl6ˆj-èÅoîþá¿î±ÌA‰g{"ó®  s<¡G@äÁ¶Ø´ÄD&7&ýsRrBrê©ÍõÍSÃS¿?ûû¬´¬„7Òw§W•V±¦X‡(‡~ýO¿.È.ÐÈÕJ¥Z,•0Ž}7)17¿€+ß}øèã’²½U‡ÚOä—Wí¯mèq†p¦P>0Í:ò/göUÕR›”×Ò®Þºkv¯M²ùô#Ç›Ž~zñ?¾:ñ¯gIûÊÛ>=}¿Њú'æê[[Ò¨ÔÇ+èM¥ôÖQ‰Jê Í¡3ÌG"¯° ŠœA!à¹} î0ËŽÏ[ñ¿òÜìd¬ŽÛ¼v‡Ëâ¶{ÖœŸ;ê÷F"±h_g2§ÒÒÒ233m6Ô8NvvvJJÊØØXUUUfffVVÖ[o½U__?<<ÜÖÖöî»ï&%%%%%•••Ásšˆ"U| EQ(¼À^ ÜPƒì˜¥g#OÏÌÙ¬g+øòKÑtûŒÇjñ˜¸U?*Ûz^®ž­Æo_âB´‡¿›ãïËÒŒ†-†|>_&“mÃèmÙ,¯œ?^¡Pðx¼®®® .Øív·Û}ëÖ-•Jí&ˆÛ, ";ÆB¡@ hkkûðÃßx㜜œC‡MNNŠÅbFãr¹–––H$RQQ›ÍÆqÜ`0|ôÑG………ƒƒƒõõõ;wîܹsç™3gººº8ðÁœ>}º»»;''§¨¨ˆÏçs¹ÜÂÂÂ;v0Œëׯ—””¼÷Þ{$©£££««+###--Á` ݼysxxxrr’Åbuvvfff–•• …ÂÔÔÔ·ß~ûèÑ£“““555 @ÀápôõõeffݳgÏþýû¥R)ŽãPR'ÇnÃ0`H$’žž«ÕFaAEÐ= ïéõzaÒ4M~~þûï¿ÿßì]{P“W›w¶3;Óv¦¶^j±( 5@€Ü/oî!á.± WÅE,ÚÚNwvºív·ËvíL·«»ÕO¥ŠÂàW@ È-!„KB—€ !DÐ@îHˆy÷ó™akñ_§3œá¯xÞ_rÎy~çy~Ïï)((˜œœ„aX­VS©TA .^¼xýúu$ùá‡J$’ÁÁA …APWW—F£±Ùl‰„Ëå"ˆ††¹\žœœž™™Éçóøá©TÊçóÅbñ7ÂÂÂÒÒÒ:;;KKK#""bbb~üñÇÏ?ÿ@Ðh´ÁÁA™L&"‘(---22ò‹/¾xòä‰_-Ñï Ìf3•"ENWQQ¢Å&“ i@Ýü·Û=??Ïf³£££ÛÚÚ666,  Ž€¼„P(d0µ¶¶^½zõ—_~Q©T]]]ååå( |ér¹üĉâþýûµµµ©©©111gΜ‘H$±±±UUU--- ™™™Ÿ|òÉ£G.]ºM$…B¡@  áÓßß_TT„F£+**äryMMMzzúùóç3{«K³méàåtØ †5Ç<·ìúê¿î}u­Niƒe&Xbñ˜]r»[nuÉ Î ó³Ñß<}ʧé§b?ˆÂ#щŠE•WÜUŒOöK‡¨,nÊÉì6aï…/þéÂgÿØÖÕÝÜÖþÓrˆÎx?èHNAa‡ ')5-äØçJ/\½vƒ›Ä;|äXÞé3Q|bBL ò?þó[á@_EM-3-ãó¥}Ó Ê5÷V=ºö7=¶¶®´oŒØ½’X² W‹—þåÖÃa½Á û ŽU«Ãèvx6í>»Ñm4Ø.§Þ”Éd€±ÃçóFãÜÜ\cc#‡ËÉÉ‘J¥8îèÑ£‰D4-p8™LNMMŒŒd³ÙಠêPýûÅf³ù‰òn·{yyùÎ;J¥†a‡Ãáwíà ò?²5úû¢—Œ?…­ä~·VÁ né`ÎKàÚŽWkg»±ñ;V¿ªÿ?ð‡þ÷Ú¹YÐÅÐ/x·£wÆ‹cWYY™N§¿}ûvYY™Á`0™L\ÉpÚív ì'›Êd²[·nÑéô   ¨¨(—––öÕW_------‘Éd …ÒÖÖðN§C"‘T*U&“*H}}ýÐÐÇ;xð ‹Åº{÷®J¥b2™I¥ÒG„„„ ¸WPPpøðaöðáé©)±gϞ¹¹9‘H499ùé§ŸâñøãljDƒQ[[K&“²T(N§³µµ•D"Éåò .p¹Ü¦¦&AŤõÿ ^šÍf "\SS$ÀL~a`‹Åtßt:‡Ã e0ß|óMjjjww7‹ÅºråÊÍ›7Q(‹•J¥'88877wffÆ`0ðx¼ÈÈH™L¦P(bcc¹\nmmíüü|qqñÁƒ!jnnE£Ñ‡JIIinnÆãñÑÑÑ ¬%—ËÑh4¯¯¯Çb±EEE Î£GP(‰¼xñbKKKQQQ\\ÜÇA= Ãàgh0ƒO7÷ñãÇ÷îÝRY‚°ÙÖÌ,8ƒàçw¤¢¢"•JÃðìì,AaaaÉÉɵµµ†D"‘H$>Ÿ/‘HbbbH$èèr¹Äb1‡Ã9qâÄüüüÂÂÇ{óÍ7!R«Õ### qƒÁˆ%)))uuu)))ûöíãp8*•ªººšD"Ñh4±XÜÓÓ–­Ñh´Z­X, …àB¸U/űE7Úf³y<ž•••êêjð²  äa@XÀeFVÀ@€»l{®ÓÂçóI$R\\ Õ †‰‰‰œœœøøø:®R©ÚÛÛ£¢¢D^^è.R©ŒŠŠ ýî»ï ài¤¦¦îÞ½›Çãéõúééi2™˜——W__ŽB¡>ûì3…Báp8†‡‡e2X~R ù¹°7àÊo¬;­V»û¼`†¿þï¿~ý—ÆéMxÀðLbñ˜r»sÈj6Ø&ÍžÉå ñ¸îdf^è¡‘rë§© É-¿>ÌHþ0Žsãg G'Q;[ÚK· 9ž–”Êã0錣GBãZ6³Ùl #•J-V»zv¢0°8Òµ¶¯°£“¯PŒž;w..Ž‹Çã##‘999ÝÝ= †Ã‰ohhP©fòòò^ýM<ÿàÁ¯8,šBÄ1i¤o¾ùZ9== PÖwõOüæ-XF×¼Š5àCƒhô¨qcÔ¸>j²Z܃Ë>Å\7¸üíÏÍ3—† ¶å ·öÂ^ì4o:m›÷ƳuƒÁÐÜÜL"‘ŠŠŠ„BáÅ‹ÁÅéÁƒaÿþý ·nÝJOOommÍÌ̼zõêÍ›71 ‹Ñhÿºk ¨Ö‰èí`4ø'ÐÑÐúü¦çG/ÚÙ¾üYì<{ö )[W)Økààl@ !øÒÈŽWkg»ùÛù—­¾Õü\j ¬?ô¿¯Êpô^¯W«Õ^¾|´_ÙÚ Œ½3` ­ÑhÆÇÇ+++/_¾ :o•——}†ÍÍM€ÁÞ}UÀ‡ày…BQRRÂb±fݽ{7—ËEëh4:99Y,Ã0¼¸¸AF‰DAAAH$R¯×«Õj‰´wïÞÂÂB›ÍèL&³­­­»»›F£Q(@}>uêÔk¯½F¥Rgggu:‰DŠŠŠÊÊÊZXXp¹\===H$‡Ãáñxú­««KNN~ÿý÷óóóõz= ÃsssT*‡Ã ‚ŽŽ4M§Ó5 U`ûm½jƒ­åõzM&Smmíôô4(¿ÛØØðË•€cÈétj4:XXXØÜÜœŸŸßÔÔ”‘‘!‰Äb1‹ÅJHHH$H$2 €Åb•——×ÕÕáñx.—ÛÝÝÝ××G  •j</88˜J¥NMM---a±Ø#GŽ\¸pÜ:0 ¦ËåEGG×××ÓéôÈÈÈââboÖét ãí·ßNLL¬©©A"‘D"qrr¤›=ÏvÇ ¨Pœ™™©®®^]]õù| ­i/«Õ œâÚÚ :x‘žž~ûÄÄ…By÷ÝwKJJæææÔj5HJJššš![½^*íz{{cbb0 h)Ÿ‘‘qèÐ! ¥Õj½^oWW™LÞ³g‹%$©½½½   <<<11Q¥R©ÕêøøøÄÄÄŽŽµZ]RRŠÁ`Z[[›››‡‡‡ý剠tukï? ¼çêêjPçêõz- x_0Ç£±X,“ÉT( |ü`öx<¬Þ™™™ÕÕUµZ}ýúõ£Gb±X‰D¡Pº»»‡††ŠŠŠÂÃÃCBBîܹÓßß?66ÃðÊÊJiiixxø šÜÜÜœÍÌÌ $‰ ;‘H''§¯¯O*•Òét<.Û–ç?ÜôKËÃÛ:\.˜³n‚¯ïíl_þ,v|>ßæææVÄc}ïÀ'N§Óëõ1“—À»;¯ÖÎvó_[ýÓ@BÃ_ô¿¯ÊÐ-Ø £AiÊŒÞ/Ž]_~ù%(n«ªªúþûï——— Ceeåìì, Ã6› (fø‹êÀ|úô) `Z­Öééi¥RÉf³¹\nTTÔž={222„B!•J¥P(B¡ÐçóŽŽR©T‚:;;,HLL\\\œ˜˜HHH8vìXJJ ¨½#‰t:}``@&“Ñh´˜˜­V«R©²³³÷íÛÇår?~ŒÁ`BBB¸\îãÇÛÛÛét:@hkkkll(M$±X¬×_=++K§ÓY,…BÇãA¨[,£P("‘¸+ zçÏûøý„ÙlÑ£ÊÊJ¥R r÷þÄ–Óé0Ën·C@@@VVVcccrrò•+Wòóó¥R©@ Àb±H$²££#))éøñãáááùùùgΜ¡Ñh)))]]]½½½@hµZ­V{éÒ¥½{÷’ÉdPOÆb±ÒÓÓëëëÑh4A .S(¢P(*•*—Ë£¢¢öîÝ›=33Ãðôô4‡ LIIáóùT*•ÍfkµZdz±±4C@` ¼þÖœx¼ªªJ¯×û|>PîéO–ùE¯Ád2™ž>}š””œ‘‘jéžýÎ;ï°Ùì••™LF¡P>œ””ÔÔÔtòäÉÊÊJ&“)‰är9ƒÁ ÓéóóóþVþÈ+ØeÀ»oWbü·'¾ýýíàËŸÅßqlé¨å7îlØ— vì¼Z;ÛÍ·nã_ÌÏÕZl[’€A÷‡þ÷UÙq: NïçFŒŒ¨Tª½3^»Êþý²ú‰V¡½w¯ª¬¬l~~~eeåþýû VÌnwšLÇc4WWs:íf³Ù¹îÚÜÜôÁðÄÄÄcÕÔââ¢F;÷ôéÓæÖ–ìììCA‡cccù݂ȈÏç»6Ö{{{)4*™LnëhÏÉÉ xÿÿØ·Ò ¶®,ÝÿgjfªâÄvŒí$^À „Bl›ZHìf±“Il'i§Òw25©N¦žîdz¦;=N¼A¼ÅNl³ƒHÄj’ í¤·¿§mo~àrõŒí_]SSSéS_½§Î«sÎýÞ=uß¹¯3 ­^‚`^AþÞ½{›šO »ÓQVVv<)qrr²·¿N§—•sVWW]ën.—,Á`,–ìv{N.s×®]õ 2™¬®¡žB¡L¨U‚ô”––¦¤¥vvv²Yû÷ï×HVVV"±¨Ó鬬efföô÷÷çä—””¬ZÌ‘HÄx! ‚ E0 Ãðíg«Ývûöm§ÛE’$Fà‚ Š¢(î#@DqŒ óš%??ßþ8±X<(“Òh´œ\fbb¢B9ªV«iYô²²²G]l6;îÀþŠŠ •F­Ñh&Ô*¥R mÉd²B6‹Ífkõ:AXEìC‡åæç----è´eee»^{•Ïç«4êìììŒLªÑhŒ‘äðð03/·  àî½¶ß^[[k0I’tº]………‰ÉI\.÷ʵ« …Ë«0›Íã8£È6Â0Œâ†a#‚x$‰‘¤Ñh¼ÑÑn·ÛcÛ¨1t;3(ŠÂ(ACÛöÁ`P·¨ÏÉÉÙ·?®¦¦Æ¸l ‡Ãv§####þXBmmíÜüc§Ó™ÅȦR©Ó³3|F[³YI’ †¶úûû X…iii“ÓS 3s³½½½ýƒz½^\#¡R©?Ü¿g2™ŒË¦ÂÂÂVaOOO]Cýróó,Ë蘒Á`”Wpçææ.§N§ÓLMJ¥RV›N§ïÛWUUe±®…Ãa_ÀÃ0C(ŠbNŠc0 ã>bkkËæ°···{A€$Éíx}?‚ Ûñâ>"¯Ù¬"‘ˆ’‘ÞÝÝmw:B¡Ð†gÓét—M›››*:7777?O¡P*…{öìaI¥R»ÓA§ÓiYô‰‰ Cõz½tH¦P( Ù, …’˜œÄãñV-æ•••¡‘áååeÙð›ÍN8~ŒÏçH«««&Ä—––þôðÁÒÒÒ¸jâþýûêI ÃæÕjµr|¬¹¹9éDòÁƒy¾Á`ˆ‘䳕ŒûˆíHaB@"¸åÁ#V€üârÏù¯î/¤êél´ozm€ýKO|Z;GÔp41YXÚÕ?Lg²²rÙ{öT×?ê•q…’tzîÈØ¤¤¡õõƒGò‹ÊgµF÷8;§«­;ùÚî8FN¾Z3SXPL§å\½Ò>?¯Õk ££cý½Rí¼®¹©%9ñ“‘;<<<¥™f±XGc*6›}")e~~^¯]lkk;wÉdÎÍ<žTkT£#KÚÇtæ³JQ²ŠDM*ÓºÖÚ>~®“Íè<˜paò®ÒüÙŸ~ôDI4öÀ²áC‘ Ð Ž‡¸ð:Ý®êêjZý믿×HöïßÏðÍfóšÍÊb±^yu—X,V(Góòò’SN ËGäryF&•Á`¬˜Wý~(~ö yÆ, ÃŒF£ÀÛÞÞ¾hX"IÒ l×hûKòç¬Äq|››/ô³ÍÊçñÿŠŸ—¬Oè™ Œ"xAð`hëeþwüüïúy™ýËöAž­m ‚0ô²ý÷ÚŒ"(ŽaŽâØÓ­ ·‡6‹DbN§ûw¿ûúúõöÙÙÇzýÎÃyüìÓO¿4lÓÓºë7n~ñŇmÝí¼~õÚ²q…Œ‘LxF0ÂÃ(€ûÂ{  ,¯­\úͯ³r³EÕ•C£ÃVûšbb4'—±;nOyG¦ªo¬;‘žróö÷ß^ÿ®¹õd&ƒ–œ’Ô=ÐSÊ)Ù÷F%#mÍiu­;i9ô¿þ›¿ª”ˆÈ»´b eeR³3‡åCR¹ŒJË`æêµ«6s•X´ï¸Ü|¦Õe³XÍÔì̸ûj›êeâzIrJÒ¨JiZ1>è~˜_˜—SÀììKLM:|ô¸^²¼jÚ7ƒ¡@W_/ƒ™Ã*)M£d¤¤gÌÎÍo‚¾­‚â†?‡(î#ln{ÇÍvê1 „â„Ã`3 nz7@ ò}kN+ƒ™ý·»þ®¨„­™Tò;RÁçj ºž¾î´LJvN–|\!ª®|ýà>¡H0¦” ”ñ8ç>8kq¬)ÇG§$2órÌv‹Ýi+p_Û›–I±9¬K+†ìœ¬W_­ZR5¤ E•Ç’O(Ç&,vÇ¿ù#§¹¥­O*ËÊÎ9xè°@(²¹Ü8áwozä %WÀ?|ôHZ&…™—£Tùƒ>C‘-/  áðG %ü>(l¶X¿½vÝîpaþŒ`á{I~p°:]É'R_Ù½‡/¨tmlÆHreÍJͤïÞWS[os¹­6GN~ÁñÄä1µfB¥IÍ ¦¤RLfKŒ$ƒ‘è„JÃáñsó F'T½}ͧNSÒ©_üå¨r¼œ/`±‹;nßq8Ýò±qj&½ ¨xP:Ôúö;Ç“3èYßüéò;gξùÖáÌl†J=ùX§}÷Ì{åî÷ï^í¸–_˜·ï¸RNÉÂ’6 [>ƒ½!àv¼ Á(b/@ØÜöëí׬.[8‚ „/< ¥œÃ/?¸¢’§•Êe¥œ’sçß7˜–Æ'' XùéôŒÅ0WXñêî]U5åøXW_~+;7ol\µ°¸tá~YÌ)ø¨ëö½ûi”ŒWvï©×tõõÿâÂÅbNYgw×ôüÌŽë©Ô´’²â^iß{gßOJ(düðàÞ¢A_Ä)æ y¿ºô¹thð̇gÙÅ,¥zL=©úä³OÓ©žˆo0-…Èð‹ë‹Ã†z`Ìë#.ßg|ðÉ7s9“j˜œ€"*00g‘ÈÒ¸üªoO’”’‘žÉ­kl=ðæÑ ííwßWŒi(ÔìäTê€LÁ¯”ì{3· øÇ‡=:½ixd,B;r4±ªºîÇŸºêZÒ3²¾ýî†ÕæU¨DU5ç~¡§{à£óN$SÊ9ü®î^å¨*‡™·ûµ}œòЉñÉLZ#;o~A73=_T\zè­øâ’²G{ZÛNKjê®¶wÌ/¯ß¹O¡ó›N?TL-!½'¤õF柎G“O<@r#'¼‘9„œõ4öü×»+O ?Š`(Š `(ˆ¡ðÓ^C!ö"€fJ‘EM8(áp:•22&GqÄh61˜Ù»ö¾Êå•÷Éú›š_Û·»²J(W(”òtzFÂñx“e9FF=°÷eù÷ˆ5§õêõ+‹ËK12ŠpŒ@| }!ïvpwð/FÄQ‡·u›nŠ#(Ž~ ì„ÝëžÏ¿øMÇ÷w§§uF“åâÅOþ¼iÞ¾J´ÓFÿ?—Ÿ]¼ðÏKzÛô”¶ýÆ­K—.=YwÞÍ[7;Ö–-dŒÄŸØBQÔï'P ðQ‡Ö7œ„ĶœnÇ•k—«Ä"ž \Pɯ éÙ´Æ“uƒ²¾½r¹¾±†/äýö«/ûÕ¿ðå\^ù°\úáùŽ'Å‹kªõK k6K]ƒ„JËhnm4­W-&·L\#ÒL©•ãr¾'ñ/Ì9\Ö¦æÆ©‰\^ùªÅd\6•&§$z»ÅlÞ¼ÝÎT´´5qyå’Ú*3›/äˇÊʋө”úÆšyíc‡bdÔá²—rÊâ%¼ñ֛ձź#I0àZwcþBõ׺ýÖ›æµåÅ#P‡.;FÀäE0pÃódÑ åpËŽ&:÷ÁY«Ý<4"ûøÂùÞþñš×V…"žP$°;׺z:ß=óö‰ÔäRNQ]C­PÄëîíÚô®(†• Mõ3s“ºEíGx$þ°PÄ[ßp›V–J9% Ç|ôñù5›E¡å µõunyy·J\}ëÎm§ÛURVJ¥ežli6˜ŒÁÐV$5˜Œ’Úš£ GvïÝÕÐT¿dÔá> B¼#ˆèv,ÿÑp4#I›ÃÞþ}džg3 ~Ÿ/àY~žlnXí¶Ê*Q#»¥­uA§…xÍf­àórr™gß?÷xa~fn¶©ù¤¸F¢žÔŒŽ)ëjëëV-æP$#É`hK:$ËÍÏ×H*ø¼¼‚|qäQW§†å#|OÀç Í­-´,º¨ºJ·¨w?YÿöÊw'[šÏœ;ûûÿ·âÒ’ÖSmƒ2)Šcý$©­ª–T—²Ìì÷ξs÷ÞE¶üAÅ ~VGÂ~4 mxÜ·ïÞz²éŠ‘ÑmK‡žGÐwûî÷-mÍ©”ä’²b¡ˆÇTôôuúƒ>帜]Ì*åÙÖ®ž‡m§[S))|¡ ¾±!;‡ÑÔ|rnþ1Fà=×H•BQu«ˆÍá–k¦&7<›?>øI(ª,d*+JÊŠÅ5"éÐ`(°;m}Ý’Zq)§¨ZR%¨¬èì~´¼ja`D!Tò[Úš*«„y9m§[ûº1 †|/¬/âC1¿ üÈiÞÜúõ凟]îÖ!äFNC¤Œ©€°Ï ±˜Ôl„Õ6¸åÌÇIi4v OoZëT¼söüïÿðű12ª©il«’4uŒœþûèLVz&³¶é3¿„UÊËf²¹BÉèø´Õå¹s¯³¾ùmaU½PÜÏâ°Ëø?>ìÃQÍ´öÆ­û¹Åâú“ 'O¥R³ ‹8§Þ=×Ý'T×r**g×ìëï}ð-;é&çäãš|—UÁ/Vsëù ­÷dJ·Ÿ´â±EOPï -<í¤ÉYœÈ)ˆœDÉ1ot!ç¼dï”ëó?Üw±­(‰áœ€  A8á8Ž8¸Ñ-j…"•–‘NMmhªw¸¬12ºhЕ°'ÅŸ}ÿÌòªáηþÑû#Šañjõ '[jëkìεp4ô"f=Å(YßpvÜl_µ˜bd4 ¶ü/·G_Æ»ÝÑý !p؇>SÄ÷”n8îózAÁp,àrn|þ«/¿ï¸;7»h^µï´Ñ;ò¼üìâ/þiI·<=9×q£ýËKŸo®Û!Ï“»7ÛÍ!/Œ@(àñb(æ‰Eƒ¢ˆbI†ÃÿüÂÌðÈ€TÖ7(í‘Êú¦¦'bdÈçG`Ä»¼²øðÑ}ýâc—Û¦ž™ÕxuƒQ'•õʲM+ö+FeʱÍä{`Ä3¡îÀ ×-ê‘úöŸì]Ëo$Çy×ø ˆ 9çä'NÀAøb ÑÅF. É!#²äD°¤HBr±ãr¤•Vû’ä]­µÒ.¹»$—äòµ|írÈ>çÙ¯z?»«»º»r¨mh—¹èÊÂ…žîêªïû}¿¯Q]Ý3#ñòÊÂù©ù…“KòÓ÷fN=n,c’$`´²º895þáGWff'Î=X^™âáÜüƒ¹ùéݽ-ç¬É%¡ ÝÙùïÿÁW¾òkßøÆ}òÉMŒ!1ÆÐÚœô P  ¯\½xp¸[Õ9e€P€IŒI"‘Šš\r+ææ§§gî/,ÎG'RÑյŪÎ0B8¾ù‹«µËOº­Ý͉Éñ¹ù“ãç¦òBWµŽºç¦«JSÆÑÚúÒÜüôÄä€!ãp~afzæ~»³Ã8B8¾ÿîääý±±ÛÆúöö–,Š‚¹¹Ù¥¥…v{c˜¦Ê¹j4üÍóßþÚoýÆo~õ×?úùU!±$͸¯ „&OÕ /tžgûûíwÞùßá°ŸešRŒ1|6?‚œ«fg§ÇÇï,.ÎC˜äyF)ž¾wo|kk#Ë4„ÉôôÔÜÜ,ç”sº¸8÷î±ç4I¢ÃÃý;w>›žžzð`rqq€˜R,%Ÿšš¸{wìæÍÓÓSËËK#ÖæEaVVݾýi¯wrëÖ/Z­mç¬äáÜÔÝ{·?»}óÞý;ý8šI‡˜$˜Ä^i>Ž~;ÍÄ(è^ºüî`x\Ø”2@|f-0èì·&&ÇîÝ¿395þhy¾v9Âqœ 7V–WæóBŸt÷÷vo}úñÌìäÔÔÄääýõõÕ££ƒ¢0u].--ÌÍÍNLÜ›¾qãçþÛ€&3³OráöO6·Öœ³AØK3FýÍ­õé™ûç,,ÎÄÉ¢P*Ń¥GsŸÝ¾¹°8;6~keuQ§ŒPà\qV|)' ¥Pí!å'þÛO®o&nºuâVˆ}„²U’5¨Ý ¶ŠÆHÝœ\Ÿ]™_o5GC´ÐØK„= H7æc3Ëã3Ë'1Ÿ_oͬ4'Ï®,6öfW·7Ú½Çg ªö‡¨Ó7ïÎέíÜ}¸:µØXntcÞrç(‘l±Ñ›]¸÷péîÜÒÜÚæÌjã(“K«cÓóíAxÂãŸÜ{06»Rµ×æ6Z ÛíëÛwׯOίwã}loB³…ŠÏ¯ã—Óh¿½IÝtã«ý×z="ué!Œ’„“€Ó!'¥%ˆ”€ÀäRiöhynyearj|zf‚Єq„p4ûpêÁô½vgǹ"ÁÚúe°°Àpüî§wÆnQ мºžÅRØ´?8zçÂ[­ÝMç¬ÏGˆÂ³âE(øRïç8Ç—¦2ÀàÏsb"7iVLÐ믽qùý+k+ëûíƒ_|ñ|}^¾Pžû÷ï¿ÜÙi7V×®\|ïÍ×~O`8¸zñÂðøØUN3•I%)ɵЙˆF]FAªÙhx’QUf¥M+œ+êÊÔ•±…v®è´·«2+rå\‘jA 8r® 8ÖŠ:WhE“xXW†3è\QÚ£H ì\Á(€ `äFún£°ï»5™``)Iü!)°’ÄdÂÕ¹7ÃC+êí©Ê¬*³“ãÎý{·?¾ñÁŸþÉ×ÿ÷~÷Ÿÿéö;­Ü¨ºÊ)FJÒg¢ÈUo\¿6èy_´¢Rà,åÞŒ¢^÷ÀÛæ‡fØBוq®à ¦š9W¸:O5Kâá©~n$F‚á)©fž[h­¨·¿*3ï”Ä9ëœU’æF$pÎ2 µb¹QÎY“Éã£ÎÌôÄëüÕ_þùïüöW¿ûç!ü@J%‰àÈo< çl]åÇG‹ï½Ä£ºÊ•¤œ¡³ø‰£¡s¶ÈuUW”Œâ0èûÞT)g¨Èµ·Ð9k‹Ô©V ‚QèœÍRá»ò.T¥ašLþê)u•{gGîɤ’Ô›'8®J㜠»EOwEn¤ÉDUf§jEO¡$ñú)m ’ч\ŠÂ¾«ó/4ûU€d¤M5óñ= Y @2òA´…>>j~Ôr†l‘:gãhØïy½SÎÙܨAÿ8 úœ!ç,F‘´—Ð~gÇ$öÊùÔ°…ö¹Cp|š€Îû0è_ª3Å”æ¦>‰Õ›o]õg7w±Û¡nƒºÇ¤XCz éÉš´hb»äÈô¸=¡ùVŽ´[=ùíN"Û±Øj/âݤ‹·ÇÄlö@s€öZ? 'ñVdÕåf<>Žö"~„³½ˆ·ÚId_”»!Ù‹H_äGXµÔ Ð^D‘<€¢ ¡²»!Èb/"{ÙìÅ {ÇÍ€ì&¢ U›Q6Q¶ëÓ9ôcüä%iÿžtC¸eh·¨ÛŒË;K‡¯ýøê ɳÂI)•$©ŠS§(åÎ1Š ƒQ—’ijZä £(u}Vº:ç 2 ¼  ý͹¢*3[è(ìçFž_Eöß¿øv§½í¥’¥œ’ä¬ögåÝ9ÎqŽ/©ü“I*?‡’\IžIQåÆU%É›¯½zéÝ «K‹ÖÎKß?ŸFŸ—/–ç~øÒK‡{;›kË×Þ÷?ÿã•hÐáðÊÅ G¶«+ˆ"Œ2ÆQ¢9a(1Š»Úf’•F»ÚæZ¸ª(R™I¦9qµuµ•ùº.2Wæ0åZ¸ÚVyZm3åjËPâûqež ŠâÀ7¨‹LR”Iæ»*Ri3Uåi•§© u‘Uyª9)vÖ¸Ú¦‚Jм1šÍI•§¾sW澓ÖVã/|ï[ñÍ¿þö·®x¹{ÔVuÚZ`É`‘ Éà3‘I {]½trÐvU!)’q Œâ’¢\‹ºÈ(Œ]m5®Ì‹TjNü¸8 5Fqos‘ÊSrJ£þc&Ù©³¹ž(Ïd‘Ê*O=-¾ÑÀ¹Â•™ó³++’°ï\APÛÔÕùîvã…ïýËß~çùoþÙÿãßÿÝ[7m¦(ŒÃ0QS{Gž†«LmÓãƒÝ‹ÞÑ ¶©XPp?ÖHWç§÷†’AãïRîÊÌÉpì­5šQù¹ñ§ýÉÊÔ65šeЦ’„Ãç Š¢2WµMSI2EµÀ®2®Îý(™¢Î‚W-°¤Èf*“ÌYSå©WZ‘J†ޤH1ì= 5'© ¥Ñ ~pùb4ì9kÃþèÓðQV çZÅŸ¾Ì]mýY>RƒºÈ\U¸ÊœúXæÊSQÛ´ÈDžrÅ‘wÐ)t•ñJðYc÷R"°ÚºªpµõÚÃIÈ1peîEèsáT3Ï .gHj¥¤iyŠ7úÁ+ÿs½\¹&©ج#Ù€rë&6Û¤ÜÁå0k²tœ´•né8i¼gÜ6H”ëç®ÃÊ}^m„b#Û mSÛLôf$»™ÛÙ±v;0[é¡QwXÙ¦v[±ê°òH¹•Z µPºñ͈ ×Bé1Û@­ôÀ¡¬6#ÞLäj®ñ(÷xÙ® Íz$;ÊíðræÛÔ5péÿ|íó9´ŸFo· òmê6“bìÑÁ«?ºÔOR•;Êãóˆó€óˆ3ÈáŒd©°™’¹ªpÖÔEæ³Ò_”žl¿ú‹Ø“ÆUá¬ÁIôOJ£Ï̯2†½Kï¾½¿»íjk3ežÕþ¬¼;Ç9Îñ%Áb1F#ü—@œ!"ÍI¡o¼òòÕ‹6Vwv_zñ…óiôyùByîåüëþ^scmùÚ¥÷þë×£ D^»rµ³×vUÍO¹˜™ ÌS®3 LÂ`(8µ…œºº4™NµÄP‚¢pAœ›TpŒEžAg©b§Z"˜ §Q8œÆQÀ(¦ úÝܤQ8R’û¾eޤ`~›3âo9#ÞŒ8 Â`ˆ`"8MµäŒ0ŠÅZ‰"Ï’8ŒÂ‘ÉtnÒîÉÑnk{»ÙP’R²Tø5BÎVL òL¤Z£Áµ«—:ue§Jr)XQ8"úAS-µ” ­‚‰-ŒÉ´ŒQì“SIÎ(F0IâäêÒ»)“‚)ɧÞ#ßI00ŠÃ`GVBIN ƒaªyªù …J@ K›ùÌF=¿‚Ë(ln=~¼¾¼¶º´³½•¥ŠÄ(v®òæ&õã>"×E®öÞ½ð³0è¹ökÉgñ# „ ¤0 “xDp¢£Œ†]'q4çF%ñȯš—6 ƒ~ª¹à8ކœ¡0è ŽÛþtB¿R›¥¢{r@ (m&8ö@ø! Œ…'”Îà”QìCãe“Äá ßeûØùð N=í¹IƒÑàò¥‹ÃA¯´¹àô,ÄQ@0¤™Lk%@IÁŠ<‹£À÷‰œB{MúµvŒbÏH¿ b‹4‰GÁ¨‡`…ƒ( Q0a0ŒÂg$‘s•ñð Œ>/@$œÖ•£ÀFI®•€ z&Ó^{OCF¥ˆ™@ºlù«ÿ}íåØŒêäÉ?ÿm å±…²&¶MT­òкýÔ­¬Ì^êvX½ÍJ —ñhÈ×@¶Ÿº b×ãt‹UmåVbý8ÉV“ôq’­CÓ⮋GCÞÀE“”-åVCµ0d*Fú® IDAT{Ò5y½§ÜÉ·Ùÿ±w¥Ïm$×}ÿÑ|rå«+qªœuVò®V¼E‰Ä%¸(ŠÚ«œÄe'ñfeiI¸^’HÌ æècNÌ=óò¡)¬V$«¢ìº”r±ëW¯šoz~ýú.Ô ¶´ ‰Ý¶Ve«†œÓ TÕI‹xû²ÕÕÃã Q׌¯¥%]öÆ“Ÿõ_øP'I•¾}†®Ò¤¦ÅÙ=Ô¡‹ã? f2ŸñÄ·CÐ,Û°¨i!ÓB¦E ‹–i˜¶(ŠņNY±0ŒEÞЩ(ŒVÃÀKâpb›,Í\Çfé¤ÈcÏP‚t0å5õå ü°TÌŸ&qèL,gb]7Ø2¯Ý—npƒühè¦i–iš¶ùƒ¦OlÓs'3QÄÑÂÌ϶ËíÚ~ïåÉÍ1ú¦]n|të—Ç'ju¿\~43»Èñ² áByç¬ÏGNÇ D!@q \ Š/:†éD1˜–ëzQà‰=ñ-Û›8AFÀ” €ëEŽbb$A~x~ìù1ã™8i¹¬ÏÆ3öÂìt˜i¹Óa޲‘qž¦35OÓmfátjÏ™&Jb×÷? Á R€’«‘Âú—_}Í Efvœ|ï„8(†0Ç ÙŠ¶|6º=ñ§î #` ñƒÄ’(¾¸qJ8å´loªñü˜9½éx.{9/†dâ:A–É^ Œ!¡º†)¹ ‚„u01TPŸ—¨ãÙ~|Æ‹ÖE¢]’º¨êýÕlq·ÞEšÇIDBö˜X¯ú'f9Ö}^Õ‘æaËÙÛ! E*Q›¶|•ºC™ŽÆš¬MÎ2¯ê– g#y(RdzŒ³?B#E“Í«º¨˜"6UêöU³‘ÌK:¶|^ÒdÓ鉒 Űú¼$Zî˜ê‚BxDF2üpŒ4NV;'¯°íuL,¤yº¿ì±å#`±yYò2S½Þ9¾—ɽì1æ«íÝOm¶Nαîm¬Z2®”cbIÈ~Ée,/fʳéüBº´+Í<Ø\È•RùÏW Oî¯=žÏ3å…\i)»ÅúóÙâ½Ü£åíTþótù‹¹ÕÂ{áYÌV Û™âgKkÅ¹Õ¹ÌÆR¶8ŸÝ\Ìrù…t~&µ¶ü°œ*í¤ò;óÙÂBºpÙ†¹La1S^)<É¿\ZÛš[-Ìe Ó« ¹ÒBº4“Ú\~¸*}žÊ>Ÿ-.¤K?O~jíÔò¹ÌÆ5ë*,fŠïdÏêæãåò½\i.³1ŸÚœM¯¿9ã%ÿl¾£=?Ï|j“]e#Ãlzs){±®{ëXô—²[Ÿ®¬/e·æ2…ùT‘åÏÒÚÖb¦<“ÚÌ–¾Z) NöqôA5â×pX§AƒDuí¡pŸ@C‡Š”|YÌ?úã¯V·o¥ËŸdóŸds³kk ››³kùOR³¹íTéëté«…\iv%¿+Ý_{¼+Í­fÓùÙ•ü|¶ø`}g¥ð$•ÿ|yc{1S¾»º1Ÿ*Τ6—²[÷Ö-e·î®n,e·Òå/²×ðLcñ–¼žçó…\~ve}!—¿¿¶µËÏ­n̦×gWÖç³›Ö­¶Sùåòb¦pwõá|js&µ¶”-Þ[/-e‹wW.e‹éò“÷ų”-¾UÑ éü›»Ëÿår*¿“.?™[ݘÏn.?,/>,,f lÿYÈåg¬-äò¬‚î¯m]Þ+nxÞ Ït^Èå—–ï­—V6¶ïon½¯Ï…wâYHçÓÒù¯2åYZr7Uú4S¼›-}šÉßÍær……\q>½~{îþZi§Rﶺ§·nüæùéæçWn|ð‹?¬¾<ùýVvçß~öËwÒ_?;Ñþ|Lª½6Ä¡Ú(õs¥uNŽÞ‹QØîÑ6GŽFZgH;Üì£Îw†´q&·9r*šÇ¢y8¤S}—#­n÷ÑTs4ÒŽ£ÝGï…§y. øTÔ[œÜ<—ZœÜåP‹“;µ=T:µÞ;õxLyZïI]Ž´9rY6Ï•C^?ÍÖ7Ï•Ö3 ;ÜæHg€ëçJg€Eó˜×ëçÊOǃÚÃ+ä5ëRßÕžcžvyt8ÄSŸq—G×øG|G{®óóÿ…§9Ú}¥Ñ·z2ë·8õpHYžt†”E¿Ë‘Ú™<Õ·øpH»#­Ë‘fFw¤M¯²‰«¯¿-¾ÄWõø]$LÁÛ¨“¨N¢ ^ÃkSŸ¡IÚœÔ(t ¨)ð¬o?ï É®òêÁoŒÄ– 5‡ÒÁ™Xï‘CÁ>¬FO­¿’˜?[Üê©Ì‡lŸ9äõc^Ÿú¹3ÀžÊö–ÎW_I­žz$'‚q%Ïuuw=Öèë¯D–'-Nnõd–9í¡r4"‡f5ÛåPs uj£?î ÔÎHí ÔêßêÉG"y_ ˆ€ƒò€¸ ù€#À P (5=@1h… €BÀ àèû‘oÊ÷Å£ø€bÀ(! M.4¬¯F 9_ÌÎfDñ…毕‡ù™I_ÜKàÿݺ¦WBPƒd‚âƒM€à$ÿâ®ëræ­Œbü•ÎØs9›õµ)/˹*{ž6çìJ7÷™_Ÿç–óËqr¬OíT™üëÿ¼S«$›¤»¤V©jÓb™Ê”Ê”ëô &™ÒH†WÞúÞ§~ÿÅ¿|ó»ïß;¼=8ܾFoÛÙvã¿üâýýÿ¼5zýÝûçþü ¯þóϤ=îÖÍ]aïKÔjXÂhîÆÇËÉÉjR„QÅÃÂî8˜Ó^ÃÂŽk;˜Ó°¤Qé†% æ´;7{ ×vXجUœÏî-x¯À¬ùä9£Šó¿¼Íÿõv*ýD¸¬œ7•~\Û|6+óä|¯,‡…Í6äûKW~*ý¤Y?n*âclþ•8Ùžû~ÊÔó×õÄö pŠ™±¿À?—µçWæŒkÛG3÷¬ÉÿÉx÷ È+v°€qåóz Ø[pï‡ÇÄâj8Ùž¹, ¯hXÒ`yù{žrø£µ?5{ö 3ªí^éwŽì Xí›4¨W?Õ»µ»'àŽÐ·k}[àáïˆæ®lïn§r÷j_„ÜïÕ>kÎíý„»¥í§Ý¡?<ñ—ê;ïTü¿ °÷…»/ܽÚÞ¯)÷{Þ«p§â~>Ç÷ŽñVwêõNÙÞYØ‚††÷„ܭ˽ºÖ0¬xXøa6k$ï*£ÒM¥ïëewnvO>ŽÑTÄìíÍ«†%]ÄÙ¬¸My!g£FrÂŒ*žJßîÎÍîÜô)”OmfT¹Îf9ŸºöÜÚß<ìKc\ÛSå“oñ˜=dËùd8}X7·îÌ¿–çÂ¥8“’' ;žóðÄî.h·p»µ»/ÜNÅï?è•}å­ï=ó7ßxûŸ>¼w´3x°}Þ¶³íÆç¾ôùÙèÇî½ñæ³_û³¯ýðÎQét¿Ž»öd=Õ´V%.šÃEsPÒ¬2Ó¦¥ž–0«Ì¬Â,÷k˜U8-õ¤0“BÍ*<˜Ç›3óµ›ãOÇÒÎ*œ–0«p¿¦ýš¦%ô=kú yò&¿¿ã¡´Y?«Ì~Mû5lÎÉš|÷Y…‡’®ˆ“íÁM›³æ±ëzr{°ö‡“ÂŒú±þ¹¬=WÀ™&sÎúIaÏ*³™-û5mfÅ)ÿoŽ7³åŠ8Ùžœ½¦_Å)o®÷)Û£&¥Ø;®vÖ{GzZ„ƒº=¨ã¬æ™€‰”c%ÆJ ¥JÉ0Vq*}ß'Âõ¿^f*œÛ'Âõ=OÎOµ‹9n¦ì“÷±¤±¤±ä'ìÅ#Acᦪ™ÈvX¹½ Fµž(1•‹‰˜Oª“Y-%=¨í~åök:9ërÈt¿“lz»—9 Óòs(é@ð~ qEጼ€C9Ãs\”B›½/«Iarù\/g³¢7÷–ÍŠØœynQœ5)[u x˹^N¿“_¼›}¢Ï…KqJ<ö°ÄÙ&Œk  Hš?tû°xùëßú­çþøÍoýÝ·fûûÕÍ/n_£·ít»ñ'_ùÜît÷¿vï›ßþÁ§Ÿíå¯ÿh\¦c—ŽC:òî¡“s'J ’ IA’ÜÕÖÕÖ çUhTˆÂ…Š­ôQú \Îg™5%Ù’¸$[±­ØõR¸pœ(}¬(T„mjޏ\ÖäqÍQØ&k¤™,\>Hó¸¶Nú˜mÎ×ÖHrÖäqm]¶¤b'}¸"N¬­¶¶‘®•®Í㚣tí9ëbwI{¼ôq[ó#wï׿Ã'µçê8%úŠBU¶³¤ ó'{¸Ï>g6s©b—s¬$¾"ŽÝôçæóŠ”ï”ïzW žª=²ò®2¦6L>‘O´µÚ7 # ’¥“¥“skŽO0Ì©-©+©+±]@³0qnbÍšŠº’º³r®C¶Ä6Ï/°­.ä4%‡ŠÂʒÜ}î'N0ƒ?„þý1…cjN¸Í}n›…u%»’CAqnâƒð–maUíeíeÅuEJZ®“ÜI²'³Ç²?KâìUé£ Q…&Ç¢¶nsNI6Ç"Çè\Ω¸ôòN.ä )Ñ—èsžäAÎ¥œB9ÇúSyP¢Ï9–ïZ89ÛOm)=6Wñfÿ¸6ª#oçÖÑ–s½œ>¾§®º®çÂ¥8‚"ûÎPì… ¥oæ6œøX´ëz™fvV¯}óíçžÿÂ[oýíO|w6>Ù¾FoÛÙvã+/½0í¾÷Þ¿zëûÏüÎË_}õ_FÇiAIv©jíÜ•Ï% MŒ¦Ý:EZ¢'žkÂÚäÙhBÍÆ0 yŒÎ‚CM(Q £%* &®C(……ÖiâJia€| „JiM Ö)$aò7ŽŒf–)0y×Û Á !k*] £ó²R2_¥H÷Ö^ŒÌf›ó*$ýá–síœÍÞGÿºž —å ¢1Æ ¦šiަðlV«c¤é¢<˜‹o¼ùö Ÿ}ñ;ïüýÏþëÖl÷ðK_üòæûÓö5zÛRJ7nþéïM§÷?¼uïÛïüë§Ÿ}嫯~tœJ—\JœÝ v œfG–ÂYtbBc‘Aê È) ƒ¬É"Yd ÊnÕ6wÁf B†~ü‰s´¥ÁXkC­µ 3/׫ÕjE–1ìlŒ‘5ÆhPÆ(dí<†ÆÆÆZÄZƒ0 4ë 4Ö:Ð ØšåºY­²Æ€`±±ì Ï4 ®‚“í!ç\hbŒÑzGDŒ1æ¼uu«Uw{!h0ˆH–½÷¡‰øÇc.cÏE~þe8±m¬µ@ˆˆìlÓ4Ëu [CÖk …¤€”Ò²Ž­óÈš¬gku?vÚÎw«(¯‚Óh df²LDÙZ TJ!SŒ1¶÷ž,#"aƧgÏrÁÔ ‚´)Pi‡äÈÔÆÌ­-›N·K"°7È@–‰ cµaBï|Ûµi‚tg¥VDš¸jÚuðÛHè‰S8Ÿƒ ÈO.}Û¹¦µ±ùø§¹Ü;iƒö -6Kj’kW%’DR@&GÁ º Ww+X%t¡( •ck‰™‘ ) Ây\®›nÈšÞŸeޱÎúvVëÆz4 €”uõçrˆ5²>+/à 3²³ˆ¨Á8ç–ëU×ud™™‘  !e}»ìV«•õ΄ÖÚ¬¿NŒÑÇà½gg™9ï-a¹\6]»‘ùæç13»à›¦ M$"©UÖ#"2å8æ{m9×Ëɱî5ýU×ø\xrŽ1Fk­•ÐdQ‘®Œ}Jó¡Žkxíõ7?ó™ç¿ûî?ÜyïÞâ°Ø¾FoÛÙvãæÍƓݟ¼û¿þÁo>ûúKñ£Ýysd£K+\zpÚ; d49âXÕ2¥•1†SJÖÒb±°–RJ)­B)­º®Ki•RŠÑç1"¦´ÒZÅÜZKBˆ:„°^/1Fˆu]¶m[sï}Ó"RJ¤””Ò{+„hÛhŒ Ái­SZ)¥ŒQ)¥¦ ZkfDD"`f"@DDƒˆÌh-!͌΢aïmð–µdt-½·„¦iBÛD¼ÕZº-çZ9Ë® Á¥„Ñ’Ú&¬V]þäõY™çÄàš&Äà¬%&@4–1Û ó]šèÿ¿rÖ«®ÿ î©/N]3©¨ml l¢af""@@ÌÜ„¸Z­‰ˆÎJ£4Eš¦‰>Xk -]Žƒ ×îG BhÛ¶m[k-|Ô”RJ)ƒ;;ÛtmÓ.C:"Ñd  eˆÜµ®k³A"h?ý= ¥¶]CpY³ÉÉš\žc-e ‚V[Î/â¬W]Œ>ï:×53šü)æ\Û„h-/Êÿ-çz9L`Œ£òÌ~üäù¢‰ƒ6œßò0°]¯æìGs5šë×Þx÷·÷óoÿÍ?Þ½µ7l_£·íl»qóæ&ƒŸ¼ûïüðSϾùÒkÿ1(›“è!yI™5BBò•Öä–«4 vvv&“ÉÁÁAY–ëõº,ËÁ`ptttrròðáCDüàƒöööŽwvvö÷÷‡Ãáx<ÖZ+¥À9ç½!0s~4ƵÖDÄÌÆ˜år™EQEADRÊ”¥”ªª*˲iš²,¥”B­õññ1­×ëúØ;÷تŠ}ë?çO£&Så!†¥¥úØ»¥…Ý–B)¥¥ $ ®\oYýÚ}½ƒoZÿýŠeÓìïaôGŸŸÝ{øãÿ¯ïú‚¯®†"(@å…àgatÏg]]]FFF]]]ww· ôõõUTTäçççååŸ/꺮ªj$1 C’$Y–ãñx4Õ4M×uÃ0ÌŒacEQ|>Ÿaœs]ףѨ,ˉÿ…G"J)šÙÄÜ•˜Ç’e@Q»,ˉó[!ô‡ÃaÑ63Æ „'¹Y–EþÜfzèâŽæÏ*ÓÙl! þjA,Nr93™P‹øù-Ër"DdÄyálqf!G\…µ4#Td6¼§YœärÄ“™Äü¯íÏ“2¾ˆ%ÈdÄ åŒŒ$…$ ðèP[a´e¿ÄxýõWúî\ºÚõ?'›§„ÑAÂr #ƒ2 2GLt•””¤¦¦ž:uª§§grrò‡~(((˜?þÃ?\SSsþüùµkצ§§Ÿ>}º¯¯ïÇliiÉÏÏß½{÷ÐÐBˆ1æñx\.×;w†‡‡£Ñ¨ùî9Î@ àõzÝnwWW—Çã‰ÇÃÜX IDATã.—K’$—Ë…»uë–aæ+ít:¯_¿îîîŽÅb^¯wÊ(n~DIâ-Ã!"C!&ŒRÊ‚ˆ•aÄôQÎ$ `JaaYbL‘!Hô±8¿=G–¡ÈCŒ0¦ˆ`„@!"<§_ÁSˆ„XxbJ@A“ <1%'¹á9ý*IL*BSÊE-ac*T)Á˜†eÉâÌN!lʳ†ËˆrS9¦–fÒ‰ÅI.‡R.ÊEßnæã @˜é+Ss6Pˆ#º—hý^àô!Fò·onvõö[a´eÓí?þñŸý=Wnœø¬é¹úË[´9ºGS Áe@l0jp%‚‚¹FÕȤǗ——·ê®:t¨±±±®®.''gÕªUóæÍÛ·o_sss^^ÞºuëdYŽF£†aâââåË—÷öö:Φ¦¦‚‚‚ººº§Ÿ~º¦¦æóÏ?w¹\”Ò‹/8p`çÎ%%%hnnîééÙ¶m[vvvMMÍûï¿ÿÚk¯íرãÂ… ¡PˆRÚÙÙùì³Ï–––VVVfeeÕÖÖ6551ÆcbY'!D¬!¡”bŒW¡sÌÊ4H¡*ÓtE"Æ1QD¹ð!Š cšŽ0W"QMB5*c¸nq’Èa\'ŠJ™&ʉ¢r%Â4P1>ý*|×1W0QãŒëLÓ¤€PÊ4ªj\‰×#'¹A˜NÆDaš®j1ÁÇ\ž¢V(„2ëEBÊ,Îìä¨ZŒªãºÐæ Â\BXQ£DQ UßÔÒL:±8Éå0®CÊ0QÁÌÿNÆÆ#$ª$C$Œ$Ldı£|01ŒîºqÇ £-»§ý=ŒþïO¿ÝsèÏæÚhbh0 6þi6R…(úÈè¸ÃáX°`Áœ9s{ì±C‡;w.##cÞ¼y+W®\¼xñîÝ»ÊËËKJJnß¾Çu]ïííu8¹¹¹×®]{ýõ×.\X\\üÅ_|òÉ'«W¯v8ï¼óNkkëÑ£GÏœ9sõêÕ›7o?~Ün·×ÖÖž;w®¬¬lîܹ999uuuo¿ýv^^ÞöíÛ»ººœNç¾}ûÒÓÓ¿úê«“'Ofee-Z´hûöín·[Q7BÄ:lÆ”ªÄTñoyÌ5®Ç¸Ã\CL¥j„(:¤ŠÈ˘q=†˜ªÇ =n ¦r=&cfq’ÎaZ”(:暨R"q5j ¦Þ3 ¦E©Á\¿ ¹“5áÂÁâÌއxôB$¢-Qt%W"qQK]h@ÈÀ,4},Îìä¨QC‰Ä…NÌ®@B”ë±DåÑöžÉâ$—#2âÕNÌÿ>ÆÄ©f¬È!ˆ8¾O!4 ©qWôÈþõ÷~õ/Ÿ}}õÊíÞž~+Œ¶lºÝc‹áŸ:¡0bh²FBR4Š„%ä— †!¹ÔùömÛV¬XQRR²xñâêêêúúúõë×—””äççgffnÙ²åÓO?ÍÊÊ*..njjºvíÚ7NŸ>m³ÙvíÚU__¿iÓ¦'žxbãÆƒƒƒ.—Ën·?òÈ#eee ï½÷Þàà`[[ÛåË—?žžž¾lÙ²æææ¢¢¢Å‹§§§ïÝ»÷ܹs¥¥¥éééííí]]]ÙÙÙK—.ýþû‡9RPP°gÏž±v* †Ãa±–C–å`H Câ—_B!HCdDÂ\Â<H@Æ¢Üôñ…¡L¿„UUý’‰â ÃD‹óÛsˆ… "RR s™(fÉô‚4ŒX±Ä~ ™_F$ s@U‹“\Žx¸~ ùÂІ~ ‰Â€Œe¢ˆmÍ&¦B„p°8³¨*až¨‡€ŒýJ|¯…Š_óéÉâ$—3¥Nú¸ðk9AÀdÄWU•S !ŽÃ©ñáR+Œ¶ìÚúÓ¿ ºú~¸q[l1|ëƒ61 %Ä¡/ z=Ìã Ž{ÝnoÀ”œ}¹¹¹999'NœX»vmqqqKKKyyù‰'òóóçÎ[QQqûöí 6<õÔS;w¬¬­­­­­ÍÌÌlllìîîÞ¸qã‚ òòò¾ûî»ÎÎN›Í–’’RYY9444>>ÞÞÞ^UUUVVVYY™™™i·Ûoݺ•ŸŸŸššºeË– .ŒŒŒØl6»ÝÞÑÑqùòåêêjQÕÚÚzåÊ•sçιÝn¡ßï÷x<ããã>ŸO¬·wOº½‘ ßÈ„oÜ÷G'ýcžÀd@š HcžÀè¤_”›>cž€7†Ý^·/äö…†Ý^oŒy‰>ç·çŒNúE^øüÃ$î8áOøÃâvf•¸£(óܾÐd@²8Éåˆæ£“~ñôMìÈ„O4*rûBþ° ‹Œ©·/42á³8³“#šï¯© ‘D¡©"³»˜ž,Nr9B âÉ&æ?ãKX‚,3 #Æ( …}€BÍ0ü,2àƒæÿ~~úÆõž>—F[6Ýxíµ£Îþs‹á¿½ßÚãUÆö»ý>Ï$%T… …<ä±Ûí%%%õõõÕÕÕYYY'NœØµk×™3gÇC=TWW×ÖÖ–———žž~åÊ•‘‘‘þþþŽŽŽÌÌÌ;vtttlÛ¶-%%eéÒ¥UUUUUUëÖ­ËÎÎÞ»wïÐÐPggç† ÊËËÏž={áÂ…•+W={¶  `áÂ…;vìp»Ý.—K„Ñ¡K—.UVV¦§§Ûl¶Õ«WïÞ½»··wrrß˦*€ªi"ªŠÉ‘¦T‰$~¬OÏ[œ$rD^LK‡SÑ÷ObÆ%twÞZ&Š˜_Ó¢‚,ª¦|[‹“Î=“ƒPˆ„¹È›fiB¦›Å™œ)2w';§hFü)ãþz°8IäÜÇV/C¦‰+ ª˜–öKÈí“ü!Áb$A– 8k£ÍEŸ¬¿q½ÇZmÙ=íg'u$.ê`F$@e¯_!؈D Q˜ÕãÆè˜ÛápØl¶††‡Ãñè£fff~ýõׇ#55u×®]ß~û­ÍfÛ´iÓèè¨ßï7 £··×n·çää477×ÖÖ¦¦¦Úl¶––ŸÏ×ÒÒÒÔÔtõêÕîîœüüüÆÆÆ`0xëÖ­µk׊còŠŠŠüñêêêÉÉÉÑÑÑÜÜÜ5kÖ\¼xQ–å–––+W®ìß¿ùòåóçϯ¨¨ðx<÷£U*B\G\ù_K°8ÿïÑ¡#®5JÔ(âºYxÏ$°"™=,dšL³·‘¸¯ÅI.g¦‚tºZDÉtåÜg˜·8Iç$Ê`ŠHDJÔÒL|‹“\ÎýýgÏø"aNµ˜„9ÓãLËDaz\ÂÜÆ+ªÑuUá˜qD5Æâ1+Œ¶ì—ÛÔµÑæIAއ½*…£R¿Ì´èØø„Ýn_²dÉåË—_}õÕìììùóç×ÔÔ´··Ûl¶¹sçŠ0:??¿´´´¹¹ÙétZZZŠŠŠª««›ššŽ=ºlÙ²òòòÖÖÖóçÏ—•••––¾ñÆçÏŸ¯©©)--½~ýúààà7ß|³fÍšÂÂÂÖÖÖÜÜÜÅ‹WUUõ÷÷ •”””””´··:tÈáp444;v¬°°pÅŠ555ýýý3…Ñ€p@¸Œ™H*æ¦Ì5H³Jx gH±µB8$VYœ¤pÌZsK“Øeb6™’dÌÌ}'€p Q ÑD‚ð„‹=7'¹œ™RþtdØáOÜ%î(T„¹†ÄâÌNŽhhŠÁÔF¢fÌÝc‰]Á”dq’Ι)ͪñÅKˆúÃ@ AAªøCpd"àAB9¥ÈÁPØ'ajªF[öËí§µÑ×nžø¬iÏ¡?¿õA[_0â‹hÀ`^ò5qntI€p-f Œ­]»vÑ¢EN§³««ëرc[·n­¯¯¿uëVZZÚþðñ1###%%eóæÍ¥¥¥ëÖ­³ÛíK—.ýòË/'''»»»ß}÷Ý%K–çääddd|ôÑG—.] …B§NJKKËËË«¬¬´ÛísæÌyòÉ'ÛÚÚV­Zõàƒ–—— dgg§¥¥566B[ZZÒÒÒrss«««SRR222š››ïsŒ<ÂTÆ@†@*Žj2áÂD‘–‘†ˆB!bÂâØ&q´€TÆanq’ÈÑô8QTi@’”izÑÃPÊ)g08ˆ”Ú8›ç½~ج9Ω–ÅçYQô³Œ Ê !SH±6Í{E–ÃC½Ôœ'г¹²"·6…A@˜àŒ Âh9,ˆ¶&Ï{y¯HÓl—üPs–³Ki}Òî/>ÏŒqE¿×︃Xç×£:Œ®õmUÙ¨C÷oÜ[&q”*Iáƒw©Ïoܸ±¸¸ãrK)ïܹ ~öÙg‹‹‹wîÜ1Æ,,,,,,\½zuaaáÁƒ7nÜXYYI’Ä9GY[[»uëÖÊÊÊ­[·–––¼÷!øÆóÚÚÚÊÊÊÒÒÒÍ›7———WWWáç½{÷®]»†rÎ-,,,//gYÛív»÷ïßÿôÓOaÝõõõ/¿üRT‚éòg9>K9\Ë6O)% \ìœsΕ?kÎÁr Úêh­Í@Uç]aŒ¸RjxHÛ­?ƒA.áCã5çÀ9#Å>ÀS ²R †[*Ç®9OÇ914uYØK8ð«àBˆšs°œ*ÿƒº/TqB0…='„cå÷W£ºQG­o©Ýºv%^]׌öSß“N1e•Ëúƒ cŒéõzý~ss³( Œ1d¦,˲,ë÷ûàÕÌEQ„Ö××­µŒ±~¿ï½/wÂ{_…÷>MSJ)$Âð‡!)%Ì$„`­åœCié÷ûJ)pƒmÁ¾Uo%ù6ÓJ”¶s©’\p*3Z-¥`‚Ó‘n5ç»äœP‚JÁ)£˜àD 6Ò§¥qFJ ÑR+Qº)ÉkÎÁrªŒQ ( @n<óyŒQ\sžX¤HÁÊŒ¡$Î3Ãy©Š_s–³»¿|bî/FËËRuÉÛ*¤¡*Ú»4K=x~ŽQP¡ž¦Ö{Ÿ2ÛêÝK˺ºmUeeE|àŒÔÓëõò<‡jl=Ôf Ï—×ü*xÍy8UÚÿqsRÅææfžçBˆ8Ž×¢x—0z©Ë‡Ãèk×n×at­‘:4qfüÞò×?ú¼ÑúÃ_¼xÂèÈi×óĸOeÎsA5§¾ÀÿPG.ˆ˜CI’¬­­aŒã8Ž¢ˆ’$#ŽG4ê’îîn਋ãˆt»qEqÜMÐF‚6âd-ŠW»ÑjmD£/b€Üív»ÝnEq—3I’$IR:Œ„€ç¾Tů9ËA•n务ªó¾ [ŽBaŒŸvB*r(¥PpÀ§*Œ!¬®®v»](ÅpŒ9ç'Å(‰’$B(Æ8¡SÎ’!¡Y‚QL¦QLNp‚P„1ŠQœ $ "8šc'û²AÅ3#ã„àÁF·6œ ‚ j™QL†—OaéN¡$Â(†?KÁ”`Jâ´²‚.P-¿:2Áé+Ý’ ýÐ83!±dbŒË™áµªà5çIàTi_üÇÍ àÆÆÆÚÚZ’$Ƙ<ÏÓ¢WÕ¨ƒØÞ‰ºÛÝêb8×þýǾpûnF×Ú©C§¦N.Þ]ºú§sÍ®¶EеîbH5'©äƒ^…Ã1´Rjyyy~~~ffæwÞ™™™i·ÛçÏŸŸ¹tq‡]˜¹taföüÌì?Î̾>3÷뙹_ÎÌýr¦ñ‹™¹_½qéµj}.Uè ÷ëIÑÅ‹gff.^¼xîܹ×_ýÂ… ívûÝwßBk)õýû_6›ív{þÊ•·÷»™F£5ß¹rùò•N«ÝiµKµ:íV§ÝèÌ7[ÒZ­N«Õi7ßl7;íö|»ÝÞót¾5X}kµ[—÷8m5ßlµ:;§Uþv³Óžk·›ív³Ýn·ÚóÎ|£3?×~³´­?Þn5ÛC‡fojU¨æ<]ûYs­ÿãæ4›ÍN§Ól6[­Ö|°²²bŒÑ©ß%Œ¾Ë:Œ®µmu1üøÃëÛºBÛèϾ`TC­£ ’VFBšÍæùóçß{ï½F£1;;ûöÛo_¼xq¶qi®ùF£u©Ùn€5Z—í7šKö…FçÜ\û·síß4æ_k̿֜ÿm«}id1˜››k4P Ê‚?a¦Ñh€Ã°ÏÈâ´/UñkÎs†¯³åÉýÚS?’?ìÓh4æææžvN§Óét:­Vknnn~~þý÷ß_XX ƒ¶ª;Ãè8F—/_ùðÃ?%1]ý²K‰¤Dn¬'(aŒ8Âaa¶è¢]̺˜E˜%ˆ%ˆ¡d`˜CJœÐ8¡[K‰ŸÃ{·8aqÂć a°Ø–8Òs÷¥sŒ1Bq‚Q‚Q„I³5ÌÖ0['¼‹9üåpHðW5sו¯¥Fzî¾ô{Ì®hÆn«´Þ–¸ ¿æ§Jûå?nN·ÛeŒaŒ×ÖÖè`ˆ·„²ªF±ô‹lq]¼6sùož?Ùzó½ë×î.­Ôat­:täÄKŸßºùÑ_ÝF§ýŒÚôîFšðëB#,œJ £œ©Áw Ïç¼×ë%IÑó[o½õòË/=zôìÙ³ÓÓÓ'O¿òwÿpjlâ¥#Ç_þùߟ9ûÊÑŸœ˜<ýòÏ_™>{ú§/üäåW&'§½ò‹‰©³/½tì'cãÇ?~ìØ±éééçŸ~||üðáÃGŽ9}úô™3g&''_|ñÅ3gÎ>|øÄ‰‡ž¨Ð± Ú¿ÆÇÇ'&&¦¦¦¦¦¦&&&ÆÇÇ¿¤æyãJåK’M•åMínÙ’-ë¢(Š)Š)‘‰ƒ !ˆ‘dÉro¶ùXK&E7æ÷˜ûÀ`0f0ùðÌÎÙòªjã5Åšjöô¼~ýº1óºûõ{ ÇzžëyÞxüÀ¿`ÝåÇã±ëº²,C¸¶­žçA?×u]×Eµø›¤@‡rò÷s -EŸ6¢Ôÿ¢ˆ¼ÀÜBñt"…ÆÃ¾|&;ŠTâòÚ¶Íó¼®ë–eɲ D\×µmõ2zÐ_Å¡Í$DF ;iEQ Vò`9=G£Ñ`0€àĦi8wîÿ¶m£*9ÇyUtÐI’Á`²µm[PÔ:Ë·%ñ²ão IDAT…Óú’&+ª öE]×tË–Œq…QÉŽz#¼ù'ß_ÝÛÚÊâXåÃÏùûÆÆ±ýÇ·ž:ú5š¤‡ŒäY}[ãxYWM”YÛ °2Ø‹¹Ôèx<F/^¼833sêÔ©Ó§?¸zõæÄÄÜûï_ž¸xýô©+/ÌŸ;{íü¹¹¿ûÅ/gg—/]š™\»8199uéÜ…3“S/]º899911133söìÙ¹¹¹©©©+W®LOO_¾|ybbâ½÷Þ .\˜›››››»|¦ŽÀ¥—Çää$ðpåÊ•©©©ÉÉÉ@ä˜Î«¥3== e&&&&&&àYÈ<G¨wrrÄÀšÎ¹sç@ ˜žžN&“EÁ"š¦‰Ž3öz¬iÚ+EÂñf£ë:žÖ7á°¦¹®ë‰ò¨Ëé]ѨÐüFí–uI{ŽWwË|µÕ»x­‹WhŒl‰&Ù`ŠUº@µñFoô T»X¥‰&S¬ÒY²±S¬lçÉb%CÔ‹Uoô ‘+7³dƒh2X½›+7±zw«¦ñZ†¨gÉF–ldˆz¯íbÕT©–Æ¢™%[Y²•+·óº@u T'_¡så6ägˆfo¤Jµ]¬î/ùyØ_-ƒ—3x9M”w j‡¨='kÏÉÆs²Qjq»D­+DÝu]”¥J…T‰¢¨r¹LA8ŽcV*•0 #I²\.ã8žÏçs¹ŽãÕjµV«G€$IÇqÇYHûË”ËeÉïA†a†Èú þñü$Y«Õ2™ I’ÅbʆÑn·{½ÏóžçŸ‚ ž>} Â) ¹\.›ÍæóùR©D’d¡P(‹¥R ĈD íEUã8^,óù|¡P ¢Z­V* ÊÅ"AµZ­ÕjÕëuŠ¢à©R©u‘GÀ/;N¥RŠ µZ È  @r@ò‡Š•9hòÖÖV©Tª×ëèx½,ˆaÐ4MQT6›Íd2NðT*…a0†F ’ ê}?@&дR©DùP`V©T wŠÅb¹\Eq/4Ä·û{QZ Æ0r'š¦!œš¦!ç(¡ir1…_ £Ôè'в*èEWûšÔפáp`ŒÉƒ§Žc5ú߉ýž:ÎÜúÔÄhë=Ñ3U«Ïr F+}iÏà_ \ÝÑ4 “Éd8žýüóÏ/_¾\ެÅ_¿¾¹´ôñƒ[‹ cÑ'±èó×þíßüúó'ÕZÍlµÍNצ»zµ.ÂÇÉz½^.—kµŽãN‡¦éV«Õh4jµÃ0ív›ã8x—ACÑ=í—MÓèÌb·Û¥iú9¦ójét»]/èt(üRø±Ó©V«ð«©T*4M†áº®aÈÇ[«Õày~8J’bš6ÏÉ‘ðz³ÁŒ]¯¯Z"?”E³ß×[êÀöú¶÷,KÝÞx8±¾¸¸raqeâÖê¥Ûá+ËÑ™ÐÚ|hýFhýúíØÌbxòFèÂÜÒ¹Ù[æ–PâÒÐÊæãÅø½«ËkS‹á+·"3ÁøÜjâzøÎôRdv%>ukuêÖêõHb.¼qåvøj0XŽÎ„ÖæÂóÑ;óÑ;sá™ÐZ`)XŠÏ†s«É¹Õäl(qmåÎ|ds1þ`1þ`>²ymåŽÿn`)XŠÏ7æV“ó‘ÍùÈæÜjr&¸Dþ] ­_ ů…b³+ñ«+ëWWÖ+‰ÀJ2°’˜¼^ŠÊñËZw(@ŒÀ@4¥(J–ån·ëyMÓ¥RéÞ½{Ñhôþýû ËËËÑh4‘H$“É ÿAÆÄ1ŸÝW<O&“‰Db_þÚÑFtáp8I$÷ïßôèQ¥RQUéêž7:Y–‘^»êž |°.Ó|1VÁ"Ò‰Aýõ{ú#é ÿE°’íyžmÛ¢ÚopBWÕ$cØ74m ƒm:®8týjtxíþövŽÀ©c5úñ­Ž‚̓ƒj´4Pz}ÔhðŠ~B¦iŠ¢xûöí»wï.//T*uæÌ™{÷>Y_û¯PðÓøÚ‘ð“ðÊçwï¤?Âc‘§ÿð÷—¿xÒá9O–nÁÏ˲Îánˆ¯˜@², OÁ-ô8L2‘Û}N]_—CÑjµz½·çì=þ ùá8NÅF£ÑjµJ¥R4Õ4­Ñh€NÓét‚Á I’¶mÓ4Í0 Ð;W°–¡ß0„âO CäüClv†¡i9v@v´ßÙdØœÓ4Íó|¿ßE±ÕjµZ-Qz|Ï0yðóŒ¼T¢Ól6Aà8.ŸÏÇãñ^¯çyž¦iív»P(lnn¦Óižç)ŠR…ã8EQX–m6›Íf“aðªM€.Ét:X†‘ O÷hW<ÏC,ËÖëõ¯¾ú ,ŒÁÇ(,CBÙ‹Š,òÓ ™ƒÁÖŒu]ýaß*2Ê•÷UÑA#5z4!5Z6 ÍhŒ:Œ‘j4A+ûÔè³gÏ£7³·§F»/o(uŒ?'üìýs§q’@G ÏÜúãt΂ FþÕhYU`"(Ë2D’$É0 I’æçç766VWWgggS©Ôôôtj7ßëš­–½½Ýžº¼±úïÐògÿv7‹|=7ûIbý›åî&ŸƒŸ¬®þûjøñ?ÿË»$Qß· :Ä+й® ™`€õ§Ü1ŽñÿàWÛ0 ˜:‚ù °ÂqL­F1 Óï÷;ÏɯFÂëzϱ½Aßô=UvXÞn²ÎGŸå¿»ÿEa§3(ÈnF4³Ê(¯Ø9ÙÊ F^41Á( f…7+¼éhiFϳF–3àšíé©®VìoæY#/X¸äàê—œ¼`å¹!Þw1ÉÎ ¡8yÑÌ2ƒ¢l—D« YEÁÌ‹&\!]LñÙ+Jn³³œUàì¢äb¢›ãíÛª5™NO{¶…Ÿ;c9øÑƒPè?n~¼±þåÍ›ݼyÿÆÍdhåÁý>[[ôùs‚¬:¾£'étzww7•JU*•ÝÝ]Š¢ŠÅb&“»ÃáðÅ«•ñýFñãÀÁž}Ù~‡„Ê|ç*½Ñh¤(Ç1Žc{ž;«ÕêÖ7©^—çX9M6j¬mz†îÙ¦Çöél}êFrñÎÿ|ºÓ.I^Añ¾l* µ‚á}A+O{Úo¦#˹ޠØÑ‹½aš1²â('3‚œœädE7Å9iœGi~´Ë™)ÖJ±Æ.g§y뺟âô­Ž¶ÕU3‚Œíî`—B:Å;ì`‡¦8=Í[ÁNó£Œàd„qVt³¢—ÝŒ0Nó£4ïfr²¢eÒü®)v´Ã˜;Œb-(¹Ë9»œ}àê¤ØQŠ=gÝ笻͎·9w›sŸ³£’ê¿Ç¥ÎI¿ÿâÉÃG÷ ÙmÇê < kf°, Ûtðz„ˆ(b”(Š ÃÈG@P>ÀŸ|ø!,°ÁÚ6˜³CE "P@AX ô¯ ¢-o¸ kÆ“¢»½~Ð:Çq®ëªªšL&yž¯×ëÁ`pqqQ–eŽãdYfYÖ0 Aà|DÓ0 CÓ4ŽãlÛž!\ x=GÐ…½˜vÈԶ숢( Wíú¶l™ú„|È P„€OÖe)ŠÒëõŽ¢ƒŒwQ§ nBáôÐʨ¿ÒqÕn·‰„,Ë4M …X,Ø`‘ÕívUUå8dsf$7Q ÀutÔ_°Á “ á(~ÀlƒçyUUišŽÇãFc<C¡XªHÔ`» kmðë$Éo}Œ@à—¢ë:úIæÒA Õ~5ZÖÕÛU5Õ¶t{Ø×$px§[¶_þ§w>‡w(ü ¼{½=5MñÓÄÏÎN|X¦*©gÙÕÈ'H次ådêtMNñFGDÕÐ 8zyÁçlöeYï`0ø›ßüæôéÓ'Ož|ÿÌé_ýöä{gßý×wýËwÞùÕoO½xõÌù«ï}0uêÌ•Sg¦ÎœŸžš¹ñå³mšc¾~þì¯þúç•*åí-9ã8þöÛo¿ñÆ'NœxóÍ7Oœ8ñú믿öÚko½õV.—ÛwBùÇ8|Váкÿ-¯(’$sžç‚ŽÝív±R¹¯{]!¾¶Ùjð–áY†7²<£w÷Éì+LNÓÎVÛÜfœÂÐ+˜Þ3ÑÙý_ö®4Ȫ"K÷Ÿù1ýgæ—F83ÓÑ1mO¨=€ÒÚƒP(ˆ´‚–-Ú "hã‚Ë´¸€¢¶Ë„mk«lMQ€Ž"‚¬%UUoßë½zû~·¼™y3ï~ïËù‘ð¢BИ‰vº{:8‘ñ"ëV¾S'—Šû%Ï¡,f°„ÁbÈ F¸MÓ’›×XTñ¢ˆ…U6&Zc¢Vý(b!à¶Ì€ìD‹"Tü1Ñ—Ü â&)‹6&Zc’‘1YÚdÙ9+êaÕC÷«ŸªÏ™‡‹@×X ³€Üm™£-; {1Ìâ‹@°Êøà‹¶¼x RX°`ã€,Øp½øþÁBc×§7nÛ:14Ñ5N5®´ð÷77òddtÍÀN窬aŒù6éºÎ£K¹;»Ûr¶žçq£ÚÔb–ô|U9vš÷ùIఘ°oQnæ¨Ñu]I’vìØF÷ìÙ322R.—ëõ:cŒÃh΄KÈ!7Ûó8©;çæ]”Æí‹ÜÜÕ»L¦ª ÝéûSbX¹x)J!„/_Unç*Ÿc—ïp /JóFë©9&æÐY×un6M“Cϯ“‡©Õj7n …B»wïÞºuk0Äó•äÓ”$‰§…±mÛ0 ¾ÑÝxþœË`šf=ó9rýÜ÷}Çq¾"ó…òðÃ`Y–$I›6m* Ü@Î_$çat×`L)å;Õý‘á[Éq-÷Ep¿D7î™áû[áƒÏÇXwa´çyF·5J}Ïô,Ba×-S—'¼› £§&¼»£/Q—¾³áÕ‹åRt<¹mû!Ô‘©hé6s‘åÚ¶Œ™§ûP5‹Ê‚R¹×É÷}îE*—Ë;w~°cÇ®¾¾þçž[·ví³¯¼òêc¯yzÝ“«`ÙÊ{^xeÝKîxô‰Ç—¯\uï}+V?úä†WÞxvÝKkŸ_Ï$<æFSá…‹„"Áî‹§Ùl®\¹rÖ¬YÓ¦M»âŠ+.»ì²éÓ§Ï™3gåÊ•ü²ÅìøNµÿ]âó§Ãç÷ÿ»¿¿Ìš|º®ðÎùô[žçY–E©fY¥Z¹\”e‘uóë°F]ܹã£F X3mf9ltlâ¥7¶FJz\dY²”E ;#9£’3¦ºAÜö©ªz8^üd8~`$q:QVPL2cŠlëÃe0\Á¶Sœ¨l4œ8š¬Ž7Iú)Ô‰7¦8qÕ‹ÈöH |/JGëêh]=+|t6’ôˆbÆ€›€~ ùÕ)n@²cEd?(8aÉ‹?":#et"Û:1!Œ”QDtbÀK^Pp"²ƒl\rÇÁ9üC,¢ºÙ‰fDu#ªVý dÈ#€l\9ׯ¦´ Ì¢Jç™·¶}>ykKÿž?i uÖ±4Ð&ò<§ÓéPª!„LSw]×¶Íf³GŽ=‡%IpÇumžlàÂO„ïóg”—a§”‚ Á”j©È†Ay¬B*ÆpjÓ4ÄG B+ E"Ah ‚Ðí{žç8–¦i²,Bxª>Bð·%2/˜¦Þéx‚ÐÚµkÇÐÐ`_©Tð<Ï0¨(ŠC„¥cÌ÷]I’Òéd<¯VËBŒa£QK¥‰DLÛ\Ù3 Šªë„R.ªª"IB«Õð<§V«„B¡D"&Š"„ —Ë<ùåàààÀÀ±ÁÁÁX,"‚iê†a ÄýþðÂOƘãX„Œ¡®ë–e˜¦i´Z­b ÑhøÄ‰¡P@–eßw¿ï»¦iž7òª!¥!B¨Ÿ‹òMÓTUå+ûØm®kóéC¶lÙtúôÐÆïõ÷÷ BËumQl B‹/>ÆÐ4uäD"644¡e”R ™1ßó8DÆ¡f³žH$†‡O G£áZ­Fægƒ€ü ór]›RêyÆxóæ•J… ÀãÄ4 Qªñ3Àϧª*„`> B0Ÿ¬¦!„€_t†è:ét¼NÇã|¦þª»×ÿ>®øu]Üüß5·F—EY¤†Á:vÇ¡:BX¡T³<ŸÃè‰:üŸÀèoN€x‰þìé;Ϭ_{Ñ*†¦oK f2šÖ"U5 ±°Îkžclšf³Ù~ÿ½-ýÛwïÚ¹÷Õ_ýç;ooÎOÖ ùj2•95|ú?Þ|ãË“oþúõSí~pý‹/Cñtº˜J•6nÜ^©6!ÒR™äí‹&“Éîu§Z­F£Ñááá™3gΜ9spp§ï1 #•Jq%8›Í2Æ:Ž$IÕjµT*5›Í.çy¹\æ_äŠ;WÄÙùƒ©I^/Ñ%ú3&¢SL4Y–÷ïßW«–]Ó`ßІڶßíŒÇs.cuÙK5~ÛàèX!)vbr'ªtÂÀ / œ0p¢ÀJB'ÒBÕžÞ¥ÿrí쫯뙿pÉ©³ÉvñT4Û¿ïȽ¿xò‹ÑhE¢#‘…½Ë{ï[ÉÖ“õ 0rm”k£2r&ãl®~÷ê¿÷ѵ§“GÇb7ݹôú…½ŸDòÀ,c?R…%Ê¢PÜb‘:­R­á¬hW«BïÃw=ðè]+Ö 2ÕÍ f¸¤6Loèá¨nÚ£-˜!n°*¦Ÿh+y¤§-!£(УØCÞIÙRý€ÎÆ€P½`A•U,¤°Ìr”=´þ×?3δDÕ±\W׉*+²Hu! ê:Áp«XÊ=zxNÏ¿-X0ÞüžÞÞ;>;ðÉÄDaBÀ˜ !ª(ŠmB!B*Õ‘ª*†©Y–¡B B`Ù”R "Y7° %ÃÔt+@`Ì••vµVÄï·…:c.Ö@[¨»ž9xr`ñ?]±bùD6Y,æW<°ì™gžŽ'ÂŽc1æ¶Z Ó"–e8®¡iaEUÓ"©’Ü"C$«ª¢AQ$Æ\Bp[¨[–A(„c.Šíè²Òæž# ¨"Ö€n`¨‚Ø0LM”šoýæÍOöíU€T!U#*!˜êˆRPˆ1ì0§T*Üvû‚»îºsðä€(¶±Q(fo»}Á¼ù=ÕZÑïØžo©PÂàKQo”M‹¦öåàñÇŸx$ž{¾•ÎÄ׬yôî%wÖj•h,xË-ó¦Ï¸¦·÷Žž¹³,˜ÿð#NNf-›b 5¢Rª©Pª×«„BIT(5›uÓ"Œùæ@°šÍºeÓZ­rìø¡+–×ê¥R©°üþŸßÿ}¯üêÅR©À˜kÛf£YÁRé:!òoÙ¶É×¶Þ([–áwìF£fZ¤T*øcH(dÌ׈*-Ó"üezaCXªÈ£u_{ýå÷…#ã*”x‰¬¨+|YüŽ=™Ïô޵誫¯\õàýåJž¯/Ëǘ«iHBµZY²ägÓg\³hÑm¿X½2ŸÏ9®a”1W–E¾ûTG²,ÚŽ®ªŠFTUU…„`¬Œ¡ë™µZe×ÛS©c.ŸD2Öébƒó?B;‡û¿J½bØæW ùDÁºíˆšÍ3uð*†Ûw} ¥rÙâ³Ï>?ÕÌÁY}+†Kôÿ—¾F[G5¬©U M• U£aaE#*‡Ñš¦Y–… ݼi[ß¶]}Ûv¿¼á;ßíi5`,:Q­cgCÁ`°Xʧ3‘h,†SÉ\&]œÌ6ßùÍÖüdM‘Ñäää¢E·%“IîóòºYiÈçósçÎíéé‰F£ÜEU¯×Ÿzê©ÞÞÞ¾¾¾wß}wbbâôéÓ/¼ðÂ-·Ürë­·®Zµ*•Jq”aÃÃÃ=öØìÙ³Ÿ~úéh4ÊuGîÌê&ø#ïÃ%ºDÒˆ®ݲ¬v»éÛE U-a ˆÍÖ»öfrÝcÙ†¶iï±#góñ–“½¨âMÁÐVL1ãŠ~¦ÜjXìÀÐÙ³ç]?kþ´i7Ü0sn,:IäOŒûåú]û“kÜ3r6räÈмy·ÿð‡3n¼qþ‘#C‚¬MäáÔäéñøÐxìøpH±ÙÁ£Ÿ;¥èÞñÓãÿzCÏU×ÍŠfËgb¹Á`*Z&ÚFúcyéãg3-£üh^¿íÝüŽŸ-ÿ‡ï_¹à§?;tìT© Š™l Œ¥?9>ž“ícÉR Ûid¶;,ч¢‰áxâØ™³_‰¦˜ÅÆXKõ„Ç;TÃcªR€Ú Âs0:¬°ˆìñ¯mLÕ ²˜Ç˜cù:$X¦A5¢Jr a¥ÃQjþ×Ç®yüáþÁ÷—.[2=ppßÍóæüàÊZvß=_/–rÙd±”3L a¥-ÔÓ™¸íè@-›¦–Í¥2‰l.•HFt;®nÙ¤PœÈæ’±x0™ŠÄâAÆ€t&‹+Õ|¥šŸÈ&d¥5xòøì9?¹êê+-^8ðÅÏ·Ž=xèðg@s=ßÊæR•j!•Ž¥Ò±Jµ X’[ü!D2ÕQ¥ZHgâ¦EL‹ ss“éX<”ÎÄ Ål³Uåej€*`MÑ $ÉM¬)–MT(jªU°ª€ööþ-ÁÐ޳ …<›v@›­*cnµVœùãéÿø½¿¿{Écã#Œ¹©tôÆÙ×_{ÝJåœëIéL¬PœÈ2¹É”eÆœC‡÷ßtó3®½f<0ÒaÎd>svlxdthÿgϺñú¿ý»+-^øÞûoóçŸÚJ5›K…#ãf¥Ùª*@ä– %Ûѳ¹Ÿcn2]®ä!’O}±ì¾{®ø›Ë®›9íøÀa×3‡NØÿÙÇû?û˜êHB¹’ç|²¹T*Ãð|«XÊÕê¥ñÀ(Ö@¡˜ÍM¦ …ù„߱sÇÆGøÊG¢~,›bM¹h3LŒ5…êP7Ж­ïÅ!QjøK#€Ð@X†HÒ äw¬F³|ý 3þ꯿»hñ­¥r.7™Ê2Ë|×ò… BáÈèТŠÿò»qÓͳ·õmŽÅC„ÂD2‹‡êr:o¶ªü¨(@à˜Ò05*”ºÈØõÌZ½´ëƒí©tŒ1ײ‰FDRW$„eBÕ¯›×ÿqûÖ`´Dœ©0ºoç¾`0™(\‚Ñ—èBú&m7S%È`D¡D’%¤ ! @$qÍoSBlÛ–Dõ·ïlÜÑÿaÿöÝ^|½ûžT¢øÎÛ›zpÍ’»—®_¿~ÃKë~dÕÝK½öúËk×þòùç6ìèß;‘®½ýÖ–O÷>ôù±ÁÁÁeË~>99ÉÝÐÁÔ…Õ IDAT] ±eY…BaÞ¼y===©TŠ?,‹sçνüòË—.] þ7{çýÕÖ™§ñ_öÌž³g;“‡¸àc°hBê½"@¢°1Ü&qf3Îx’Ù]gÏÄ3™8Åq¥˜æ˜BD‘eª‘„@ !a¡Þ%°¸ûÃádÏ$Þý)9ÙÃóÜòžs?ïó>Ï÷N(•Ê .ÔÖÖZ­VF#‹+++AÓzbb¢¼¼|ddD¡PðùüÒÒÒ‰‰‰­S×ÿ×ý¶¶õÿC6»Ójsx½^/6V´ê'#C¦ç+«ú•†ú‡ J'(W}ÿzõKµP¹€YËæÔÿ°¢ÿ†ÑJwp`fž˜’cµñ::{qh •Ââ Fßyÿ÷aûŒ8‰EdçP)l–|è`…ÿè£Ozø‚ÔÌ\‰ÊJËÎÈ/®¸tåv}kF~éñ²ÇãÓí½‚D<I Ý¸S›²’™ž70>«0¸t±r˹e—žHuR­õ›ž!nI%ŽšœWt:lÏ¡}ŽDGÃ+Î]z<ú´³OHKÍ¡qr ÊÞ‰'$'WÜëì[°¸DóÊ¿¬âää墳S9¹ùŸÜ¾7¡Ò/¸Ö'Í>‘%0ãd/±í»1zÖúã†%[ÐꬿÕìXsù€›ëk>0Ò`·[ ¤Ñ¨òò¸»vý’ÃI©««q8l«­­…ÃI‰Š:Êf³ææf“’˜ éÑ£‡ÝÝgÏV‰x¡pÈãq©ÕÊÆÆú¬¬L'%33=--õQK“vY©TÍ—äÃà±éÉ'Šr¯~ôÙüìȨàKçó ²s¸èˆãYœÎ®–â’ü˜Ø¨½{w‡‡ï}÷Ýwx¼n.7;++sddBõõuÉÉIYY™T*ƒA=[Ñ×׫×/s8),£¡áŸÏ+-=I¡FG…6›eqQÞÖÖrüxFNNV^·¸øDkÛÃ9ÉL 3Mz×¾¶î¶;LN—Åës¸ÜÖ@Ðk¶4$€POO×ùógÏœ9Í`Ð ZQQaUÕÍŠŠ311wðàþ>¸ÒÔÔ}<..æäÉb•J18ØO¡òò¸T*™J%—–žäñº%’gN ‰¸xñ|KË7gÏV¤¤°E¢1Éå²ÞÞ …TX˜Ÿ••‰@ÀJJŠx¼î—`´×ç)Ùé²ÔÖÝ5šôÁ ß‹ÐÚ÷aô¢BŠBÃÜ“ÃÍàõvädçæÿªêówók–––zçέüüÜðð½‡Ãœ;W©T. ôæ3™tðF²²2{zº‚ÁupIƒY‹ÅF†¶Ò&:¶¶¶t£A¯Çkwº,[KDêŸ:F[}/”F—Âà1 uÌËÛ½­ÔË0Ú±hœàµù|V›Åe³®¹.«Ýar8- F»\¯× ¾0<7_ÿôËuÍÕ÷ëå?jª›¦&æßyûòÙÊ_W”Ÿçr¹å¥…'¸|øÞ¸H¨Ñ¨‹KOÅϤsKõí#§’9y{{{J [­V°¹¹ ¶%X__—J¥ …B¡€ù`0¨T*9‡ëéé@¥R1™ÌÛ·o ±XÜ××—™™©T*‹Åt:Çã©Õê––ƒ!‹777Á¾ØVùQ_Á¶¶õÃi=°áñúNçÊŠÎã°;,FZáw»œV[Mu½T®Yƒ¸rí–)<Õúf¬›SÖ-’Îü £=s&rÍHa¦g †Ç†Ù¬T<–$š˜~,çž8yà­£"mX8þ°¹€§Ddgå55¶LLÎ"PøD,áNMýèäÌœR7:-GSŽÆ&ò‡Æ…¢)™‰@áóOœºW׌%²EAO_x?2EIÉ=p >,–ÚüÀè„MbÁP„Uwñjl,ëxî°p|ìÉTFNa Wu¿Q¦Ymï=‡äž>Û5(,*; P™Ÿüå³§âéô,njVþo>ü/žX¢ò3Ö `É9´â°~7FO¯nüÛÔ–u»pû^¸>Íå¶ÚmV³Ãa[[ó­P,àp˜×_­¨¨P©\r¹,=³gÏ›8¦¹¹‘Åb@ Q,ãí·/Þ¸ñƒb³Y RÇÆF£P‰<^÷ððc8<áØ±È²3'[Z›°¸D Ñ?УPÊÔšÃêòÕþ x̯o¨îìjùôú5$*!=#¹îÁ}ƒIJböööÈd’„„øèècOžŒ‹Dc\n6‰D ‡††ÉÉIaa» Z{{+“I‡Bã Úûïÿö³Ï>Åã±ÉÉIÁ@EÅ™øøX(4îÚµ?54< ‘T*ù«ª/”ªyÿšËí±Ù&‡Ó¼¶î¶X [–¤Ëm][wëW4õ Õ`œãÛInð“ár9¼^w(´!‘<#‘X,‡ÃP©ä3gNóz;“Ø4 ßÝÓVx‚»ë×Ðøã¡¾9ÉÔ1ȑȨÕ¥_U}‰Ž ‘±OÄ£n·stTˆ@Àbb 7o~™”Ä„BãRRØÁÀü¼”@À‘ÉÄÞÞžééÉÂÂ|$J$VV–“H„ÊÊr>Ÿ÷ÁWÆÇGÛÚZ¦§'ëêjâãcÙlÖ;·JJŠvìx‰DtuuÌÌL±Ù¬×_-''‹Ïçed¤¡ÑÈÑQ¡Z­¬««Á`P¹¹9}t:uÿþ}o•”}þùu<‹Å¢ÅbÑÂÂ<›Í¢RÉwïÞîêêÈÈH‹ŒŒ(++ÕhT.·í;‰Ðá4;œf«mÕhÒß»ÿµÕ¶zAËÿ;1Z§W'"¡{÷½ÁÍÍ<æ£Ð°Ão…_¸XÙÚúH(B¡Q¨ÄÚÚj6›µsç«IIÌ®®pYÂá íí­f³±··‡N§ä--©Aƒ üU“ÅbÚØ|F‡‚~pïb´Ãi7T?uŒ¶ùC*“[¹êÙÂh±ø™Lº¸ÑÛúG½,mñø¦Ìà³ûN—Íã0û—Lïmµ=@Œ¶ÛÜ_ݼ]ÿàá­¯ïÿö½Zõ:óg׫>ºz­®¶ñêÕ« µ½üÎéѼ|N§ÓZÌN·+à°k«¿á¤f T$‰B%*Š­U¾e«T*‰D§Óu:@@"‘P(*• Ο×jµIIIP(‹Å²X,2™>;; y<F£P(„@  P¨áááÐßg‚aÿX/`[Ûú!åõ­9]½^ßÔÔ ^\ØXó›k÷šÇ[õÕ©\½#ô×{­Ën`Tᘲ“¶ÍIÛæä·0Zbõ,X=#Rå1E¢Ž‹&úûÉD GTh–„B;õWoîÆ(jõÒ³)‰9tàHYY¹dN._P иJ49c°»”+ᄊ¢ Œ©¹…qñ4KB¢pí=s9"ûÏÿ²ƒ‘œñ¨«MbœÇ#“vO@0< %’hbñ…BûùÏ_夤---OM>ƒÁQx}ìÉT`˜•)iIip$®»§Igÿjç/Ñ0dYqéü¬tH0Ò×?4òtná¹]ãÜœ3æl€&|F?3oþñFƒÆ°ù?ä÷Ö\>ŸÃe6­º\ÐÀ{ñ"¨Ñ¨p8LddDAAžJ¥Ðé´.—C«Õp¹Ùáá{Q¨Ä>,óÉ'ž™™ZX˜'px<¶««#''+,l]\”ëõËX,:,lWn^ÖÐðž€ND&¨5‹°Ú ˜ÌÏ¥Ïæ¦òò³9il<õ•F¬®¹säÈáÝ»ÃN*YZRkµ"O£QÄbÑà`?‹^ZR†¼<îίb0¨ÁÁþÄD8 ½víOr¹ìÙ³ðzzzºàð„_üb'‰8sæ´N§žž ø&ós0¡ 6Àd˜ásÌëï²N]]sgÕ¨·Û­v»¬‚¹\°¡–ý~¯Z­d±D"þòå÷bc£1TcSЇFwt¶œ.+9qF'Ie³jÍ"ˆ9t8<%•ÕÙÕJ£“ht’èÉH °&‹âSS“gf¦ Ð¸ŸýìŸN*Q©óóR<K"úúzÇÆFHNU*ý~ïÄ„¸»»S,™ÍF‘hL­V^ºô6™L\Ökkšÿ|ízõýüñ×·n¨5 .·Ùl1ØíÖe­ÁðܦQ™ªnÖdgrR3étzyy™^¯ßÜ­¹E …‚ÅbQ(¥R Ù‘Ëå †D"I¥Rpn ƒ@ #==ƒÁœ;wÓÓÜÜŒÅbi4‘H$“ÉEEE“““ßž‡:Ó?ÊÓßÖ¶~`Ym³Å¦Óéîß¿»¤TëÀæ†Ýlò8œ7oÜZTé [v\¯n_4oJL›SVpâˆÑ/¶0zvÅ"ZTÉT4™ª\Ò >ŽC£Ñ²E…xf&·ðÄîðýÑѱOž<™šE#1;^Ù™ÎɘžžG¢00$jJ"Q­Vl~ÑŒ‚@Oc¤f?’òûÃpx¢L&á0øÈ£Ñœ´ã½ýC$*+"*vA© 7…#"8‰Å⇆†hjdÄÑ’¢â™©éÉÉI…Â>¾öረ¦¶AgGFD}xùwx8ŽMhº_;+žµ˜NoH¾l™ÖØTn@îJˤ5ð}nôV5ëœ!£sÃ`rZÍŽ€g ØyÜέ ¡Ð†N§Åá0„§¥¥‚s$ ¤ÓiSS“÷îÝM¥’ÛÚZàð*Q …6ŒFƒBãúûùyo¾ù }ø°©½½Oؽ; ‹E75?Àâ*ÁhZY[÷¬¼`¡P8"€&Äì?°ƒM„#â D̰pF£ìÛ·§¸ø„F£’É$8&55y||tp°‡ÃÀá RéœT:—}|Ïž7Édbww'‡Ãaø|J¯G$»|ù=wøðA$ª¹¹qzzÒl6@6@Tò¯¹Ár¡×çôxv‡¬š7ü†U]݃û†Ud ' ’4ø¬<×úºßh4$''1™ôÞÞ‹‡'\¿þW*H¦à{ó rÞ{‰‚Íÿ7{gÔÆ™§ñýºÉ‡¶‹$‹Ì`#ƒÑ-µn@BF0!±lãu2“d73ÙJœ©M&Él2©­ÝÚr2ãÄðÇ 6À tÄm@²n¤º…®îVë@WIj+žÚ­šÌÎìò”ª«Õ%©©»½ïÿ}~&ɬçñY;JŸ¬òã#L€Êá2ñèÆéi-…B¢PHCCr‡õÔS»ÛÛۢѰÍfa2éd2ϯ(//«®®Òë—ñ. Ëã‰"x¹ ƒAÛµ«´ªê‹Å¬««éïï“J%;v”P©d´›L6(//ûÅ/^Âë"¼^7†åçæf¬¬¬$ŒŒ Q©ä’’'kk++V¯×][+`³ááoVV¬§Nر£„DªþF.Ðé–2™t6»Ã?<·Ñ‰$ û/u} û3Ù$Þ üƒ6úžaIT_óÓ²ÝM͇oݾYu°‚ËLf}&“V«•L&½¶V00ð5™L,)yòèÑf|§H¤j6P©Æ …œÏça0h,Óétàå7øŽ…BÁ‡ÙèõõTÝ/ò|°Iiz³¨cS?ªþXQGM.ZÃaKD“éF¢¡$„ ÷mä~¼9‚ étÆ z>>árÏõï¿÷»KW ÷¬_}ùõ§º:/ötww_½Ö :­h":mzý²ô:ì^÷jô«›ò)í]›\XX°Ù,Éd²ð€YÌd2:N,³X,N‡¢h¡Pp:§¡¡ÆÃ‡Z­Ö»wïZ,–‘‘¯× ðV«íèè¸zõªÍf[^^ÞHÙă>þ·¿‚Mmêϧôz‰'’ɤÝn+dÖÓ([ó£P,\ë¼Øcµ{Ö1Ìè‚~ûÉuËZÁÃæ#ØL›~g£—é{¡x¨€]!²ùÕ àöÀ7Ÿß¸)‘H¸\îqåÌââÑcí»~òS‘H¬×f¦f«âiBGûsƒiR;ÍØT& œœ4ØAw8qG³p€Ì!2ø#c*µöXZ+¨s¬€JÅ8“J+/Û×xøÈ‘q‘°FgNj§}>ßØØ˜X,æ°Ø}·{iTòÞ²Ÿ‹¼—^·Äf³y<^KK‹D"•J¥ƒùlk›Ãj³Þ»×õÉï?Ó*`° íx»ìÊåWÈN.‚á7l„0K{˜ÑùÎý{O ‰¥1,‘Æà ­Eþ@*‘L$Á`ÿAÃá·´´˜ÍfA0 ³Ùl%%%T*U©TÖÕÕ055•Éd@d2™l6»¯¯ïÌ™3eeeR©´±±qÿþý íõ×96v‡Ïçr¹l·{E‘L&ó³ÓK$ªøúöí^¡°¶®®fp°¿¦¦fëÖ­GÅÎt:Á`( ¥R) E"‘Ïç³Ûí¥¥¥t:]©T @055…a˜Åba±X\.wqqÑãñÈåògžy …ÒØØØ×w o‚ÅMÞãÿý˜3|÷Ç7n\|¿xú:Ã0NW±ÛíUUUT*U¡Pœ9s†H<È`жoÿ;`(•Š––¦'ž(ærÙ¡Pí\.»¤äÉÆÆÃjµ’J%‰].'†a###|>_$ŽŽ‰Ä-[¶477/--Ùív@Àd25F£a³Ù"‘Èãñð³Ù¬ÛíV*•‰d×®]2™ì£>l6[¥R;vlëÖ­b±Øåréõzƒ±eË–ÆÆÆ¾¾>&“É`0G2™œžž€ÍfkµZ¡P¸gÏž–––@ `2™h4‡ÃaX£Ñ´¶¶VUUUWW‰D™L&—ËÃᵇÞ=¸ÃÆÖÖŸƒþt:ùýª¼îzË糋‰Ïç>úèß¶´4iµß9"­­X­æ|>¯Ñhø|>•Jâñx%%%‰D§Ó ÈårÁääd.—s8à‡èûî3•Jùýþœæl6ër¹®\¹rïÞ= ˧ÓI<{nc“ðŸÁÃöëÇüilôæÃMý÷õÇl4”Μ”ÄÐH …Cp$’Fôþ‚ŸV±X †ã©Ôzh-öé…K?ëþìÓ®ß}øoW¯|áõ¬é–MçÞ|ûg§þ¾££ãg§eï½ÿÏãÝU—=‹¸]Ðá øâ~Ùï‹bð¥Øƒ,j}Gh4š?üpzzzµŠ—_oÄËojSÿ·…&RþÀÚýäàBÞ¤_\óyÃà•ËŸ-N4‹¹¢¹_ÿk§-Œ-z3óal&ŠÍFî;é = ]¨o;NäÕYœš†ÃõR©ðx¡¢¡©©ôαçO<¶u—Wk¶Øfgîr¹üÒ{n\\º·¸¤ãê¸uu£ª O8‘1íƒ/aÕ4 +&&´s‡%MÕÕ¤o5“wçø\ÞÅ·m“ˇÈdjM­pyyyÅaïî¾ÄdÒÙl`lì—Ë-.ÞÖÞÞ¾¸¸p÷î]:zð`å…Ïþà÷û¾üò‹ùùY£Aoµ˜çfG‡†'Õš–¦£û«Ê÷WµŸzQ>1ï]ÇÌPnÚÝff£õaì½ 7WÖRÞH:M Hj=žJÄ`‚Qõûý8,Ãb±ˆD¢ÒÒR¡P¨R©\.W$™˜˜‹Å{÷î•H$ÃÃÃõõõ¡¯¯Ïív›Íf>ŸÏb±úûû¥RiQQ‘P(¼uë‚333½½½ÃÃÃzý2‡Ã©¨xzee¿BÚl@@¡T*U2‰NMM‰Ëåd2¹´´´­­Íh4::N§Ó5V«e0 þlmmݾ};›Íîííår¹4mtt4NÛl6‘HÄápVWWÍf³Á`T*•MMM;wîÜ·oßéÓ§çæfòù<Š"~¿?‹d2™@ÀFQI¥Rxv“ÇãºqãF ÀÁ ¾!‚=r¹œÏç«®® …‡Ãl6÷÷÷P^^& 5USSSqñ62™lµšA¬®®***jn>266F§S©T*NQ*••J%‰Š‹‹Ÿ}öY‡Ãá÷ûI$NŸ››ª­­e0v»=»\.—Ëe±XðævéÒ%Çãp8H$Reeåððp[[Ûã?N¥Rñ#)‘H¶mÛÖÞÞ®Õj9‹År:©TJ«Õâ®Z.—“H¤¢¢¢³gÏ‚ h2™x<óóó‘HÄb±h4…B!‘H*++‹ŠŠÚÚÚVWWaþÁœæd2‰¢H, …‚ÝÝÝÁ ?NÃ0ξŽÁ0ŒwÇb‘D"‘Ïg C}½pË–-ír¹œJ%WVV®®®bÖßßÏáp˜Læèè(‹Å*--=qâ„Åb ‡ÃL&óÀÃ0›ÍÆãñh4ÚÊÊ ˜ ‡Ã¡P(—Ëáü²ÑX*•‚ážù§nÃ0üàµ?°_?òô¡Ú ¼ÛÔ§¿9÷ö›ÖÛÂÔò†6®%‚éDˇѤq5ˆbY4_ Å0N1Ü ˆ¢h2ƒ!Éç1¿?xþü'W.Þy±çÝßüKçů'h4XϽùö¯~ùÆåË—ÿpá¼Z3ærÛì‹Ã±ðG<îдVßÕyŠ¥ y ðlö~ü\&“Áš…BAŸÏwüøq™L‚ N3AP&“?~‡ca677÷Æo:t¨¹¹ù¥—^Z\\Ä™RÑhT­V¿öÚkR©ôå—_6 ~¿¯Áó@6iˆ›úÿ£H‚†a¿ß›Dà4 û]`Ã`O÷5ËŠ;UÀ”˜09+X5€749=¹¤Ÿž¹£ùö ɨ©½=rçÅüÕSû ÕTfo¿\o´–6ïyª¼^ÒøíÌ‚ΰçi§^¬œ™3¹|+Ax|î‰'ÞOf«§—FÆ5l~-‰L•ÉN~ñÅ—4£‚Pyâä j¶åè1ÞÙuéŸÎ½ÉáqI2AW)%GšJ÷ìni;Ö7805=û¼ìxÅÂ|ò±Ã ö Ô5ˆO=3¢ûõ;ol–¨þЭþÎË×dÚÞjʳ/þÃ7³z0…Mû %¸fLcóÑÌB,;Ã6lô|[XËéÃØ;Ÿ|¾²– Å 0šÃÉD,ŽF!º?÷q¡(zëÖ­^xJ¥²Ùì©©©ññq‘HD$Ož<©ÕjF#Ç#‘H,ë­·Þêììäp8‰dvvöìÙ³ûöíÃ[¬jjjÞÿ}Fsð ±²²Òj]Á0,…œN§XÜ@¡PnÝêóz½×¯ßàóùBåøøxCCÃöíÛÅbñÀÀ€Ïçc±XÕÕÕÇŽãr¹7oÞœšš …<òHkk«V«%“É E­Vcær¹h4ZUU•Z­þàƒ„Ba}}}oooOOÏþýû«ªª^}õÕååå\®Ã0Š&c±˜×ëO&“‚&‰x<AP¡€ƒÁ«W¯¯­­áÆÇ"nàÊÃáp0ÜXFs8‚,--577ïܹ“ÅâLNNþüç/—••Q©t¥R9>®âr¹{÷î;wîÜàà7'Ož¤Ph*•*‰¨Õj"‘H&“ñ™;w>ÿüó6›Íív "‘8==­T*;::ètúðððüüüsÏ=' O:%—ËOœ8QUU¥P(Ç;w˜Lfyy¹B¡xýõ×wïÞ]SSÓÛÛëõzÉdò£>*“É ESS¾:‹ÅÒÕÕUYYyänîé;vìJ¥.— AÜÄOLL€ øÊ+¯:tèöíÛW®\!‰ÅÅÅÍÍ͇ãÍ‹ÿ—)!ñxAÐp8ÜÝ}9‰d29‚b18•J¡h2Â0ÎwŒçóy§ÓÅáp{¬¨½½]­ž ‘HBa=>^_­V¦R©BIII{{»×ëõûýõõõ øýþÉÉIš››õz=Š¢Á`0™LÂà0 '‰h4ŠaX èéé1™L8"ŽJ IDATEÑ4FÇã‰D ‚ h7F!g¶'ñ)‚ „Äb1ÂYƒq|ï BEÑD"…Oÿ‡Ÿó]¿ÄAñ°CA6îþš°ûƒ^3ëèzF"0ðšý~QÞm2Ú6ð+¸6\ÊŸù2¾©¿(=ÔF¯cÙ558[G2p ‰ÇñH$…aÇÙ')ŽC’ÍæÝnoWWÏÏ¿úøü…ß¼óÛ/oÞ†¡„Çð¸ƒV‹†a“Yoµ itlèë¯oƒÕ²ªŸ»øéµµ ¼žÎã$°B¡€Ÿœ„Âl6›H$|>ŸÑhÄ—ãr£ÑèóùR©þ4‰¸\.§Óét:ÇF{3i‡7F‚¾_ ý§‚ÆmjSJ$Ó©tÆétŽŽŽuËùõd6ϯ§#ÁµË=×Í6W"‡¹¢¹÷Î_µ†°¥`þ;Áæ#…Åpn)œÖ‡R£:«à™ÿdïÌ~ã8ÒþÇäÅXÀF`džüà]ø”C‘"ER4)R’eØðî:al XŒHvÀ0ÃdQ‡%GÐIR¼ïÎÁáP$g¤!gHŠä}wÝÕwå¡´múZ¬È„ú¡§ºªæ«£¿úꫪ“?oí_Zyh“MÝZ(Ÿom{¾µ=U,å×7?î¿ÜÔÑýË÷ÎŽÎ,L&²­'þâgùË÷Î^¾5øRkÇ«]=_m™èÆ‹m§~u¤ûÌ|¾0‘Ìöœy³©µã‹þKí¯8ÒÚ~ýÎÐ|:ÿÆÝÑWŽ{ëwn ^½q£©­í篼ºVÞ¼|ýf×ëg{ò¯~õ›¿œ˜úêÆ­¯¿þó—^:ÚÙÙ|¼³µ·çÊÝ•â©löóË_?sæå¶öW»z^}­ï_¿¸0¿ñ0¯‚¬‚î!ÿtòcÙe¿ý ¿¬2ƒH|hlB¨›Ð„Û¶E‘0 ØÚÚhkkkmm=zôè©S§®]»¶²²¢ëúîîî3Ïsÿ~in.ñÖ[o½ð ï¼óεk×:;;ßxãL&ãyÞµk×úúú:::º»»Ÿ{î¹ÞÞÞáááåååöööæææ\.'„¨T*…B¡R© ž:uª¥¥åðáÃ---ÃÃë«Çñ8w÷öj–8w!Ä„0ËiÚ¾6ê… —E³íG†Àø ¹ø¤ñ  …B{{ûñãÇK¥’çy¡±±‰žžÞ#GZJ¥ÍÍí›7o÷öžùäÉ“'K¥R¹\îééyùå—óù¼bxx¸¹¹¹«««»»û‰'žèî˜`Œ?¾½½ýñÇomm=~üø¡C‡:;;«ÕêÊÊÊÐÐPssóûï¿ûöí¶¶¶gŸ}öÝwß-‹333}}}GŽinn~óÍ7§§§777[ZZžzê©·ß~{gg§R©;v¬³³3N«ª:::ÚÕÕÕÞÞ.ùœ={öæÍ›žç!D~ð˜Â@†úÙg_4*cŽaX–abËSyAT(Üïî>ñÌ3‡ÎžýûÙÙù––Ö¾¾S¬;Ž·´´túôé'NŒŽŽ655=ýôÓ}}}©TŠ288xæÌ™W^yåÅ_”í~óæMiZ²,‹Rj†išr&B¨(ŠçyÕjõüùóz#BĶ%ýcÛ†ûï@„0Y?•!ò§,EœçðÓóùæ@rIÿð‘ë)Šc—ÚaB·­ò=æs¹D2> ü£äO”ÅhO:¦Òía©šE€J,€ ÆÔ¶!B$"×õ)åQ$E;þÂ… —>þø“ßýîßîÜE¡ð=áð@„e@gk{£\^ÇÈ!ØßÙ6¦&R”þ£­2B¹i´çy¾ïö$ÞRCÞĨ-SÅ‘åNvr6 B(cz–‘ƒ ˆßÿ±CÅä@þwEÕ Æ]BÈöö¦K‰ˆ|¸¡ëütñêÆf•…bÏÿù?.WL±fˆÂhoEw¶´+“éÛÉ|Í]|XÛDüöÂâäbŦۀ?Pì;³é[“É|yo׿™µÊçWoÝŸ_Wì¡Äâùì½eð:+(ø?'ÒWGç+¾¿§_™º5>]®k_ŽÌçVêÞßS7ö¶EgSS3+åÊ–ªLÍÜžž+5ŒbUZZ½1™Ï.ïX¤NøX2uîëkWï \Ÿ¸2:º‰pÉ´R•í²…nͧþåóþ+³_MÎg÷´-_q˜Öð"ôr$X0höG0:± ûAÿCËGž ŽÀ  `øh¸!VUÿL¥233s##c³³ó–„Q$îÝ[mm=ö‹_933·°†{^ËåïŽM MLLÂÇK§³CCÃÉdÊu}Çñ&'§“ÉcŽëúºnf2‹ éññÉ‘‘±••µFC C ûÁKD" …ÃÓ€_ž»hèÀ÷‚ Ø„ õ=á{‚3?ð…e¢Lzi|l:™È4êúÜìB6“§”s=;;?55ƒI§³S¹\^jåº~&³8<<:=={÷îÈÊÊšìÀ’S)å¶ 1¦”ò¸WG‘¨ÕýýËåM!„ë„;P™ìÐø†W*ël[8ŽŒ'ØáÌwÐuBÎ|‚Ž ‹#ÿ„|\ßu]ιôÈ—Eð‘ëé#‰1:> \Á>¹V’Äýà£äO—?æÔa1§´Gu(BQÄ£?Œ’åûcŽï‡šf|ôÑÇŸ~úù¥K_}ðÁ‡~øÑµk×—¦&ç&'f§§§ç槆Gîä—S“#©Trvfar"qëÆøå‹7öv5F½ 8§±fÒŠoêÌB`Œ%"KofÆ¥TBp†Œ1Bˆ´OËøÒaQ®)áœÇïçœs.Iý£äÿˆ-2Ƈ‰À·´Æf©8\Z£×+{£¿±F›ßÂè%ãIW˜ØvŪƊ¦›WQ¶fTxT¼Lü¢AVPP`É »$*©x­jå6k5ûŠ褨¢2ò :NTêù:*šî×moÃâ%• ¼GüÅÊÞrw›©ìU,§dð¢ÎJ­˜d²]Èî7¬-講l ù¹=½ Ñ|Í\©Y«5c­ª×ˆ«:a±¡o`^„4¹S[5ÉÀ·qOGÿ0y&jVÎr–I˜2Ù¼ŽÓÀù1ŒNn¡øðâ.±/ Mˆ…@ù‰r]_Úm*Š“‡|äyÒ ”Ë›MMÍ=ö³±±‰8‚d ˆ8w9ÿÆÇÌ÷C‡PÉŽ$.pæ3êI¤Žùž!v]_!µbìÑfG¶ ¿Ód„ý›æ›¦½?‚dÉÓBÇñlJ€F¹N(áæû…BS­Ë—¾ÖµGA$m¨q}GÏ 8w- ììÈBÉÌã2›HÞrxÂሄï‡ûµ••)os IJŒªªÇö×O£¡Ê:—¬¯ë¦Lèº>ÆT& ÃGIö×Ûþ?’#Œi¬ cÎþv´,ë¹_8wM~ð’Evжpÿù˦_H¾¤Ä6€†|A‰Ë™ï¹‘LB°#kFŽpb ‚xg*†‚R¾¹¹-Ç]quE‘‚B¬i†E`L¥½9.i‰jµþå—ýëëe!„ë„q`“£åýÇ_ɾ1þznä¹Ñ~ŒŽûÒOÎÇ öctlœ–óçr>$6ÉJk4|æsLìkôüò“1@ 5t`[X~Iå4èÓO??wîü—_ö¿÷ÞoNž<ÝÔÔÔÕÕÝ{âtO÷ÉcÇŽuo{áÅçÚŽýõ+‡_xíµã'zN·íj9Òý·ïœ]Ì®¾B÷ ´ïûòlð˜§]×½™e¸€îGá0 c§ñm"¾-ÿý5| òH×7Lcžð½J©˜˜žÐê5µVÿòÜÅRy—…¢ ¢úèRI s57gŠ´ýŒŽ– S¥ëT,ìÁ¹m³@D‘‹œå$ê ]Ù:,‘­¥¼oy+ RÐøšÊÊ0*êN¾ŽÖ‰XÖYNÁe.Š0LWA^¡«¦³¸g¯©d8‹»ZÑ ™‡Jj[-¿`ù«VSy^c©­zð‚ ÍÖÀb@SȲ`YeT”að@'E¬( Ó°³:YÅÁ®µE•,ª$±c§kø×¨È ü%e‘—ÐðatvÿãG—Z¾Í…SÈÔ†¶ "r’$í»†aIdÜÙÙSMZ=)å†aÝ¿_:q¢¯³³kffNzD˜¦­ë¦4LX€7êÎÎÆÔ²€i@]Œú¦LQâé°-‚ 6ÝÚÜÛyX¯UµZUÃÈ6AˆPÊÇÛÝ­ÖëŠaXŽã™¦íº>„¸^W¤>’e1¦R·ÝÝj ×õ¥2²,¦iSÊMÓ®Vë2!¨§4L9gA£nèpxˆ 7 ³-bÈ÷„Ò0/^¸ª*¦´@#D, H?Æi¦”Kû=¥\×MJ9! j™#Ç21‚##goW±L¬*%°©ü J¼ZU…+Š&güëuŲ€,µiÚò‘®›²-0¦Š¢†õðᮌ`ÛPÓ .ß¹ÎG¿lSM3s¤Û€L"'LÓV­Z­Û6”u«ªºl>×õ ê×Y"M3d*×õkµ†¢h²“H<66ýþ… 'Ø…€©ŠõÅçýJäÄÓT[>2thÈá!gebÓ@9ªb) Óu"¹®FÝ€€:ާi†*Ôj J9H×M×õmÊ‚mÃý"Û=îÆSM3âž)»Ö¹sç%F3ú¨El‹›Jeâ{Û"²m‹X&6• `ñ½,)Á®ìÒßðSòqþà- b§ŽýË[rm«Â÷}¢Í†Z‡z.õØoôüyòS:€Afж0£‚L¾uÛÛ;Ÿ|òÙåËWúû/þþ÷ÿžL¦–——WW …µR±°^(Ö7ŠKùôb.‘[Jol”6Ö·‹…J>·^XÝÄÈ‘ˆ¢È÷]éŒ!•“Vdé€Á“»GI°B „¤ÉY±ÿ0BÏó¤±Y·ã8rß8øðQùþ±w¥¿qÙÝÿa>åÃ.°@›q¼k{×â!i8ˆ/ìH<[——¶ÃKÎ"H¼‰³^[¦¥rx“¢E‘â1WuWWwuuu÷ˇ'¯m@'|( jŠ5¯_½nÎüêÕ¯^ù»ãLÎä^0Í#$‚Ò×…íÒ0„¾33ûè$p¡KWgqØ&0€ÑËüñ9,ˆ¤?zHXØÏaÚ‚e^ßenËÂŽ…ebwX'îQíòês{hØ né˜í*ø¬«>m‹5áwX޲mQí¨r¹§¶X~ÁFœ°gêMîKøøD-ön\ng°§ë@}rÐo×°« Ú»}{XÁn [ÖyµÙV¬ôäV”ìéê³¾ÞÍ¡Eý*+÷Hl+ØàðÀB£ŸokØ1ФùÝ(m©r-)›Ü~ŒÞŠëW®ÎŠ’g ´L§"ɵÑ2ÁÐ#âQ„GÖ:Œä ¡¬uˆ¥GZë¶¶vz½Á7Ô5`,“Ç—Úq%= bhÁMû4HMQW@‰’"MM¡•#(Dp“¥Þf%g:M­s~@0EÞ2˜q7 Rò”J(åH*E´xQJ-¥¦”÷zR 2÷­UæòÁœÍJDó¹­C#ÜT%D!gf.Ž„V™19Ð?"åÀq.…PQÈ£’ O± r„Ô.¯MâH,•Ìr[™Ä™$ÿ‚ma8CÈóâèèÄ9ØÇHBá BàÛéôÉ]–5òLX²,Ïóᦵ5hm(åQDpþœË4µEQRÊ‘µ2XUÀèç’V–5Îd0ÎÐ6Š£J+ûµEŠ]AbyóÆ;$–6+ÕZÙÔ‚ÁÍFs–˜Ä1ªI,SS`ô’Í|·ÛG;ã˜"ù$Ërœ&á4Ç[eÆÈQc²("ú5Îp †ñÌÌ»B .¯#ŠE³±Ž8НÔ4':W2Ã;;€ÑØòdŸ§ÔóF«/¶:"ÙØ>€ÑÈ>ì‡g[ Ïä)O»ÅPH•˜$ÇE%„ÑøU«TrãÆ­™™w'&¦.\¸0H QWƒ¥%ï Óí=’8g]^Cý8¾Ë!Ñ6Ïs€Ç ƒÇƒ˜ñ ͧ÷Wj`Óz =÷P8bhÔ6ð@3dX|!ÿ¯^?“3ùžDªDistttçÎÇG÷¡*Jk *ãïÌÌîvp‹!r£w¬²º)êe^/óz•Á*ƒ5 kîXp7ôw³»´jjø„ø;´\æõ½8ßT°ÊÊm ¿=$kq±«`éD¶Â|[ÃRGïYرpßÁ½8¿ÓV+4ß5°)ý½¾Z‰ÓûI½gà?t6Tu§—|Ü5k ì|Bê:Ù–‚m^,÷Ír”7B·ç Aáw]÷I߯*X¦°ÔM×yuÃ}U-÷M“–> |#®7%ìep¯çîõÜA«¤Zj›FhWy±ÌÜ2·ëIùM[ 7ÂòïÆ6Ö•ÐN “Š$“:M²ÂÕ¥d_0ª(‘$…«ãˆ“X`Äû ™Í¾FUÐ'ý^Œ! “ä¾€,-(‘œi­2 ýú²Ô':/=d©W2à¬in«ÜVJfôàµr["–Q…œFU¢mnËD[F®’›$#Ž|€0 'Ç]) 2Fr[®ö®fT…ÅÅtJT0DíYê]^Û¬$±”"Åè)g‰/ ß#·n¾Ûï¤diËýøÊ¨‚Ði”È(d¸FŸ‡!±|üQ,“8Œtº¼ÎRô)%ªp0 ?àïg9HÀUSäÇG2g_ñ¦$Ú¶Oû…«‘1‚$NX¤ÆE!#±H´EžI–kû½8 ™IòÔ¸Ô8 Ü „ÃL3IŽ «8ÓQÈ(‘Hº°™— þýb‡7}£]^ npº‚AY›•ˆ˜•ÌH,«|AŸje .¯¥H¡†Ô8¤=˜$B†þšh[zÀ%å,-Œä8â6óœi|N(‘.¯JU HÚî÷â×uw?·åÓÂèÁŒèÉÎOF£GTý”z¾ŒFR+"zÆFÜ.‰%Õ'QÕPÐ2°. d;–YÕâÐ’ðiXµ¬Hh)h2X°i Ÿu“ ë –B¿¡áóA}·WnX¥ÐèvØÒ°©`;uY6¢t…-á[¢Z‘UKÀ ¯[Zq}çHþôµ·VŽå)N™ï¬™n˜„Âõ¸=ÌI¤ÛqÚyŸ»M»ÔÒ”@Lõ¨'‰©Q<ê‹£¾¢iJßeY;NéX §qÒŽÓM……61'aÂ-ôXÞ¥¶ÇòË÷Ž¢“0éì Ã{,ï¬ÏÝ£ž DÑ¥¶ÏIêî8 “@,ƒvœ¶‰!IHwØ¡*"U¢…t]jû"¥oëj¿M:$;“H•‘ö‡]q'§‘éq{ÔWGì,.ÖÕaWt© Dˆâ42G}Õ!Y¤ÊXW§‘éR{™G=ëj÷0xcêíÝÃà8Ðè61’¡ÚÄœFæ(±®ðZ‘ö'aÒcy;N#U>êÉ.µÇîR{ر®ºÔFª<슣¾ŠTJßçî8ÐG}%ôXþùID œD:ÖU‡¦û§ôQ_„Òwhz™.ËN¯ª‚gpÐe±®{[úÜ=ìÐ>w§qÒ!Y—e‡]H‡÷E9@;IÍã{Ô¡)Úc|Ô“Ç¡"I=ð^åtèçvœRæªâ8Бöí8mÇi—Ú¯-}îúÜ„ÉÞQôW®}~¢8ê«“0éR{&ÇDÑç®§x;"UFª|Øf<ÔÌRÀgò8ÐÇ¡º }‚¯,…“Hw©“rÿ”FÚǺ:è².µšFª U±sФë¬Çíþ) U±ù óÚøµ¥Ö^(=ÞšÓÈtH†¬wH†|ZoO#ÓŽÓÉÚq:范ſ~¥ÃSéé ב'qrBÔ)Õm–´‰>!ª¯lW¤§±èÎtš>Ë-•j°Å¹Ñg[ Ïäg^zn{w£¹´òÖõûÁ³Ó£¿Ý‹òÈZ[[–%û&’P$E™eQb—ZkÎ%ç\kƒ¬£², !ÓÓÓ·nݺ~ýú«¯¾Ún· 0¹wº¬S–ù.—€óEUPûÚe©â¹Msç+@¬¬à‰&÷<±…Cá«ÂWeͽ÷Úá…כϽ¾øü¿þ›úè•éG^[xsöÓ©s€Ô•eY@íK›€Ï¡tH¨€ªª0 f€ŒdöÀÃ_0/æ Bpü·ñÞã¼–1I’! MSÀüMø¬|åÔqÄñƒÎÞûA(]QUU?fìadªÔu=°R:0U!M!J)œscDŸsî½l°@…ƒà`qF!„èv»efMÁSÙã8FKp¤ú1†1†}ðèœïø!=“ïJjH•–\ô¢Ðuµ½¹µ83KzQ·ßûÇ·^¸ôáèÕÍçÆþõG/O ÝÞ!°ÎaMš„U--k644 4 ,SXa°Ì¡É A¡Aa™C“Ô¸n’úñ_)4IýÍz¾ì9èÜ$õ {¬§A¡‰— Ð °®`C‹Ãº‚u-+Ö444(<©gåK=Јë/­¥Ð$°)¡Åa}©§Åa]Cƒe ž6¹mÊ|YMî´lÐzËÀÅ·óÜï¾òöþìòìϯ̽úö‡ç®ÌŸŸX›¾2w~rá+õó“ &o_œ¾}qêý S‹Ør~bqtb~øÊÜÈø¶ŒŽ/`ýkôL,b¹0yûâÔû§Þ¿0y{Ð8:¾0|endlþË·ã󣓋çÆgÏÏL-ŒLýúÈÔÂèôíóWoŸ¿úþèôâÈÔâèÔÂèäwcÏÈØüÅ©÷GÇþþÆC—g‡.ÏþøâÔ…é÷ŸÎžÉÅÑɯ×ÈØ<–Ññ…¯4>•Ÿ‡'æ†Çç‡&æ†'ІáÉù¡‰ù¡‰¹§õÏÈØüÈøÜèøÂàZØòtöŒÏÏMÌO¢C‡'†&æ‡&æ_›ž\xilö'¿|ûÙK7ž}æÜøÜÿµ¾e\/½ñÞÈøÜ…ÉÛÃc³/¼ö«Ÿ\ºñâ/g†.Ï~ísø-ö<Ù826?|eîÉ>¿ÿ<“žlš˜?7>7<¹04½8zó7ÏOÞþÙôíÑkÿòÒä?ÿ|â½Ñ«óç¦f/\ûõ‹cï¼üÖâÈØw?ø(27š%ê(".ãDóDHE•fiš¤®à¶>Õ~O]¾6û5ôÖÍùfsóó½ƒÑÑ ƒS–`@ý¾¿ÙÏäû”g^:ÿÓ½;­{k×nüûŸ}stâ£û¡ ³ÌÖ–ej¿§BY9í’˜cCp¡E !Œ1¸zò-0:-* Hâ>ïÒ¾²D&ŒkDžÊªH* s¯=$8€ÌU)O¬q•JóÁvmx‚0]äNäpó¿¶þìïÞûÁè­?ÿÛ¹ ÍüÉ/ÿé_¾~iúƒã¸¶ Ei(x‡9õ ,K<z½ž÷>Ë2Öˆ2bÏ$IÅb‡¢(ð ò„E† –9é IDAT`!¢I2¹aûóÏ"}qûæ?íØºsûó/n}në–Í¡‘ÐØÕèøµs=—Þ? ^û°-ºs÷¡¥"gÕÁv׳Þv-Y±YÓD4-êhŒFK²¢( JfŽâ¯¢(†ñS4Ú² Û 'š—oáÿÕ?Ùtzà £‘¿OcTUÐ4€€\Í€:x¶£+UžÉØŸ÷îþúËÏ—H<ŸN"-dðB OcsØìí™Û7¶m}vã“O<ûìæ_Øõì_Ø´é¥çžûÓþðo¹¥¢Swºy—F;¶`;Ï/?ùä“>úh¡PÈf³=ôЖ-[r¹ò¶X,nÙ²å¸qã†ã8ù|þé§ŸF÷BA†aà8Ç=ÏS…¦ižç———=ÏCÄ”eÙT*ešf¡P ÁªªJQÔæÍ›“É$¢¿º®OOO‹ÅX,ÖXöŸŸššB5k T*Q533CÊÉâÜEq‡ã8†aÇù‰„Ö£Ñ÷¨xئ¥Ú6–ÍT Ã0,•k _aJïŸ íüÑ·ûr³"Ì0:Y62%-Y®Å(q±(Æ(1ɨ ¦º°,ÜÉR$g$ÙN×0ZN”•S%*ZŠ×0ªšdkiÞ$85QVb”ˆÓ5RÐqzmp0ªº°Â/ÅxYj´×£$’WVÆ1A‹ Zĉ¨ÔHÎø&Ëd''ÕqZKPÚ|^šÉò8¥¦Š|2_NæËd±B+¾½D‹¹²)q¨=Sârea‰¹Y¬d)>½ÂâKT"W" 4êöëqÒ—Z©à…2^(§V*v޳´€:¤V*iŠËÒBŽY3â™b¡"/Ñ"Q )Q›Å³yFÊ”ùµò‡(ÐDN-3hÜÔ2ƒZV;?Ée:¹LEKÔr¯ù“gå#’%M×REZªHiŠûMýùyœô Kh²XÉ•…BEÎ3R–âW‹ƒN%r%|‰J¯°YŠ'‹•D®´ÚùA§°¥R"O‘%=i±¥òש"V“+<±Ìfi\áSe©l@Vt—k[‘>èìoïîchª®‹Š@çš’Ù2UKkÌÔÁk.Y–R%ùLï°O£ÉTî{4Úÿ“÷;ÿb_—ßU¾£Ñ=}W^øs¢Ñœež!Jº¬TªÞÝ-†šY©©’¬T«UAdYF ˆ«ÕêOG£ë®cÔÁ­(Æè?oì¾x"0ÙÜ5Þ|á“·“—n%ËjV´IV‰Y’U4lË€º¶ Ž&Ñ¥-›ž*dÏÒ‚c€k‚c¸FM“y™£D6_XŠ/‘8µ\ é…YlâòÕ—wï!IÂq LL³n¹¶$™Ù°aÃc=V*•²ÙìÃ?üøãG£QŠ¢†™˜˜Ø¶mÛ#<233ÙlvÇŽÙl6‹íÚµk×®]---û÷ïÿꫯ …Âõë×_yå•mÛ¶% °m;‘H¼óÎ;}ôѾ}û¶oß>66–N§Qûã?NQêvãÆ–––;v:t(ÎÏÏ›¦Y,<øÞ{ï;vlçÎGýâ‹/ÆÇÇ_}õÕ;vœ8qâÎ;ècûüóÏOœ8ñÄOlÚ´©¹¹ùúõ뚦†1==ýúë¯wuuíß¿¿µµ5‰È²ŒaXssóƒ>¸gÏžùùyQãñø¾}ûžzê©ýû÷öÙg4Mß¾}ûÀ÷ßÿ®]»ÒéôÂÂBKK †aº®ÏÎÎ:thãÆ7n<~üøôô4¢æ7oÞÜ»wïÀÀ@ssó¾}û†‡‡Ñ •¿ºz]î9ñ@¥ba¹j&€Q¯'É;׿ZIe™Òò‡çCÿÞ4v²Ÿ\dÿºØ¸Ø¼Ø38Ö1x©}àbÇà¥ÎèXgt¬-2z&8Ô=9>Û=Û=>A§Î‡GÚ.¶EFÏöGχ΅†Û.vF×çÂÐÇm‘ѶÈè…¡í5Á鼨n EÛû‡ÎÛ‚ƒçû‡:úGÚãíÁáà¥+¡‹SƒcÝcm¡ÑöàHïÐå¾ðPw0Ü ÷…áAßîˆ"C}¡ÞÐúŒ ¢Ý²ûƒ=ÁpW ÔÛ D†¯ý58}áÁî`¸;î 6ÚÁh 2Ôð5 D»ÖÈŸ¾Ð@8:z¦íBw0<:6ÞÖÕ;4úq8:ºVþôöGzú#㢖ÕÎOO¤§?Ò8.j¹×üéîî vöõwBÁhhh$ú­ýùœÞÐ@O0ÜÓ F†ú‡CƒÃÈÐjq‚‘¡žþHw Ô ƒȷ®@hµóƒNuBèqBŸÝ…`¸=é ] G/Ž\:ÝÖùîç»Æby6¶,ÞŒçN}Øq¦íBieôªkT—è²_~E©‰~ÞèF½çõcðÇß|'S9”©ã{4z=lôÿ\î{óTk:KÌ~=ߘòi4kšX’YËÐ5VGu4^¨ê«jrµ†"Њ¢ <ÍhŸÜOÑh¨›¹B½}>xð£þK ô§yëR\jîš<ÒyùíÞ?˜øKßð›§»‚ãÿXU¨›ª¥ÊŽYcV ;wl-dIpLð\ðÜ»‰¦¿§V«Ø¶hŠëXPð€ÀÓO?ýt’ˆÛN­Z4lÛ5<Ëvqæ™g6lØP( …ÂÖ­[O:uàÀ“'Oþ7{×úÕµåóW$Ÿò1ßµ «‚”‚ÒDÄÇèÄÊÃR“¹¹#&u“Š7™™Ä{“LâhÔ7^#‰ò”Ø ^QÞ4ýîÓçœ~wŸ÷ÞçýÚóaÇרs)«†U«v-ö^{í_­sºYµ{íµ½^ï¡C‡Ž;ÖÜÜ<22bÛöÔÔÔæÍ›ñ^l]]Ý /¼ÐÔÔtîܹ˗/oÙ²¥¢¢âµ×^»råÊ¥K—Ž=Š÷žïܹ³bÅŠ–––ñññ}ûö8p@Å|>ßÖÖ6??ŸËånݺÕÕÕÕÛÛ{ùòåÞÞÞ·Þzëã?¦i:‰¬^½zëÖ­§Nòz½gΜihhØ·oß•+W¼^ïÙ³g<Ç#‘Èo¼ÑÝÝ=:::99yñâÅ®®®X,†Wé¥—šššº»»š¦?ú裳gÏÞ½{wttôüùóG%Â0Œ7n”——777÷÷÷_¼xñÕW_=v솱üµòŒ’‹  ryÙ4 „TËe B"H¸Pƒ"sâë ;œ?øáÍ £¡?õ\œ¸H%¸™P‰ØOÒ¡D sNéäÒ?:¤“OÅN(‘ò“tqâRù©Ø Ó©0•‰R™™Žé0•‰R©0 Æ(_4ˆÒA2!“a:E$2d*KЉHœŒ$A'Hêg9NÒ1Š&âT”¤p‹{Õ.ûØÁx"q’ KåEÇ(:JREÆ=˜ÿq<`øÎäÔ×§ÏŽ…#±¹E$Jø‚¡§…‡ˆSXÆx°\\ý×û§¸ÆS„ñ¬á Å,‡bD0‹ÄÉ(IEâäÿ6žGÙ!âT˜ˆG¢D(FÄ÷Dâä“ÚyüêOú¼~ÉO¤Ã‘x(™žžýúô¹w?øø‹ÓçIFKˆÎ<Á|ôÙןyºË#]Õ¡Ïä²@–[³õ.¥ùÂèH8^,x·F/S‘žûÃddöÞü™opR‡?gä5Í@†hȱœÌ@d+¶Êñ@3E•€ !ÄwÓ£ûE-\×}Tí˜!‹HçŽ?uää¥y…ºF»¿;s½­ë?·¼ùÁÞ÷>Ýý·õÿtàý/ÎP%(‹—~ßÎ;Üû§u©¦£ÙHwî Ýu-dYÈ0\ÓFHÖlI6ff}Û;Ú}þ9Æ‚‚é¶£!䘦jkk×­[7??‰D:::®]»VQQ±zõê›7o666öõõµ··÷õõ!„b±Øž={ðnôÆŸþùŽŽŽT*FKJJV®\yäÈ‘l6K’dsss86 cxxxýúõ;vì IÒçómÚ´Éãñ,,,LLL´´´LOO#„FGG=ÏÌÌ Bˆ ˆ²²²ÊÊÊ™™–e_~ùåÚÚZ>^ZZÚÜÜœËå|>ߺuëjkkñ~ùrÍ»g”\¤¨)j†eC Ú!\„î ÃɦȯNýõŸßúî_>»zì¹:”IsÈüés‡ÙDȼ/Kþ4ï÷`Yw]ã§gÇü{}ó©Ú±„ËÕãïÇý©ÇEH7,M7]„ä:Èu2mËEÈtÛup5¡¢l»Ž…\!Üoãd5×q²k»–‹³žŠË®ƒ…¥²í:s‘äÚO•J~Ó}n1´ê¦ñ´ðüôËsÁ¤D‰h>üóÙ?Ÿèæxˆ,Kd*WÈ˪Š\Ã5eEÄu£uÛÁat0)üš0zù,Ðÿszîí#üÁ…{·§NŸ½ÖöúÉýüÆBZM`"3¸y"ŸæLK¶d†å€’âQ‚Å“…Bžçyž7Mó‘»Ñ¦lJ˜N;Ñýîɾ”y‹EC4:|âò;'/¿wªÿØé>øò/ïüÛ—Ç¿¹8|oan1躮ªŽ‹2nSCÓœ/¤˜. ^sEIâ‹ì !Öu„eeGCh!J½ÒØŒuW¶Õ¼ë„tCª‹þ 6¬]»6—˃Á¶¶¶ï¿ÿ¾²²²¬¬ìòåËMMM}}}[¶l¹}û6Bhqq±¥¥%“ÉQ^^^VVæõz!„ÉdrãÆ===ÉdÒ4ÍX,¶k×®……Û¶‡††jkkÇÇÇBwïÞ­©©©®®öù|“““¯¿þ:ι~ýºÇã¹qãF<¿téRIIIyyy, õõõ###8/Âï÷oݺubbWöðz½Û¶mK$±X¬¾¾þäÉ“$I¦R©þþþêêjœíõzkjjÆÆÆBáp¸¼¼¼¼¼œ¦i¿ßßÔÔ„Cít:‰D‚ÁàÜÜÜ­[·JKK«ªªÂá0EQ¡P!444ÔÚÚ:11 …:;;]וe9 vvvâXy||¼±±Ñëõ"„‰Diiiuu5NÇØËôÌ‘‹AR¤2™®E©¤‹²È2\!ýùW=ûþµëøß®Ý#>ïþ–cDÈI¢( ¾X`ôÆ ¼(ð¢Pœ%IžÌÎcì ’X4þ‚$Jò¢À <–1 ^2¹,'ðª®AE– p[`UÖr™<ÏJºjä³@DzóÙœ©ÃÚ–Á± q@âTEŽ“DF’ ¦ÓÙ¬¤¨Bt:#›f¦À˜¥óBÝvr<5—e¨é¢ª Pfà$ÀÀÈË2îaE‰ƒP€2!àÒ–—e@F’8 `ù!: Pæ$ÀŠ–qçRƬ(qÀò/í` O„ÇtQ2Ÿ?÷íù0IÚaÊǃ~‚!NúÙ„¬(1’T\ë¡8‹h‹³ðêR;uÈÓÅC§3¢ªŠ²RE¨éQš†šžÈå$EeÐ,;òšeçA”•T.Ï"`"“µJç Œ Uã$@§3ºíx?&Ͳ ¼ f „r,‡-ˆª T-™Ïã–Pw]ì Y7r<ÿDþùm~~¨&#ˆ„¢¬`}AQx ¢È" ¶ÃI€‘$V”ðg¡(ÿ<*ˆØ?¢¬àç"Ê  îÇž)¾`ø•%UˈŠlä²lwÏ…ß9ö§“ç}I.Øw#ü{ÇÏþÇ—Ýé<° W™gÒ"ÀG %À "€(ëÆòõ+Ëôëé¹wÞ= /NÝ™9{n°ãÀi\7:«(&2q¥Ž@¶bãºÑ8©C–eI’Š•:$Irç‘a´«#deéü€÷ÝÿúþÝo†Þ>yõÏ~øàÛá ‹ÞMÊ„èRÀ åa +e •ã$!ª@³¢Tª¶¡e6c;¬œâæT”QQRA4D@D„ŽÂªMY(í (ogt46ÚØ²íî´†dAÝd’§&nÚD®•I¥=ϪU«ÀÜÜ\GGÇÕ«WKJJ***®_¿ÞÔÔÔÛÛ»k×®ÁÁA„P<omm%ÂçóUTTx<ž¹¹9MÓ"‘HkkëÐÐÞvõûýõõõ¸vÞððp[[ÞN‡Ã%%%A°,ëñxü~¿aããã8ÕïU¯X±bÿþýEù|¾½{÷Ò4 0M3 ¶´´,,,à;½^o[[[<Ÿ™™ñxòÇOO^\LkaÝÃ?|vþßO\H±še!htAÈAçF/­Ô±F/Ó¯§ç~ÿÞáPÄëF·¿ùõׯD³•‘ M1›ã¡ÊiúÒºÑXÀÚ=*ŒÖ!K6ìHNúîæl׉Þ9Ñwôô•óc‹ó9CHt*ÖL¤ZÈBH„ºbºa*³¹µs6œˆåÁ,‘›%Ù¹„8—Ó 8€Ó)u*£M±ÆxN¾Ç˜ º›”ç³Ê•Û³›Ú;oܹe ÓF²f0l!~èͽáùEäææ+***++†!býúõƒƒƒ‡Þµk×ñãÇ>Üßß÷¤]×D"---4M‡B¡5kÖTWWãÍÚ¹¹¹ÎÎÎÑÑQükN(ª¯¯…BŽãܺukçÎ### ÃàˆòòòT* [[[±[†‡‡ëêê~üñG–eãñøÀÀÀÈȈeY³³³ÕÕÕ¸À³ã8áp¸¡¡¯èºîÈÈÈ–-[b±X6›mll<}ú4MÓ@ ™L³,‹O.âÄ „P$©®®®ªª ƒ$IÖðeÌ IDATÕÕE£QÓ4''';;;ÇÆÆ|>ß'Ÿ|R^^^ZZzïÞ½d2¹ÿþx<Žº}ûvUUU,£(jóæÍ£££øJEš¦H’D566Û¶ý~ee¥Ç㙟Ÿ_ÞŠ~vÉEl2%0…‚Èÿ7{×úÕ‘Ýó‡øC’ò·$ßòÁñ£ÊUkŒAZbâÒfYæ! c{- Þ<¼ëMpp­Ëײkl³˜xYËÆØI $¤IHš‘F3š‘F3Ò<î«ßû¾í(  ]c»Rû®®SG}ûžþéÜVÕQׯÏñ”²™X¨Ô¯÷3 Ù–ñÆ[ïo?x®ëDêÂè¢>bìèóWFáJ×#·öµžìÜ‘ßÑW¿ø{uF!F6F¶Ñ¤ À1ÆÇFr3Sz5 $gÈs™c›•ù…¹Ü¬Q«û‚{œR4–ªgÈ6\†r(´=C1l Žc2n`fPV¸Þ2€,L*–mQÁ5W­×cabqan2n3i ép×ÒfÒâbeä«ë·J› ›I‡KÈ\› @¿~[;_ñ£b­þówߟ^(ûJY\8Ü…Ò[ d®Ö.•6å·"¹ÉK·}ºzŽE…öð­3.µ—ôŠ«å·…Ç¢ÂâbÙ†5ˆX\ŒLfSÙi‡»Žt—mèH×án ¢©B©q%ùÅj¨T͹Ú.ÌLflÌy”ØLÔL°l ¼…¦ápùAÑe-*ʆ ¥oPÆ#ePf`¦×¢Q|GþùVü¬%ž-¤ž£¥ö€ÞÿzÏXT”YThË&ã«ÿ:,*L€ð.õ·Ð2ׯ\ëúK™„ézmX„X„’ÜMš9õÁo^8öã×ß97]÷ P¥ÊâØÉ_¿ööÙeà{‘‚̯ØwÃè»í›¶/«®&uÌ4½çnâÚå—‘IŸú¸i*LÆ)ºÖ®b!´m[±VM ….óB¡Ô<Š?É.Àœ•`D•J5±0©KCÅÅ4õÙ üX…JëoišÈª—F²_¤rŸÎ}6Zú45×*~’Zøxlþ“é¥óÙÊùÉʧÙÚÓµ±%|þÚØ†gÚ§æ ¾ ˆkÖ›Åj9wøÀž©Ôp³Z¸ruûöímmmÙl6—˵´´‹ÅB¡ðÑGutt\¹rEóFGG•R•JeãÆ ¥Riݺu÷ß¿>rÎd2:7œÎ¸¬Ùš2Ñ××wï½÷vuu _»víÉ'ŸÜ¼ys¥R™ššêè蘜œŒ¢(N¿øâ‹—.]Êf³—.]:~üø«¯¾šÏçubÙÙY¡¹P(´´´h¶FE½½½­­­ÃÃÃår¹½½ýìÙ³”Ò©©©ÞÞÞ®®.¡ïû===[¶lÑ Ëåòºuëxàb±¨Óíuww ! …ÂÎ;S©TooïîÝ»ï¹çžM›6B*•Ê}÷Ý700077wñâÅöööF£133³sçÎ . …r¹Ü×××ÞÞ®“ôõ÷÷·¶¶j*K&“yðÁyäön$ý‡Úb ¸¡Še6‰”²êŽ‹]˲N¼uæ?zåDúÂhõä»ï;6S(A;Œl-)Azü&‰XÑ Ö!, ØaôÎì¬%cé·´Jg„QÈ(æ ­ÇÇ`cd¬£v¬‘x.3šõ}»^8ôü•þËl2 Ua®öÿèø«ÅÙ\x8aà©$NâP%1F¶ Øh. • ÙP*–Qä)å*U6L™(O)‡Kä&a&ã&ažR¾RR)_)[H t8¢¡¥-dbƒ2› ƒ2“0‹‹•™ŽtW›„Õ^y÷ÿØa¢± ÂÆL+ˆ»IÈ$âîêq³¼½‹‹;ÅCƒ¨ToüçéÕa´Â] þQw‡p@ ÂáÒ¢\[6_Á£‘ܧI˜žoPfQ¾âÏÛÚ1 ³™ÐkÝÔ¿-<êAd` =ßÀôF¾°ïðKOoß±d95ˆ,ÊkyJÍ7šÛvï-.ÕJËõP)îG~¢B¥æÊÕP©É™|Çž}ÏvîŸ.”B¥¼X…J‰ ¶™ð•bqâ+UGØÀÔUÊWŠ'jÅ畚¾¥-#¬”TÊ;ûÞ¯º¿´éÑÇ·>ýÌùóçuõ¾ÅÅÅÎÎÎJ¥¢”J§Ó{÷î-•JóóóétcœÍf÷íÛ733“ÏçwìØ±cÇŽB¡$I¹\Þ³g&:+¥r¹œÎ¡¡”J¥R[·n=yòä®]»:::Î;§+×jµýû÷ëëjµšN§<¸eË–ÎÎÎ3gÎèäÍsss;wî,‹qÇq\.—>\(tyíñññ#GŽè¢-³³³GݰaÃæÍ›80==­©étú•W^Éåra.,,lÛ¶­­­MÓ Ÿyæ™C‡MLLضJ¥ÚÚÚ^~ùå7ß|³µµõàÁƒ‹‹‹™LfÏž=û÷ï/•J:ˆþ_bttôرc---6l8räHOOþ­/_¾üüóÏ/--EQ4;;»}ûöŽŽ®änÍ?Ôƒú2V¥^ýärÏÌ|Ù ”Ïbhâ¦íüôí³ß?ô›—OŒ}2ºôÆé3¶Í˜Ã8&” ‚0ÁbÂ(ŒKÁ(&” [%A˜3"¹œ2B †áÕO¿¢µ$†H æ ©uJ§L?åŒPL0WrÉF`£X?å”…·PšßðØ£úÇrèàÞžËú2†H©xðÚÕï~·e:;•$‘àtl,½0_œ˜Ÿ/Í™fH)vl³Tš«,.ÔõÒbÙbrÞÄ(W*eŠsHì{5Ë2)a¾}âFµÙ™šÏfkÀáA€} a1j¢ßRØ„ Ï¥žgsfbs†„°9Œ)c&%€1ì{Äuµ®Ÿ®–šçJ¥Ë\O 5¯Tµ¢™ëQéjêó­v4†;Âã%IÅ4~ùÁ³‹eMêŒAÎ×ÂC¥K„¼™…L`ÌbÔ¡ÔK‰±åÉëéñJTL¼`±yË-XÞLSf 9mø9ÛÏš$ùdN7é —XxáÚõ–­OO/äh€Ü˜1UIàIwj,3Ø?4r=Å#„†Á9Ò)GÇÉåržçA099©£U!Äøø¸eY¶m÷÷÷ 1Æ\×…cŒ5©põêU]”äÊ•+O<ñD*•*‹£££+¥ )¥z-€.¿ÒßߟN§kµ¥”sN)QJù¾†¡~E_7ô<!466V¯×…€ÁÁÁ¾¾¾±±±¡¡!Ã0Vê§ÓiBˆ944”Éd”RZïíí„a†áÔÔÔèè¨ïûƒƒƒ£££”RÃ0ÆÇÇŠÅ¢>>—RJ)9çÃÃÃétº»»;N'I"¥ô}P(8纤¢>}AÁÿï6¾Û¾ZKbå{Ê—QæÊ ·û¹=ž?‡©.W¾÷½¶§žúÛÍ›[÷îÝ=7—O’(ŸÏmܸaýúï ³Fí'?y-ŸÏIÉýЛœ™zõ_41=‰‰T|mdpû®ëþë§ÚŸ¶±ãžPE²5æ;õÏúy­ùX½J Â@…DR@¡Ôôþ‡ I™Ç™Ç‰¤¡•? =‡r‚1äœÆqÇ!çcÈÑ-AC„€¾5!\)üHx1¡Ò"ÛïýúÜKÇþù_œ™iðŒÇÊàOžzýíw›Ž¢ˆRZµÁ]RÇÝö Ûÿ†Ñ¿øåEFìh%Œ.Ô°ÍTâ&@7zuEQBˆ(ŠÖ £Ã0Ä”ˆ æ‘B^l3iB’$‘J<•Håc•xa ¥ç*¥TÀ•‹U(|Õòßln=uêÔýö“sÝŸx¡çìgÿÍÞ•>Gqdyÿ-þì‡ ƒ1³\$è ³¾±a‡YÏÚž3šðÄìz|­ ö#søÀâÆI¡åR }HêÖÑR«»«*Ê£îªÜi•e 1²5a>詌¬W¯^e«~™õò½–'¾ýìÄùÏOÿß—ÍW¶^ij;¸¥õ³c'¿>Ýzè›¶M'^ß±³¼zݵx—' O¦£c‚D ²<[!,Ë #ÔÈtŒ23ŸÂq™šÛ¶m™0DxŠ$‘²N ‚@.¾Ê áÇŽÛ¸qãððp˜íOv“ýebp˲dE¶Aຮìiš¦ïûAxžçyžëºòZ2°Žã8’UKÝlÛsÊÉL˜ú$L%hF˜Ã<¼€ïûBÙbÛ¶Œ'u$XJ-®ëš¦é8ŽüSQévâ8ŽçyS³”A`šæÔ–YÜA|`ÃâºcZBŒçQ":bÁ„Š^ÿà«úß6ýîí®ÃW&ÞÜ÷EN310æbƒRnš¶ãxžè:-T Ãr϶]ÆŒðD]§Ó•sÓB)—£’1BL£”#¤#¤SÊ abÆŒ ž„וÝ0&霛A SsçÎ++[}äȱººM‘È5!„¦ig[ÏTTW$û!Eÿ<|è±§žœ¿à¡%ÅE•ëª6±Ïöƒæ3m«+×½ö×ÿY±²´r}Å?víì¸xþPÓ×êª×V–?yDѲp5˜ÿòëÏ×V–?0îòË6?Z¨ék æR®at 1H×®†\ßò…C9HÑ)$ A¬B¬†t R(Ǿp\ß" an,:Œ"ß³Dàp†T(ŒJG•QD DPá ‹Àñ=‹Q¤ßL&`ºú8ž9>1ºwã`*W ¡ ÒÇ÷,×1,“2ЦºÎS†¤M(Ç–Ãm×à&¹©†²¸¾e»†aQ2y¢´-¹™t¸Ÿ)}ár“Hû†áÂ=×ÞºzmioôšaÑx߃ýjiÑ¢dœ2tüØáò²•åË‹—<þØ#_}ù%09_ºä×K/ìëíŠÇzê6VÄdlÆDßúêŠk=pc‰Þ÷v¾³ióÆù æ­*+9ðå§áu ³ùqùtnß>?ÁÎ… åbU§ÐñLÇ3u !V)Ça¤k«²§aQÃ’?P±JÖ à Ë žkz®)·:p†§nºÀH%:à [&·Ïv<ƘçyÂÏ>ÿâåWÿònã'ñ NBçêò—w>|óýFEµ(iE›¥Ñ³ø™¸ëåí/&¢W::wí>VóÜΗßmŽå-ŲÌÀÔ8˜`*™ÜbȘfpB¹Œs'}9cBHáÕh v…0¡Rc\ƒ©Ñ!\®ç„ … ® ÏÓ0m×u}‹ › ßô-¦fÇ^0¿ªªjUiyEõÆ5ÕõeUõ+*ë–®©]\^½xõ†ekk×ÔÖ¬X½jñò¢Šššª õk×W­¯­).Yš‰²\‚ˆê8–„c ¦;¾/r¹œëº¾ïÄ$Gô}_rSÃ0$ xžÇ“Ô¶mé8!y-çÜqÙ"å`Œ}ßïîî~öÙgåÆ;yû’+Ëå[`[òQɤåQ ”ÜÔ0 Ia埊¢HZ^cL) )¯iš2þ BÓ4Ùèû¾Ì.Õ !|ß—ó1I”]וýåDH^Ôq¹›PÁ å„O3œ„žrâ!ç!ŸÅˆÀ窂s™ÈÛBɨ-Ííéá\V…¯ðÕ¦ßzñ-I£¿’4šQ‹& („8¤¤…h.„X²dðt€@éŒÓ’S¨äó*c†e9éª aœ›„°°¢ª!ݲÆŒ|^•g…4B,it4_°`á²eÅG¯«ÛÔÞÞ¡(šïûg[[ÖÕ®Ie”‰Wÿ¼ýôÙ3©á¡¡Tó™–ç¶lÅû=!.^êœ÷Ђgž}¡µíܹsmk+Êæ=pMíºóí­mßžyò©G#]—m‡g&FŸá™S§ &ã©¡þæ3'ë7ÕŽgF²¹1ó„BÆ1eë€ù¼’a[6CXUµ,¡ºüj-+ª–EXµlÆ8Î+€›%Ÿá »&ÃH…J–h1ÈuhpëPS³ª¶ÅLŠ•|A9ÓÕÇ´èh:õñž]ƒ1!\B!e¨>A”@Š4Uk\‡œa„Uóæu “˜¥ A¤Ò“2D(ĺ‘‚°ŠuM¶ɽ¾þ8Sú|— BÙ‡ºnÓ‘ƒ•ëVǽB¸Ý=K–þzåªâK—/@¤¼ñ÷¿;z(3:”J&N?òܳOfÇF¢}×–þÛÃ%Ë—¦‡‡‡ÊW.ïOô™«ÊÄðÈ`ùê•©¡þx¢wc]uUuÅéoŽgFη·nÙú\û…6!\nèp¤æÓµÏtí\¨?ãÀûæŽO7oýꥷ®4]I¿½ÿK:ä„0B„üÕß²OØMÖÃÆÛ—Sdš€“œPE×u×u‰DIIÉ‚ º»»O:ÕÐÐpæÌÆØùóçW¯]“J¥rJ>™Lv÷ö w÷ö´¶¶®¯®J§ÓÍÍg‹–_ºtebb"‰¬ZµbÞ¼¹G6‹D®>òHýٳͺŽòùlEÅš¶¶–ÞÞî¾¾žÆÆªªÖõ÷ÇU5oLú60Fd}º¥P@Àï‚ýÝöñÖrn¿—ÍföíÛ38ØÞÔø†7½.‘‘e\BªSªÓ)A §ÆLœ©ðÚR)ä‡Á É!Ž?_TÎ)¥:„šir!|µÎÎ+[¶<êÔ‰|>ÛÒrfÆšgžy*‘ˆ æóÙöösýñk×"-g›«ªÖuE®¦Rƒ+JŠxàWÒ#¿j}åÅ‹2%Y"++[¥(¹dr`þüJKW8ðY,Ö÷Í7§ÊËK››O{žãºv6›ñ}7ŸÏþ4ûܾoørºò É¡Ëg$ë”êž Ò4ˆ!‚Šª)êž=ûþØð×w>Ü?¹­ýù?zãƒÆ¬†<ß"¦5gqÃ5%Ë—«ÑybdqF—4úãýÿ¼zµ/Lþý?ÔY= !îzéOÿÕ?8pµãÚîmx~Ç+;NEó,g2ó au'¡‚Q© ‘ªƒ,ѦK£…ø¾p…0…P g¢¡¦ð}a‹À¾#ü@ÒhOü pÛã¶gû×·{“‰Ž(¾Ï…°„°|Ïtl&‰Åb¦i"„8ðÚk¯Õ××/[¶¬¨¨¨¤¤d||¼¿¿ÿþûï/--F£/^\¹råÐÐeY†aŒWTTôööªªZVVv÷ÝwWVV–——ËÁÜÛÛ+?¥J¯Ë©öÿÙ§PÿBòÑ41]9†a1fXŒˆ4Eݽûãßÿ¡á¿ßý(6®÷rJ}õ]ßùñ¸=ÏÁ f&Ò©Œ"†!RT-‹й¡Pg*–«Ñ‰xr–FÏâFÜõû?¼ïO\¹ÐõQãÑ Ïïxù½“3L£Ï÷}wr5zê9¾£Ñ¾9u5Ú´3"ÇuÝL&³ÿ~°H¾nq_¿ø@ú×É‘›Åš¦éºÎcŒ¥Óé'žxâý÷߯««‹F£BÎùØØØ¶mÛšššdhÿ†††’’’X,†üïë뛘˜Ø¼yóððp&“QUõìÙ³555‘H$Nßwß}MMM‰D"‘H´´´H?CÛ¶E!„ÈavGÙg¦¾>ê/½ÈÖu€{÷îÿcÃßÞúǾèМKIåOoøúŽÆ1¸®t8ªj9Êåj47tÊir'÷¦:uÈdà±èÀ,žÅ¸ë?_þ]4»ÜÙµûHíoÞ“4:oq+ptÛí :°ÂÑôht „ïžïßÓè,a¦¾p¾§ÑÁ÷4:˜t»G/ð=/ð_¶ðß÷…\Uö„0„@ŽP 9‚‚{‚y„å ËîäÒµ?s£]Òè©-’1_‡ë\>f1‹_p9·M¦Ó–šÎº{b_ìùÔxZ4:LÀt#®{ézïþ¨œB¸Í×yØmêj„ itooïêÕ«-Z”H$8ç”ÒC‡=úè£÷ÜsÏÂ… 1Æ===õõõ2P.—«ªªš3gN<WUµ£££®®.†‘J¥–-[¶dÉ’ÎÎN˲FGG«««/\¸à8N6›]¹re¸ péÒ¥†††±±1€Ô_ÞŽ¼útïk¦ì3Sr<ÏËd2ûöíK&“BˆÖ’#÷œÜzxLíPHÏ;MNh=ã(nÆáÇ·lÙrðàAŒ±Œ¼444$SÞZ–uñâÅÚÚÚââb„P"‘X´hу>8>>.?›Èɉœ¤-^¼8N§Óé¢úÀ8ø IDAT¢¢ÿgïÊ¿œ(×ôüEã™ãr8ÇqÁuXa8ÀUQQäÈpE6i‘Á‹^ä*¢ÐÝô’NÒI§Ó4¢öìF¯\všô’µRY*{*©JRk*µ½óÃK×íâØwº¯x'Ï}*•¯ÞoÉ×Éó½ëÃ?<>>ÉdòàÁƒccc¨²a»XèõiÖ¾™ü¿ŽFÿ|9’TŸ­îé±íxsÿÁ#ÁL(7®ôÛG»‘F«ªRå*D&K•YÔF£¿8úF³²£y² X4úêÕ©`€hÑènÇß„Fša‚:‹FËè&úuX4Ú´h®&˜¦nꆮ«ºª)Š &`˜¦©[5€LUËHP1X *È2€d‚¤ƒd@Ããœçß5úV`z Æí-p‡-´ð³1wÍUy´–bN›ÚŒå·ÙÏ'6®Ígß™“œf°~/-(ÅÞÒûì.¸C°$IX"ô‘G¹÷Þ{±hhµZM¥R›6mz衇-Z”H$b±Øþýû»ººFFF¾üò˧Ÿ~ú¾ûî+ pîܹµk×NOOs‡-Z´xñb¤ÓÓÓK—.={ö,2×^{mllììÙ³gΜq:{÷îE"ÞòÛžÍ~æ¼æk}æKަi™LÆáp„ÃaMÓ°Ùì©Ý"Çúù°¶ÄìMrÇýsGÜ…rp•$IB†a0 (I’=öEQ·-I’{öì9{öìää¤Ýn_¾|ù=÷ÜC’d8^²dÉý÷ß_.—óùüÎ;=ÏØØØÑ£G—-[¶xñb¿ßvíÚµ{÷î¡¡¡üqddäÝwßE ‰®ë©TŠçùz½¾ÐëÓ Íäÿu˜‹®Zå$ŽçØ [fŽþ¶ÿzïÃNG‹F·°pXh§ t‹Fë+äYPÁ4@³æMm˜`˜ ªª¦*J]’D^©K†®‚©ºjèªih¦¡©›†f誮5 x€„þ\߯‡ õ@^ çx²ÈU è ™PM¨ëÐ0LÕœ}G}ó-šéÖ¿Y wæîÔ!òÒl2$òƒ;*·ø Q1ó ¸¹™Ðß/§P&êÆn×KYº+lƒ­ù×Utä}ùå—7mÚ499 étºV«QuêÔ©Ý»w# }ã7^zé¥Í›7{<ž7ú|>¬ºmÛ6‚ ™Lnݺuûöí‘H¤^¯G"‘·ß~ûûï¿A¾ýöÛ­[·.[¶lÍš5‡ºxñ"~sâøñû‡7×yÍ×úÌ—Ã0òù¼Ëå"I¬¸™Y–Q ?[&¾äï´šón“ƒÉð€„\?eÃ0b±ØæÍ›s¹fO’$©T*9sfË–-Ë—/ß±cGggçöíÛ£Ñh2™Ü¶mÛúõë§§§Y–½pá¶mÛÖ®]»gÏ—˵k×®t:›íóÏ?ߺuëÊ•+7oÞ<22bå0U¥X,–J¥…^Ÿfíç*¾äT*5–­ ÕZa+ ëv{¼÷A»ÝÛrêhaá°Ð!†H£NV¤¬ bH :˜ 0`ª¦©`þE ˜úŒ;³ÑPd$ÐÖÃÐÕº c㱎SçÛŽ´}è|¯sèýO ¼ßa§S±¨kªÔPš¢›ÚO¬Å\Ù±ÚˆþìÿÑ£[¾Ñ-ܘ{ˆ¡E£‘ð¡µíÅwÖýo4úgÊi†Û™Ù-ôn6·“$‰ã8Tža×,Ë¢*”¢¨D"AÓt.—cS@V«Uš¦oܸ1è  ‚E1£‘¢(‚ EI$‚ LNNNOO'‰r¹,Šb0Ìd2 Ãär9˜œœ¤i:•JùýþL&3Û<ÍÍŠÓšë¼æk}æKN£ÑÈf³n·;™L€E°šÉÁ€¹Ùg þ'é]³qÞmr°àî´jµŠ«Äqœa Ã\»vM]×eYÆš¥R)‘HD£Ñ\.W­VI’Ôu¦iŸÏ—H$êõz>ŸÁï÷ûý~ôxž˜˜¨T*ÜL¾Ôp8‰Dð4Øh4‰D±XÄ¥Ve¡×§Yû_ŠF€®›Z]©1l!—ïììúíζw>ho…¶°pø‡·´Ec¤ïÊ´ÝùÕ†mž§ŽY4Z eˆW”´`ðuÀÝÕÕ]SSQ”›tÙÔ5U‘D>ŸË„‚~ÿôd00 £D˜ˆ„BAÿôÔÄøDà÷ƒo}ìÙÿñÀ¡®áÃÝ'~ìüÝG=hïËWe¾®é&ª&Kr­¡¦Ñ˜Ç0CLçŒÕF0w2ÞœíÝ¢Ñ-ÜE0A`ÙJ‰®ˆb¶ÂÙš *l–®äŠÍh4SbK¥MÓ4M—J%†aªÕj­V+7Õ MÛ ÃàãåryNršÅbüÖl™–är¹Œftkx)‹¨¥“$‰¦ép8¬(J6›Eõs>Ÿ/ ’$1 “J¥DQÌd2–f±T*e2™T*… i:ŸÏã#²,ã` …B±X¬T*¹\ ú¨ç¦iºP( b±h uöZÍi^óµ>ó%‡eY’$ûúúPm­O39h·¶‡%ó'öÏq·É)‹,˲,‹; '^(0´‰5>ÎqEQ,Ë–ËeÜ$ØßÅ…Íçó Ãð<_(ðe>ŸÇr Eñ³.§µ`TØ"€’Œùßy{gͨyÊf³Èwa¦B ZÄø™ŠƒØ¬ÑhX½išÆlК¦q3eSÂáðúõëÑ3 ¸3%­J.xGE]×ñ‹ÿaÅo¨×ëVÊ00àñxzzz¼^¯ÕÇÖßßßßßïp8l6[OOËår8øHwww¿Ûíîîîr:ÃÃÃííí8£®®.»Ý~òäI›Íæv»=Ífìïïw¹\^¯···;r»Ý8øþþ~|¶¯¯_:N›Íær¹N:ÕÛÛët:ºººl6›×ëu»Ý‡çû«ÎÚëõF"š¦N?Y§ÓyüøñÓ§O;ÎÞÞÞ¾¾¾_z¼-ü}b``Ðétu;ÞÝÑi·õ>üÁ¾wa¦ôÞÿqÏ‘Î|¥¦ëj¥ÆÆò…l•ce‰“y,.ƒ¾Ñ4ßH”E"Ç}pÂýò¶Ù]§/_žhÑèN7.O¡6z_çÙPI¢I£"+á´Xª!›2[áêBY®ñâÍøzQa†bq¾;Òh³!ƒi²ÆëðÕåàŽ÷»ßí=}tà«3¦ÒÕ:¯›eAt­ º +u£Â°‚ÀeÓ™÷ßÿýéSŸž?ÿ§s£c.œ¿øç çÎŽ}7zîÜè¿q8–­ÙøàãÏüãKxté?/~üŸîÿ—ÇŸzzÉÿO&ªU@M’yÚ¬³±Ð5ЭV«!ëgŠuÃL1m«¤¶U/hš¶Š¢½ 0µP,{á…‚Á VCDž$»V«¡LŒ;´Jd—ËeL`Áz‰-íu –åXFÒ4NSU€\¾DN‡UN¬–‹Ç»½/nÞù‡ _\¡>NeÙp€Œ“‰éééX,–Íf£Ñ(I’‰D‚$É@ J¥PKD8N¥R™L&“Éá÷û ‚ (Š¢¨X,F’äÔÔJƒ$IÆãñT*‹Å°Y2™Ä›$IF"’$“É$fC!Éd2‘J¥ð­x<Žîáp˜¢¨ñññP(”Íf†™šš ‘H„ ˆH$‚b±ë@ H$Òét4M§Ó('úýþl6›L& …B, …B‘H$‹E£ÑP(”J¥ÐòŽB‚Á`4%‚$ÉÉÉÉt:Ͳ,rjjŠ¢¨b±ˆ½àšàì’É$.EQØi(òûý±X WéW ‚ FGG½^/º»ø|>\ÛË—/ƒAœ EQ¸ªÙlö—o Ÿ…"$Q*Ÿ¸áëêêÙÕvàà‘N¢ Æ*::u|ÔÕ_¨r†¡Õø*U*x‹‹R*²,*ºq v¸?»re2J$Z4º…ÛñS4š•ê$W¬( Tk\] ÅJ¥VEwCžçÑ3Ùa3­ÕE]Õ4€8-µ{¿ýÍ®Ãßúx÷Q÷ѯ‚AàtS¨°šÆÈ'É&@­Æ›¡Pä™5« Iuܪ M5M08N˜š­{nÓcO­^üèSKŸÙðø+Xüð’ÇŸ|ðÁ3iJ9Ðê`*5:¥Tó)ÂÇ—3jCžçý~¿×ëýæ›ot]çyÝÑøær¹K—.ã[cccW¯^ýá‡hšF-µaXø:‹­]»]Ó€eYŸÏwùò剉 °`‚Á Ã0ß}÷ݹsçB¡šÏ Nû|¾ÑÑщ‰ ä†aX‘"-´0Ÿ0A®Õ@×4€éÉ×Õ›i#5³Z.ë|qûðî/¾?â¬òš¡˜ Ë2šYTUmiÁ žçñzvx¼Àÿ¬F¤( ¶Á㨢(†a , ]Ë4ÍF£a)10Ë:Râ¹]’$ô¡Bù(hš†`Ë $‚õ.IUUY–±#˜u^UÅÊ«#Š"†1 ³îàSÖÑd„CÒ4Í0 üê0 ƒçùjµj†ªª’$á5MCç7ô‹µÀP~¥üÚFOœ8áóùn —.ûµðÿ7í¸&€ ÙtÆétýîÐûÐH¼¬P<Œ'ÙÇlH£MSçEîŽ!† Ã,rJ¼$D²µÙ4šŒ&[4º…ÛÑÔ©C£,HþD-Ï€&êb™©ˆµÏ FÏ- ´¯T*õz½ Ã0AÔàóÆßüȽ÷ø§{Û?ßßóǶã'?»0}#QœLf#y:Z¤©ZM¨ë H²ªDˆø£ÿúo±xª®éuEWMh¨&/×U t€†jf‹åX*ŽQ"§rd2MDc_ýíóÏ?Ÿˆ“`jŠPU%L%ê¿~`ïöhpÀ…Bƒƒƒ¯¼òÊsÏ=×ÖÖvñâEžç¯]»öì³Ï’$©iZ زe˺uë‚Áà¥K—6nܸaÆU«VíÞ½Ûï÷›¦É0 §@0\±bV¥òù|§OŸ~õÕW—/_¾aÆóçÏã/åõë×±”Ã믿þÄOìÛ·뜩ªJD[[Û“O>¹oß¾ŽŽŽ;vüém¡…y† t&#T+É\nøÌ—pC5 ±Q+”Ê…ìGí®M;>Ýß:ls1U}q7², ªªò<ßh4,×#á¿Ù»Ò§(®~ýþ©÷Cþ…¤b,£AYBÀ!8 Ê€‚H_¯K¢(qIbª¢bÜ5×%.¸¨8‚DÙ÷¥gßgºg¦{º§gº÷ùεrcê~ð­Ü¤æùDõ4çœîê®~Îs~Ïs’IôAD1Å\‡9nŠ%:r‹3ˆnæ(8"¸È¼û›5™×+Py(ŽãhH¨ i™pGÎ5›{§éG#–Ÿ*â͈|3 “cÛ¹u$’$ÑO¹š.Äû“Fm¾ò9ýAÿ͹E°\×9®ÿ×2ÕÝ¿ßb±$‰Ü2Hg!"7—€×ç9yäñÖÁqÀ³àx—ÃyõêõîS†ŸË±(ëLÂ<=xê*êà¸L‚ŒÛA/‘À:ɉdŒˆGH2‘b3~‚þ –ÉTyÇïâj£ÃIÊà"1`âiˆ&ñ…'È$ò¿¶´Åb´‹!“w„:zé^çÉ»cæÔ HTѯNÞݾïàékß»üÃ…KûOœ:sçî¼ÕJqMPãPªë„NO0ÍC€á€b8Šåh’L†¤³Ë¡4h& t†GÛ¬èôÆúúzFÃeÙ ËÉ8ðœA£,-Zi³š1›½»»{ûöíOŸ>ŸŸÿõ×_››› …J¥jll‰Çã###7n …ÓÓÓGŽÑh4‹‹‹&“©···¹¹ÙjµÂ«ï=Çq:N(Z,—ËuöìÙ®®®ééi›Í&“ÉfggY–õz½|ðACCÃ7\.×ÐÐÐ_|aµZõz}}}½D"±X,wîÜÙ°aÃ’%KPûyäñöÁ“H°)ʉØü>_8Ƥ¹Ó—M¦Â~O÷ék›vô:¯Zð¹|#&YšO‘4â¾/_¾DmÄb1$ÐæÈtNy…Wü˜$É×5Wš¦9ŽË9P°CÎr@⑈Ôf³Yd·€WÔ–eY¤ûæò†‘(Îó¼ÙlÖjµJ¥Òd2¹ÝnT‚šE- eYÇÑZ΀r H’DòöëZ)r€V«õx<A˜Íf&a˜ÕjEÅ ¨/¿ß§Ѩ"‘²@ä¶eŽF£HFÅ]h$I†Ãaãó%ÿu.ðÒ¥K*• ÝF‡Ã‡1 Óëõhå-·æ¯äü<òx»Èfùt:óà!þò˵ß¹Ö÷Øf°8Èma”Kê°ù<£S :ODq"œHTšý]5:_Ô‘ÇïâE ÏF)Úì¥Ãñÿ¶âT<Ê$ð8‘|µ÷ŠEŸ7Ñèl6K³`öÄ~ø¹÷›óg¼ "aȘÚs^òÍÏ]¸sìjßÑ«7wýxìàÙ ƒs2NºÃ± @tE¹Ýn¡PØÚÚ*•Jׯ_ßÒÒ"‹—/_¾téÒ†††¦¦¦¦¦¦çÏŸÿøã‹%‰¼ÞQ,ËÉÛ¨—d2™ÉdÐ]ÊÝ«ÜðþÒH¥RÑhôêÕ«n·›ã8‰D²zõêO>ù¤¼¼üÃ?loo—H$èLžçÑBAyü;Àq™,ððù/^¼¼sÏþcç~1(k,;g ~óÓÏGÏ^AۯĈ( ùâI"ÍüFÎ[ óø¿ã4š…l„L\d‡ ™MC8§ˆp4’‹_ÍñiŽãÞD£y€ €‡È^"Ûy¼ï«sOvžÝ|äÞî Ozž›¦í¸&L[“iU0¬ †#œŠ¦2i@gõ¬]ß2³ óá¤Ù´zC®`ÜOPþXʉû¢T8Áøñ¸;ŽÑ©ð(&’3/ëDMz F¦9€b!–L­Þº†z“ƒ0™L£££b±¸²²²¼¼|Ù²e“““àp8ÊÊÊnܸQVV&•J€¦é………ŽŽŽ 6´´´•––:xU †UWW °Ùl(ÕªU+W®£ÑXUU¥×ëÑrçÔÔÔºuëôz½Åb‰DR©”a˜d2ùòåËŠŠ ›Íög= yüÍÁ…ã‰X4ˆã>ÇI:!lZƒ'b!ÿ‰s=-ÿqww÷ÌÀœãdO/‘ÌfÓh4šíÛ·>|xëÖ­F£‘ã¸H$b³Ù¶šÍfÕj5òD£Q…B1??¯P(0 Cd7•J9¯×Ôj5âÇÈPˆònuFÇãñ8²î%“I³ÙŒê%ÐØQ/ƒ!—uc4W¯^Ýß߯T*u:Óé\XXB“áwŸ™™Q©T‹‹‹Z­A4Z¡P(ŠÅÅE•J…Ž ÞÈ è÷û—/_^RR¢Õj‘Q"‘,[¶¬¯¯O«Õz½^Š¢$É£GÐ EQÈ€(“ÉPwz½Ùìr“4µÖh4f³Y&“ý ¦Í ÃÑßßo6›'''kjjŠŠŠ&'''&&ªªªÞ}÷Ýòòr½^*ÈÿE,yü¿ÏC†f¸4ëÄ—/ÿ²÷Àá“{ Þ„9¢] Qnt&“ŽQd1$Ò ™¦’$ž ¼ “G4eö%Ž]èiÛº÷Êõþ™™EƒÞ’§ÑyüoüQmt0žTYcÞ0°É ŽD“¸—áq-D¢°”¥ÿæ¤H’)ŠåI>Ò3¶v{wÕ¿ºEç~¸>"wÓ1~¢!b)€4@§h”&ÇêÏ„R¹Æ‹3OÔ&íÚè‹ë}Ik$m‹²†@Âè™ü!G4i â f—'FÏiLÕ-*3Æxñx‚åÒóËjÁ:Ñ®Õ<ØÖÖ6>>þìÙ³k×®UVV"X«ÕŠD¢ááaDmS©”Ñhììììë뛚š’J¥'Nœ¨©©Á0 }‘æ¤×ë×®]k³Ù0 Û½{w{{{ÿôôôÅ‹ÁØØEQjµzÆ &“) €D"©­­µZ­Z­¶²²rjj xžÇ0L  ]þ´'"¿1x`‰4EZœÎË·o-¨uÈbHøCA¯ëäù›m»ú;š{8‹¸q7gSI–e2r¹\  ‰ÅbÄDggg¿ýö[4Ÿt»Ý{÷îݶm›×ëÅ0¬µµµ´´´°°ðàÁƒhB¨Ñhº»»u:ÝôôôÑ£G½^¯Ýn …ÕÕÕ+W®ìèè0¹*d©TÚÚÚº~ýú‘‘‘ãÇ;NTát:E"QMMMKKË‹/Ðù†ÕÖÖºÝnÄá ›ÍÚíöÒÒÒÜ>‚V«uË–-eee;wî´X,Ç¡@è;v”––utt,,,À«h«ÕºqãF‘H466VRRòú”Ýn/...((Ðëõè†aûöíëêêÒét6›íرcR©ôÀEEEmmmÇf³‰Åâ¥K—îÝ»×ív£zn·Û½gÏž’’’’’’-[¶¸Ýî?åqx»p8W®\1›ÍSSSï¿ÿþ{gòX„Bá?ÿùÏÂÂÂ`0øzhy¼u¤ÓŽàxà!èܺuçàᣧ.Ý|SàÝ›Ôè•uÆh‹?ÙýóMD£_¼XÈïb˜Çïâ_íÝ¥ÑigŸ¿üùâÃuŸŸê<=ôvit&“É$2àMÂà¬ñÀ™Ûßžï=s{xZëtÆh€H°YŠ €`y RL€0Ø=å5 s:»ÊÓRš@ZÌ(B ÃB°…gBLY°àIÉìq{ž«*ÛTo8¦ŠçFCCÓF¥Ö Pª+**ŠŠŠ“Éäƒ*++Ÿ?V«µ««ëÌ™3û÷™á8N«ÕŠÅbT¢T*/_¾\VVf4Ñ¥¡²K§Ó¹víZ­V«Õj‹ŠŠ  Ï󉤮®N*•²,‹Îq8è#:77W]]m2™ÔjuSSÓ­[·€ ˆ»wï~öÙgùÚè<þ]àŒÅ¨8a°Ùþ³çƼJ‹htØåGCÇN]mÿúÁ×Ç^Hä®3·ûÝ>8H‘ôââb]]]oo¯P(ÔëõÇÍÎÎ666¢L¥R‰Öîe2ÙÇ7lذbÅ @ÐÜÜüðáÃL&ãv»+**PÌsGG‡ÝnüøqsssAAAaaauuõÄÄMÓ@@©Tž;w®¶¶V(>|X  ¦î÷ûøøã=p§×ë‹‹‹‹‹‹Ñ}³Ù¼|ùò5kÖh4›Í&:;;·nݺbÅŠÍ›7ß¿ÿÞ½{b±ø£> …hT­­­Ÿ~úiIIIUUÕøøx.œ'N£œÍ¿Vêe:7oÞÄ0ltt´¸¸xåÊ•‹Åb±lܸñwÞY¶lšVåüšyäñÖ(d1 ‚W®\ݱû›ã®åh4ªFEx<›Ðú‹ IDATö&&3¯oŽv1Ì«Ñyü.þѹÁd|ùBÔè}g‡ßnQMÓl†#h.` PÃ2Ý ËäÃ]áD’ÉòÙl–e³ˆ7S,lšæyžÉr£µ²nýœÎn‹²ÃrÓc¹uॣ_îê“»ïÈ<·eÞÛrWŸÌÒ7«ë6\U½t™qèŸXüXаhsÀ3ÁÓDœ‰+õêªÏª.ÃáØ´iS}}ýÄÄ„T*ýú믗,Y233<Ï?zô¨¹¹ùÙ³gHÜ ƒMMMSSS2™ìСC………………8޳,k0ÐËc2™D"†a>ŸïóÏ?‹Å2™ ™¥æ8Îh4 »ÝŽîÉØØ¢ËÉd²½½ýéÓ§jµZ"‘477¯Zµ -ÿ9Coð¡(*N„ÂGàÅD¢qLoIɰßsäÄ•-»vþ47´à=ßûÐã'8Bð½{÷¶lÙ288øý÷ߣÕ¹\þå—_ŽŒŒ„Ãá±±1¡PØÒÒòäÉ“­[·ŽŽŽ...böèÑ£ÖÖVƒÁ`±X ªªªZ[[Qó¡C‡æææÌf³ÛíFvÛÙÙYÇÓÑÑÑÜÜ<99933ÓÖÖV\\l³ÙX–Õjµ›7oÔjµãããµµµ†y½ÞÆÆÆ±±1ƒÁ ×ëC¡F£ܱc2DªÕê]»v  Íº{zz¶mÛ†"¢¿ûî»8ÎÉÉÉ‘‘‘öövôzÊåÿÅÞ•5uïÛþýµÃêêZ]«¿ùƒö¶X ´" s,‚ ZPJÁ¡õV»]ïùÞmë½µÛ:âEPÃæ!D2‡!3™ONÎüy?|j.«]Þ¥ïÕ×g_öâWrÖIΉÉÙg÷gïÉššš®®®ñññƒ¾öÚk)))±(ƒÁ’’’’’‚Ã…<Ï †7"±Öét;vì8}úôÅ‹ñ H$’Ç_»vÍh4öôô ô®Ñhšššnܸg@.—ãáàêVl%Ša˜çËìa³ÙZ[[Çààà»ï¾›°ººº¸¸X\\üòË/'$$Øív (êðÇÿApœH£ðzÖZ[h:qêë–+k1ü¦Ž8Žã ñsøôÄ\ËÅ»%Ÿ=q¶û·1c9!H2Q/ ‹Îˆ›‚ÉÀóÎÎζµµíÛ·¯®®îÂ… õõõ¸¸ìt::¤Ñh°þ°···¾¾^§ÓE"‘±±±æææìì솆†––±XTŠ#ŽßDüþ5§G ýDT§7㈡×eÿ÷¯[÷ú¦S´OX¾úéæÒª×ãôWVTI¥Ò–––ùùù3gÎH¥R•JEQTww÷ñãÇ;::𛛝_¿Ž-Ù;wîĦ’Ë—/§§§kµÚ••‰DÒÚÚªR©8ŽC“ÑhDÿ±\./--µZ­n·;))iëÖ­ƒ!ܽ{7??˺'&&vîÜ9::j³ÙFFF6lØ€ ±V«Ý´iSZZZfffnn®T*ݼyó‰'FGG1B«Õ¦¦¦Þ¹sÇ`08Ζ–‰D²°°‡C§ÓÙl6‡Ãqûöm±X¼°°àõzÕjuRR’Ãá€[·n¥¦¦æååÍÌÌà<¢Á`@5i4EQ&“iëÖ­V«uii)++K&“áMøââ¢D"D6<11‘‘‘a2™¬Vkvvö… °ÂF¡PdffbN¬»0£,ý»ýWyz¬¬¬œ?~eeehh(11?JƒÁŸŸÿÊ+¯¼õÖ[h—G?ýïýfãøcâiit\ŽãŽŽÿù˜Î ŸRÌ>³À;Â$X£aÑ´æàx`è( #o梬K3‰DîOÏJ÷V^¾Þ.Ÿé™˜—)Tw íc ·Æ47GÔ׆®Íü ë½Õß/½ß98Ñ!ïìÿo[òJ˦ÕÓ4D 8! !¹¼£ù“ÃFƒ.¶¤ˆj`4WWWAÀ‘ùÉÉI´îŶœ››3›Í¸Œ—ç@ €Œ¿?óóó±JÄÂÂÂÌÌL(²Ûí1ÝÅh4®0ŠÅri4šîîîÞÞÞÎÎή®®ššƒÁŸdã™@¢xš²y<úÕ•U×Z”â\VŠÖ\ýûO•Ÿ´}ò—Ѷ1ã7Wo9Üa“~©¸°äÕW_MII)//—J¥[¶lÁy\“ÉTPP “ɲ²²Àb±¤¥¥íر#///??_$UWWk4š©©©={öàÒS½^àÀìì윜œììì7jµZN—•••œœ¬ÓéÀétà7´¯¯oÛ¶mb±8===##ãí·ß®¬¬\\\\\\,--żH£Ñ844T^^þÕW_aG(²ÙlÉÉÉ%%%iiièE™šš:vìXBB¾}ûD"Qrr²Ãáà8®»»»¨¨¿ž:.---))É`0 6¸$…Î+Š¢T*Ubb¢H$ÂJÅ’’üeày^¯×K$‡p¹\2™¬¬¬ ëßyçÌÌÌâââìììôôô’’»ÝŽä’eYÔ¡Ÿ/-‚Óé¼téÞ¥¦¦&&&.,,X,–¢¢¢7ÞxcóæÍF£3³ã³q<#<­©ãqwqÇ“ãg5:F£Ó¥v†Qâ·ª_á9€Ð à‚Îî·‡¹0$ ,Ë L”£"á9†ãå„bè’o•ZS(-˼—’‘..þP,Ý%–¦ˆ÷¼Ÿ[ö~nY²xïûâQa~z^fVdwAQF¶$cwn¶$/Gœm4-ðR´kuyáÛ¿}9$ïžxvmm )¸ŠŠÒ˲1{"öDÄÂ\1‘ûÏ\.A15Åëõ ‚€yv»âøÞiI’¨3a>ˆ¯¸ººZ]]]YY™––VXXXUU…Õ‰ÿ[Ÿ~ÿÏ €Çf‹V»½Mvo^«ç€(p¸Ýö•ÿüÛûn=£ìœ\>×Öisû{‡ËJË÷ïßßÝÝ­V«»»»oݺµ´´¤R©êëë/^¼X__¯T*I’Ôét999]]]؃=>>ÞÓÓ‰D¦§§KKKåry ðx< …¢ººúöíÛ&“I«Õž={öÃ?ôù|g×®]ï½÷ž^¯'b``@$¡›¶³³³¬¬¬¯¯ÏjµNOO÷÷÷a4322 ƒð½½½ ‹X–]^^–J¥7nÜÀÎmF3<< m6ÛÞ½{‡††¼^¯ÉdºtéRbb¢J¥™LöÑG©Tªh4*“ɶoß¾uëV›Í†WJ³ÙŒsèA§Ó¥¤¤¤¦¦ÎÏÏ ©TŠ·ÇY­Ö¼¼<•J…aÉýýýùùùhäÈÍͽ|ù²ÑhÔëõ333N§ÉeŒC?_4¼^ï•+W,‹L&KLLLNN^]]µÙl………¯¿þúŽ;,K¬©ç÷~³qü1ñ´4úqõ+qÇ“ã…ÏN0˜Œ3ÊùXàÖCºiò·*gȰÀ³Í"Ö;ün‚'x + , °ËÒ¦xàh†Žòãõz‡GFÇÇ”ú‡ÇúGòeïè¤|ì|ì||¦O¡PÞë¿ÝÙÕÑ3Ð;04Ø#ïž›ŠD<ïg'Ë8‰mtPÆ’!X*ús\k$AÑ— ˆµµ5‚ ðXæºÝnAü~ÌbÓø‡Ã±¾ˆ $¼6°,‹{ƒG©·ë7[ߦFÓt0T*•ÝÝÝÝÝݳ³³“““øÒñ@¨8ž  |>¡± <Âð?—ÓœcÅúÕ·W*?iûüëéŽûKgZ¯NLªNýó•¯* \Ÿ ííí‡nll\XXèèè(((èêêŠ dffÆBåÔjõÉ“'N§V«ÍÍÍÇ/Èøøx^^v¯ôõõI¥ÒM›6iµÚû÷ïoÚ´i×®]óóó8“˜˜ˆRîððpff&®çØl¶“'O677Ûl6»Ý.‰¼^o¬¼Ðf³•––ÆV‡”JeQQJÚ066vêÔ)‹Å¢Õjsrr†‡‡ÀÜÜ\]]]zzºF£ñûýSSSYYYjµÚívWUUmذaçΘï#Í))):UU“É”˜˜¸}ûvNçp8Š‹‹qÙçó™L¦ªª*½^ÞÓÓ“••eµZ5MNNŠúàp8š››———ñ·b}Cøóå!v8­­­ËËË===Û¶mÛ²e‹Éd2›Í»wï~ñÅ>|ˆ[>_ÇÇs„§5u<® ©ƒèl”bh¼$o´ûÝa.H ¼< ð4°Ïr<ÏGIxX €GME–e–gXžææðç€â€ …(¬À ø‡Âü—ó—{úÆóÅÅJÅ}’$c½'mŽÖ T[WVVH’ôù|KKKŸ~úé;wärù;wÎ;wôèQ»ÝŽÞ†Û·oI’z½þôéÓçÏŸïêêúñÇÑÙl6›M&ÓñãÇ?ÿüó«W¯¢$œ””4??ïv»çææzzzFFFîÞ½{àÀ#GŽhµZµZ]PP`±Xðíù|>³ÙÜØØxóæM—Ë%‚Ëåjhhhkkúæ›o0uÎáp?~üâÅ‹###ýýý|ðAÌL¢P(Ž9rïÞ½öööÌÌÌ—^z)--Íjµb¯¡V«‰D"‘H«ÕÒ4ÍqÜÜÜ\RRÒöíÛÕjµÙl–H$­ z½>;;Eîh4:44´gÏìAüì³Ï¾ûî»ŽŽŽžžž®®®“'Oº\.†a°?«X“âó§Óùý÷ß›ÍfFsàÀ„„„sçε¶¶&%%½ùæ›ÕÕÕjµ:–lGÏÿ½à CG’ˆBaIÇÇitOŽšN|º ÕÜþî|GÑÁ¯cI”À¬¤n•t~1ôµh0…B!¯×ë÷û±Ü›{G£§˜H$J²à‹ K ðp,-Ð`#ÀÓh·Ž!# °úè‡~(**ª¬¬D—Çq‹¥¸¸“yb¹ÑÕÕÕååå&“Éh4–——;NÜÏòò2NO€Çã™ššª¨¨@ ÊÔÔTSSSqqñž={ªªª…×3|öׯeÜà»B„÷I’$>…£™ø8>‚]Äáp‹!Ö+Rø6b^É___±Á˜çù˜±·G{7z»±yý€|lcÜ~ýì#¾(.(ããÏ]„m…~—‹’!Š2ØV½¡ËÁÚ±wåÏQ\×:‹ÿ WÙÔ«W®W¯Œ+a 9³‚ý0^“@ló¼É‰UëŒf$ XJˆñ ‹1b3hAëH³/=KwOOOOï{Ÿ÷ÃÑÜ´%PR~±SPó¥jºïrºçÞîïœ{ιy†/Ð<[B§ŽÃ§&ÿt/ÛÖ?09“,R¬®8ÀpsoY–Ëå2¦²1MSUUÿʉeY±X,‰D£ÑR©„—t]G7'EŽãð<ŸÍf1.PÓ4R2Nß½{÷úõëCCCÛ·oŸšš"FÙÑÑÑL&“Ëå‡yž‡!Î8ÎÉE¿,Üv Ã(•Jsssóóó¥R I**« à …B¡R© ªŒ¹tЩ—ã¸B¡À0 iÜ0 š¦išÆÂÈz†A[²ã8µZ¸{áîßD—e¹X,âtÓ4­X,ââOðãÿØÈårÉdÓqà5==ƒ¡‘.ºŸ¦i›¦Öh–f‚ÁÐCGŽ÷œ–ägNPµ-§ŽuŸÉ2e·& ¶ÌÈªìØš­ËŠ€¾Ñºí°’§k Z>ÚuîÕ×ßí ]ilÞÀCñ=§’ð®lj&ØeI‰d%šS²D†åe‘ùš$¢S~Àþ^Â;tÛ°uòj&$K§¸š ®žkƒ¥y¦ìè²­+Že€¡«`¶®€kg«b\ËÒdpÐ÷C·TÑ[“\CÑ ™×µŠã(5ht@ò<Ñ1DG“lÍ`øš ©Ži9øÁ#C)2a«x`š&Ò\·2a¤z¶‰N#؉§QE–eü$†Q­VÑȇ,ÁOU Ã@Ð_Ü?K]×ÅKøßE$\n&±Jx¿¦iJ’D„A+8Þ!ô~#½ÿù4|Ÿxàêº&‰ó©Ôå[7çiËG6ÀcZOõÿê7gÞoýj¬¾tµ*Zà‚ko€ÎÀXÿØ@æŠÃÞ?~HÌ.žÁÑHÊ`­t:ýñǯ]»vÍš5kÖ¬yóÍ7‡††À4Mš¦I¤¯¿øþˆÅì ÛÄùkš&j‹äDùQŒEóÕHŒd J/^òÏ>”DÓ42­ÈU2³ˆêk†?  £(Òc `æüùó¹\ŽÜùiüYüÐî_-oO&È‹< ²¹ööÎÝoí;ôé±GÑèÍ”DY²-ÕÒ$¹*Ô8Y5ËnÐèþqülÿïöEã1ÿö+VÁíW8YEk´%Û[®*5V©Ö$‘8u iãy^UÕeB ]j’ê`¦Ž*#Ù¢¦ žçk­»†j[Z},ºn™¥Ás\ËÔ,Юµhi`iàYàY® @)^´lDY3Î:™*$c4VPTX Ô:€ [&€¤»Ž ~fL&™‹¾ñ°du]¢U'Fk$èvݰMB0ý9ìІßW¬‚TUEâ¾tf.¿Èkš&Òôeüª1Uˆ¢(H,üŠÁ"½ˆ¸7hôlUUj+y¾¢ÙžªY…DV.ó]h=Õ¿eÏÀáS“Í·õŒŒÏE#I–.3u°,[.—9Ž«T*‚ ð<_©TðUP«ÕªÕ*&ð¡Z­V*–e†)—Ë<Ï“êx’eYl„¦iŠ¢A(—Ë·nÝúꫯîܹ3>>~ãÆ MÓÊår2™Ä84M‹EžçËå²_–eiš®V«˜î[Æ*¸hFÄÃÂx xLJrGÓt©T*•J(9˲x ÃðvAÎg2™T*…ûÆb1” /åóùl6DZ|6›M¥Rè!N§³Ù,EQ Ãär¹h4šL&S©T<O¥R…B!“É`1RûÊçóù|ï±P(`I,íöˆw‡ÉÉ\.—zÌÇÇÇÇC¡ÐåË—³Ùìììl4Åß‚ ,‰¶þéˆÇ“Éd:™››ž¹w÷»žžàGŸ=üãhtšaiI!¾Ñ §Ž~h´?o4†.ãÔ!˲ ˜7 ŸÞ#C ÁÐUôÅP<`4ˆ%ZÀp\5Ó4q3pLdËó<úbäÍ7ß|388xóæÍá[7¿ývøê7×®_¿1|ëæ7¿¾:üá±s‡Z/|tâÒ‘—>l½ØrúRË©¡ß}Üže%À×r5ËV…791^« K ½$ÆÎöy “ó‹x$±×ú[xè,Ò4íîÝ»˜øVÅ‘‘†adYEÑ®Çó¹®›Ë冇‡‰[ó¢¦Ú¾¿_¬"˲?ˇ$I·oß¾}û¶ß#ÞH­E¾+‹º >*K{oౄ"ÇmI†ɤyIõÀ0ì SD}ðØØÅª{èR‘Á˰ýšÁ"·¶kÛ6Yç!‹!¨’õú¬ÿ¥M{¦"iÓõT­&EEd’óSá •Š{î ¿š˜ êiì?–eI<)¹(F êTù¡›è­¡ªj*•:xðàÍ›7eYæÏŸ%Ò IDAT8®­­-Ó4½mÛ¶7bv-ÂÚ‡‡‡÷ìÙ3;;‹±J¸oË"JíÕ-è(?©îÕ“‘¯hü0G"‘––––––H$bû’,už¶m[QUU]×%1Oxe=<ð l+IQC—ÿ:21mÙà©–§l‘j;}fëÛ_þþôÔŪýüŸ³ùŠm<â—¯´þæSD¼ü¥ÁYN_d‚?ÿ:¸®ËqEŒc^ê ¿hX"%kG‹dð‡.á䨝 3dJzõ€È¥ïenÐó©©þŒ"O(ŠêííF£^=[ÑÒÜølk\ üH0 ˶]p\ð€)ÑÁ`èíý‡Žm_ÞÛ¯4B øaxdˆá£ÞÕ$QUUI’Яnð›÷PmÛ¶ë§Ø)N»xgö÷§þøÛcg>8yöÒÙ¹|EvÀÐ\ÐTÑQ1=Ë׃‰ÉÙ}ûß¿|å•/ÎÍÇ¢±D4–˜ÆñïøøÄõá{ͯ½óBÓož_½óùU¯?÷ü¯ÿcåæÿ\Ùüoÿþó©éy×õ ו ±RúúÏ_bZ= ]J¥R±X,—Ë¡­‹çyŽã:kšFQæÀWÑh4b¢+¯êGœ@ü”—eYL˜uûöíçž{.c¸ÒÄÄD­V‹ÇãO?ýôŠ+H<;&–¾råÊêÕ«#‘žŒÅb³³³‰D¢T*I’ä'‰DecYð<SzáÖnè £ëúµk×®]»†^…B¡T*‹Åùùy$ñ„pT*\›Æ(AiðêžÓ?ÉhlàG†"Çé²$¨r¶ÌŠºnZ›- %¶Âwœûõ[ç{pþVâĹAªX¿ÿþµk×nذaýúõÏ<óÌæÍ›×­[·nݺ;vLLL|öÙgDõ%¾ 8eˆêKr´ÿ{WÔÆ¹fóœ×<ä=®TW ŒƒA` ’Ù´‚ÙB0v ’€70¹N¦â$3çÆ@b“øNlbÇ@bÀec±ƒ„±„@‚–ZR«[+B[km©µuÿóðç2®ÉÄSw*™L|9ÅEu‰VK÷ùÏwÎ÷ÁÎñO*ÙpCMq¿éÇüÇÀétÚíöd2)•JÅb±D"áñx¯¼òJccãòòò“6¶?úd÷ðlâç•”Éø½¾ÁÁ›gÎØ{íº5ÒJÌûaßßv§†"AÂãÝGCI:–¤"QªÑñTÚK›w¦íÈ“ãWŒ¨¥§ç/O®Öÿ±µ‡ŸO„Åb)//çp8z½~·ÊœJ¥äryee%Ìuttܽ{÷ñãÇ‚ÈåòŽŽN—L&FckkëÜÜœT*=~üø¾}û***`c©–––ùùy“É4>>ÞÔÔ”››ëv» ‚xõÕW¹\.Š¢¹¹¹.\Óh4p šËåBQT ÔÔÔ,--ÍÎΞ¥ÝQ‡#@auss~áwvv°K œf»ú@Þ Cx …Âf³ƒA¸™dYÖãñèõzƒÁ@Q”ÏçÛuRù|>·Û½¹¹¹¹¹ 9:‚ ‚D"‘h4ªP(N:544ÔÖÖ¶½½ 3™ŒB¡€›aE¡þíp8†‘Éd°áÝ7ß|Ãçógff™Lf{{[©T"¢×ëw‹K†mmmÕÕÕ=|øp}}}kkK«ÕÂj\Åð"„B!AœNçöö6TÜq‡×.(xÅ,‹V«Õh4pü8¼üÙÇܺu Δéìì,--ššZ[[ãr¹Ï?ÿ<ŸÏ·Ûíð°?úd÷ðÌâ·¢Ñd‚±x£ØN Òèë7ÇVWu½yFïá—øUM3)o$¦ÒïxC ¢3Ñ Áˆ!4rD£Qhí€É¶_U£3i€žØ9ÿÙµ³—‡Ô~`@jN~ôÝÜ[Ôœú··ÎÑú~oåñîæî¿ÎipµÖQ€é»µi”ˆëmÖí$ÍìŠÐ€,Xæ?Çi†ŠÃ0D¯ŠªŒ&„e³,›d2ŒÁ`ð%¨#ÉZ­†OßT*¥Õj=ªP(ü~ÿÔÔTww÷ÒÒROOϵkצ§§C¡ÉdŠÅb`6›‹‹‹µZ-„N’$˲z½^  ( `F¥R …B«ÕªÕj…Báìì,²g‹Å‚a—Ë-((0 »ŸEQ …‚Ïç›ÍfA U* •Jéõúêêj³Ù‡kjj¶··Y–)((ÈÏÏG„ @U1’$'&&D"‘ÑhÔjµ"‘(''Çq§ÓÉãñîܹãõzišÖétGÅ0Ìét–––òx<£Ñ¤R)‡Ã™ŸŸß-¦ïÝ ž-0Ñ@ èÛ!\¶o¾¿Žâx*YÕèÞ«ß¿ujìý>õ=¥ëòwwÂQX°´´TYY966V__ÿøñcÀòòrOOÓé¸\®®®®sçÎaæñxšššÊËËKJJºººàA|öÙgëëë(Šž9sÃ0ŸÏ'‹«««¹\n[[ô AÈåòúúú–––ÅÅÅîîn‡Ã±‡£µµõСCB¡P¡P666Áî}F.—‹D¢ééé²²2¸fÕjõùóçggg ÃÅ‹ Åüü|OOÏââb__ßÇ»ººrss_|ñÅcÇŽƒA§ÓÙÕÕURRÂáp D _9Ó4­×ëá …Ìd2‹¥··×årQµ¼¼|éÒ¥ÉÉɺººöövx•"‘,d!BÓ4Ü? ròäɼ¼¼¢¢¢O?ý”$Éh4ú¤WûO 8:àæÍ›°øÔÔÔêê*Lh455½ð /¿ü²×ëÝ>õGŸïžMüV4:H³„Ÿ²x(hêøö»Q¥R‹l÷hô~‰§Ñh,®1úü&Rá ùâ¡P$ ó…FG"X¥}J§€žØþø«[gþzkBë[õ€¡¢ãóï»z¿ÿäÚO®}2pûâ•[ŸýíÎÔã-õ†Áï ± H§X=b YpMƒp,K0ñ$ˆ&@0š!#ÙPŒ&@šÑÈ@gA€,³©¦¾Î`D³l†a~Ž F@„¢&€ßïw»Ýfeee||œÇãi4†aB¡pbbB @c˲4M+Š•••JÕßßÏårÝn7øû¸o†a Cmm­Ñh„“'''›ššt:J¥ª««{ôè|‹D"Çõz}qqqaaáîCZE———ëêêÖ××m6[ssóÔÔT8QUUU«Ÿ~ú ¾MÓ'??¿´´¶Ê‹ÅV«Á-KUUŠ¢(ŠæççÁæSb±:Rƒ¡¡¡akk Ã0‡SVVf±X(Š2|>_©TÂò÷“ÁÄ=<`áp„ôáNëͱaƒÅB'Ó ÒŠôºû¿:~æ.¤Ñ½7G#1²€ŠÒsss‰daaÇãÁ ÞÜÜœ@ €Jìv{IIÉáÇ×××ÇÇÇ+**ŠŠŠ8P__?66ÀqœËåö÷÷߸q£½½}{{[*•–——8p€Ãáðx¼‰‰‰`0èr¹t:ÝÀÀ€P(¬««»téRnn®×ëM¥R¡Phbb¢¸¸øÈ‘#ýýýJ¥rccãܹs?üðC8N&“ UUUããã"‘:¦æççŒ@‚«W¯Þ¾}ûã?žœœär¹r¹¼««‹ÃáìÛ·¯¡¡A§Óݽ{·¹¹ùÀ¹¹¹‰äÎ;‘Hd7aXee%Žãét&T*•@ €®’™™™²²² .p¹\‰Drýúõ‰‰‰¹¹9'‹¥R)ô«Ð4=66V[[[PP““ÓÚÚ:22Wú3¨î{<žÝi©$IÚl¶wß}÷¥—^Ú¿¿^¯‡e‡½Áà{øðªÑ¸/ÕhH£WWuzÄ´G£÷ðK<ÍÔ¢S[D8H„ Б”Fg3)½»°ö/?ô\½óÑw?uõç7&î+ô–@Êà"QW`‡Êº¢2ÉÆ¨d:™J³³Wó%›¦@8µHxÃé °î(؉oxãÀÌúÀF&dÌæ ÊVÕeÕ‚M£1Í2Yöç,  5JDb³Ñ@Qôþýû"‘ŠO333©TJ£ÑTUU-..VVV.,,hš^[[;wî\uuu}}ý¡C‡ŠŠŠ´Z-Œ:Áü~uu5Š¢0:#—Ëß~ûmAl6[KKËÌÌL(Âq\ Øl6 ÃÊÊÊ8†a£Ãçëââ¢D"Y__w8---R©ô×h4Š¢ñx|ggçàÁƒ‡&Âl6WWW)¯Íf&“ EÑ7Þx£´´tsss—ÇÃÓ&¢¡¡A«Õnmmåää[­Öl6k³ÙD"ÑÚÚø»áu·Èÿñ÷r¿˜4EQaÒ`n[ õ‚.Ìiô—ß C}u»ïÖÊ&bYÛ?22òæ›oÎÍÍ?~\­V“$933ÓØØ877GQÔôô4Ç«¨¨˜žž>{ö¬L&s8f³ùþýûï¼óŽ^¯G$//¯¼¼üôéÓÆh4^¾|Y¥RÁFËjµº««Ë`0ÆÎÎN‰D233³´´tâĉ¢¢"è•Z\\ljjzðàA0”ÉdB¡°¶¶V§ÓÍÍ͵µµÙív ÃnÞ¼yúôi™Lvâĉ™™™`0¨T*E"l½ÿþ‚‚‚3gÎ,,,ø|>>Ÿït:Ífó—_~)‰”JåÚÚÚ©S§æççaéæÁƒ§NÒh4°õG Àq¼®®Îh4îš¶Çnjqq‘Ãá¼÷Þ{ r¹¼¶¶öµ×^ëè蘜œ\XXhnn†ŒÕÕÕÎÎÎááa’$Fãèèhkk«Édz|EÑ4}åÊððûý0=??ÿúë¯?÷Üsuuu†AúPß÷ðÿ¿öSÌ1»£Oz£Q¶G£÷ðK|xiié¿´åz†»tý“‰‘d8àб$`h†!¬våâ#ŸÝðl÷=Ôrzüý>õƒµþïÇÝžøŽ‹l;ùŽP(”J¥ …bppðøñãkkkÁ`P¥Rutt(•Êööv©Têv»qçp8“““A 222Âår¡YH,÷õõFEQv»}eeÅb±X­ÖÉÉɪª*·Ûm4 ÷ïßïp8ÒéôÇwƒ€555ãããƒatt4//ïÈ‘#f³Ùf³LLL466žoScc ž‡ žQ >¬ñŒ¨ñÁÛ˜UcJ±àðª4Å¢½‡ 4B|ž³®˜ëöíµ¯X˜½©©I*•*•JµZ}ðàÁÍ›7ÏÏÏCÙihhèìÙ³>d&ŸÏ/--µ´´èõúééé>ø ´´´¨¨huu5Ûív¨!9ΣGOLL|ôÑGÕÕÕÛ¶m³ÙlP$¾yó&I’jµº¶¶ÖívƒôDp'¦iš¦éŸþ¹¦¦²Ö~¿ÅpbbB.—···¿öÚko¿ý¶Ûí‡ÃGŽQ(†Ý»w¯±±qóæÍ~¿ßív¿þúëÅÅŇƒ ˆŠŠ ¸å#„âñxEEħ ÁÌÌÌÔÔTkkëŽ;ž>ÞÐЖ ³³³•••%%%†±,«Õj¥RéÂÂB*•27n”Éd‡ªªª‚ÇÝþ#N†çŒÕÕÕ¾¾>ÇÁÎedd¤¢¢¢¦¦¦¯¯ ˆ\.ÇÁÐp]*¶Žÿ#|X.—ƒA·Û]VV¦T*B©TÊãñˆÅb0Ó«««ƒF‰Dd\8Þÿ}ø"„À>R"‘Øl6A"‘BÈl6K¥R­V‹šžž...†å·Ýnß±cGQQA™LÆçó•——«Tª|>¿¸¸xðàA—Ë¿Øüüü‡~*ˆŒ5þ§C2™¶Z­:®©©©¬¬lxx.2è™A8MÓ0òGïì:^LÀÉ&p9Ä ŸÿÇ{»ÞÿèÏ_~oñ&q^Ç/yó“oþ}5åù\†N»Cd(CÓH`.C% <›çC)vŵ±¿~w»õhˆ:vO!~¾ hô‹ñ4i$ýî IDATÿc¼táÃó˜Ãþ«4:ΰv–L \&OE¢I& )†©T ¬^!õ “Éüv8–²h„‚LÞ£ˆL6P <â¢J#!] ÑHÈ!!—㲠øÝx•¤¦§w``dbplúîã¹ õí õOã ?Žë~z¬ÿqryL»Ú;±|GaP,õ<˜î›þ·k=Å¢½º‹'ðYŽõy‰»ý—Îw‡þB¸1XdkÖ‘‚ @¶YAÃPÙ.¨ú • =K/|2Þ`»@:9Ž‹F£°Þ(<ÇÌd2…Ž"¨øÂHÌa¼pïÇ Ø&Ir||\.—?~üxhhèØ±c'Ož´X,ÑhT©TªTª©©©éééË—/È›Èd2`èË0LA’˜Ëå„g|Z«Õ‚PŠ|ÝÝÝÐYEÓôÚ u¼àÙt:%ÉD$‹øB!2t{ÙD: }òÅ­Cç?ÓÝŸ÷~Þ;ôxr^"ªS*fJ"„8ŽÓjµEEE[¶lÁ0 úVçææ8޾xâĉ‰‰ …B166vãÆ‹/:NƒÁÐÐÐ0>>eÈ@ ðñÇ÷ööööö–——oÙ²;/\¸ÐÒÒrçι\^^^¾iÓ&¯×›Ïççææ.]º4>>>33ÓßßöìÙ®®.:+•ÊêêjlP³i~~~ûöí;wî„çE H- BÈétîÙ³È+hšïÝ»g2™º»»¯_¿þôéÓéééÛ·o_¹rÅjµf¢Õj=pà(ªaî,..VWWƒÌcff¦¢¢Ö´AlÛ¶mëÖ­°ju»Ý555°‡~¿ÿرcwïÞŸŸ×h4W¯^½pá‚Ñh|DS‚ Øíöžž‡Ã1::úÊ+¯¼úê«<€ ËÈȈR©„)•J*úëXÇsG>/\ (‰Þ¹s÷ò¿~ríÇ»æÕ„#–׺£|~ã/_Ý$ÈH>Ï¥2Ig He2|žÉgÓ™x! &G\:—&à :ÊÄâÉD2™ŒF£à3EQT2™üm4B /dsH` 1œ;–ö¥²4BÄ"BÂßÑèl&ɳYŽeX–5™,5ûJjl+./IKÄõoW7ì”Ú^}¨Htè­êæ·ö¶îÜ+ÛTV¿uOCIÝáÒºwöÔ¿SYÛ°µ¤ÌåR¹\!.Ÿs9œ=ßß\ÖésL–¡hbr ¿¬þ1¿·©é <ÏCñ˜4MÓ`>ž±êBH8¼D…9J ø?`'‡Ö,dîÇáÍ4ý·~úôé™3gÄbq}}ý‘#GîÝ»U1·Û}òäIP[ŠÅâÖÖÖééi´†þ ÏL»Ðß#„l6Û§Ÿ~ZWW'‹kkkÁ”|¾Öö ë­^ðl:Xõø#A qBË“V5[íéxä/Ÿõ4ë¿ð¹~TüêöðùBç‰.ÛŠŒ“BPjmkk{çw, Aï¾û.H€ÍÌÌ>|¸ªªJ*•^¹rE¥R!„0 ;räHÁ"‰Œ?~|ß¾}G½víZgg'8+Ûíö±±±?ýéOõõõ}}}û÷ï7›Í°|ôèQ[[[yy¹L&»ÿ¾Óé„ÓØd2uvv—¥( |Öôz}kkk[[›ÍfƒTê“'O* XZ­ÖS§Nž[­VK$’³gÏz<žÙÙÙÓ§OïÝ»·¹¹¹»»ûÁƒápŽ:—ËY,–®®.‹ÅRXîêtºS§NY,ŽãæææNŸ>m±XX–õz½---2™ ÇñL&c·Û/^¼¨T*Y–M§Ó »wï‰D]]]ÐŒþù«³¡P(Ô×× ùuÆ ‰D$‰D¢ªªª‹/ö8ÞuüÿBˆçÏrH@¾Uï7ß|{¼³ë_þzÍDı§q†/}öÝŸ¿üž #—…0p2äXŠ£SéX! <Æö`ÒæK^½ÑßÒþÞw=óóK+;ÐèÂw­Óèu „^ê¾Ôe³c¿ Ñ¡,E糑 móÒÁÊ&ÙTˆŒSÉ`:O& Çiš†íßÓF ŒÀgy$d Óœ'š¤h¡ʯ¡Ñ±â8–É2ÇþÃÙ0‡Éj7Z›ÛdÇMN¯Áá[tøôvï¢=°ì \‘%©ÇüKN¿XÜ^Œðy#±$ÃÐ ì¦éµ HÈóàãŒ8.L‰\.<^*ü™Íf ãðf Óè-®Ì0 ‚í ®¥È! à0R؆ý)ÌÉ_í¿WS©”Ùl¶Ûí‡C¯×ƒâŠÍ‹‹‹jµzqqÑb±€@“çyÇ ÕôBº° e6žç].¸ðz<ž`0XøöBÑ\ùžóy·Ž?<Êåx–¦rŒÙƒÅ2™h,‘&ãL, ù?ùâH1¼«r{÷!¾š°[ñD,Ö<`A™Íæ@ Ó$ pÞB÷˜Ãá0 .—‹ ¸»0 c4£ÑèZ·Ûm4ý~¿Çã1 ÀÂWWWFcooï²wíÏm\×Ù^fšL3òÄiëÆ“Ôþ©S;O9²¤ÄVÊŠâÄcñ\EZ¢íXm¦m”I“z[ª­R”d!$`}¿ŸwïžþpÈë5ii<w\Éø~"»wï{÷~÷ìw¾sãÆf³yæÌ™Ï>ûŒÝÛÛÛ†a  OŠÒˆv»=Ó…ÇMÓìt:èA‰«Ùû÷ïãK3p]·Õjá˜ò}¿Õjݽ{#ZåÜ»wÅ$Û¶}ßG®Ìž€)ƒx ¢(¬.iÇwïÞÅ/$Žcì!û»Ýîd2¡ßïã3ä ‡CAÖ××QÊÒétvwwïܹƒBy4:Äß(‚'ÀàoŠÿ· €&€*+ËË+¯»­­n Œ®}¼#_ªÎq¬–ŽÚh# œÐµlE£5?A§¤Ñ¨î´wΞ='hôt~ü–ã©×Înown´Éhô–è0ݸcõFkŽ1¶Ã2ÇAÏ; Eëºþ¨ht"Ž7:Pí±åùbHBH|äЄ° ø¡¸À¹^DÉau"ã >9Übz‘G/"NàHBrXÏó0®ü…`3 i f²‹MN$¤aQg6¢pþf;<â7Hw#-ö<ýC\×u]÷q]—íƒù‚ì@ÖÎÍH”‘î â#p…€¡n<0Š"˲°Û,âβ §x"@Çý¾.OFòø·ü݃~? uÂØöPýü+¿y=»qõ?TßýwQ€‚ïFZÂQá!8ºåð…‰ëº¸®cw²,˾ïã “Ãs-JqLBØß÷»ÝîéÓ§Ÿ~úéS§N=÷Üs/¿üò7°5lÙ4MÌÆÃš,x Àä¼KQ©•î^ºô4(Fî±^A ª*k]ב:»®Ë-€g¢ÛaWš^§÷‡T¡o\¾â<6¢Ù—óXAh4h„_l’R¾±§({NN1Åÿ>uŒÃZ­þÊés3K•öÈz ’w¤_fk,Åаô}ILS mG7-ÕumŸÄ_*êèn÷ŽÑhJ)Liô·O>ûóV§4úo^äFO|ç$Æh4Òhô¼c4úÑÚhø"ž˜H£H"H|äÐI j:ùѽ¨çy¨‘`Fˆ÷±®›qœ‚™p½A‰}š$1›GMÃ…ÂðÐq‰­&‘Á#PJ‘Ic˜ wÃ3ê†1Ô„a*l i:åἎrdœeq;2TœËã8fÙë'Çaznf`¥‘ I ~*IV)ÇK3 ƒÑëtIBü#ÍcàˆÁcÏÙQxÕ^Lñø€ú¦éÛ†ºÛÞOiLaÜ$n Žrå·~òò;ç wß»yP¼öÛ?½¿±·3Òs4a~êh4  ý~¿×ëa²ßï†!fÇêº.˲(Š;;;„Ó4A¸ÿ¾$IÃáÃØÌê§Óé`%]×?øàƒ÷ßÿÎ;ׯ_ߨØp]w0à¹ÐUÓ0ŒN§3ËN&“ápˆÇîïïc o¿ßÇ ƒƒƒQAF£Ñÿ‡“Éd4Y–¥(Êd2‘eýïƒ$I‚ `HÛu]AEÙÙÙÁÖA`û ‡Ãáp(ŠâÞÞÞ`0˜L&ØÛñx<ƒþß›$Iãñƒô”RMÓ0K÷Çž?ÖF·nÝ*—Ëøn‘íííñx<™LEQEEI’TUÕuý›îïO&EÓ4C%U”:­öòòÊù ³™ÊJG°‘F£S‡ èÔ4Zó=Ë·mG·lÍóœ ¦²C:#í$~ýõsiú÷t~ü–ã©WÏü¬Õiòá¤ÑiÃ;ÕõQÔZ‘-ÉH£5C·m[×u]ןŽãøK£CHBHbÜ!,_Aúa iJx$ HL|@ D^º@|ˆˆc ( câùžIc€$IìºnàùÄõH@éç v6ŽÑÇ/ÕWÐ#Q5Sn¤#Ó'U:Òü¥B®´h„uŒ}„ ìRo£”b` lÛf\ܶíc!CH±d$ʦib(ÏÂÒ³ µ¶f8ÍD~R@ËREAµuƒ¸^owwÞ[{[êåñ°P½ö£¸z6wç_>e.¿³”k^Y^¿ºöv©TÊçóår¹R©4›Í«W¯V*•r¹¼¼¼¼´´T,yž_\\l4óóósssÇñ<ŸËå*•J±Xl4+++¹\®X,âQ‹‹‹Ùl6“Éär¹f³Éó|¥R¹|ùòììl&“Y^^®T*™L¦^¯_ºt©^¯—Ëå™™™………z½ÎóüÚÚÇqçÏŸÇúÞ¹\.›Íæóù+W®\¹r¥P(är9žçyž/ —/_.•J¸±R©4r¹\(ÖÖÖ²ÙìÒÒÏóF#—ËÍÍÍU«Ulã¸ùùùb±X(xž/—ËÙl¶V«a#«««õz½T*e³ÙjµZ*•êõz>Ÿç8®V«Õjµl6›ËåJ¥R³Ù¬Õj™L¦Ñh4b±Èq\>ŸŸ››ËårÇaûxìc¥¥¥åååL&³¸¸ˆ_ÝüüüÊÊJ¥Rá8®T*•Ëå|>ŸÏç‹Å"^ûS|íÈd²¥R¥ÂÕÚr£9;;wöBûm9¼ÕœËTæ‹Í‘ªj†º/ÉCÝT\ÇpMËÖFû$V=zLÔññÇ÷:í)žâ$žzõÌ϶ڭ‡Ñè“)†ª®¡Í:O?’FG”(n4PLÑð¢’$†$êý–43ivc&I…>£„¢GÄâCì(Ç Ã(Ib ALb‰…ALl×±l7Ð5°-Rìß/³ /¥ãÄÇ(uZhÑY8œ`kXB…í“~³ )VʼA0ÈçMó]Œm3¦î8;Öu]x Ù•e™E “£t@B³Ál*l-}-Œ¾§ßÈ3sh]×ñ¼Øß÷™Búë¹Ý¦øf‘@hÛÊd¬Úz`‡áÝ{Ÿ^©6ÝG\mýÙ¿¿òÚâ'ï~Ø›­­ýò…B¶Z)U³Ùl¹\n4ù|Ùíüü|.—+ …B¡ÙlæóùjµZ(VVV …ÇqH@‘¼"§Ìf³Èggg‹Åb­V+—Ëår™çùK—. …r¹\­VkµÚÂÂB±XDŠŒ¶Z­–ËeŽã*•J©TB–Öh4ðÔ™LÙy6›]XXÀ6‘±Õjµ¥¥%dð؇ÅÅÅ|>_*•æ^½z5—Ë]¼x©|.—«Õjȉq7$åry~~¾^¯/,, Ìf³Åb‘5»¼¼ŒÔ¿ŽãðB8Žk6›¸´@®ÌóüÌÌ ÏóÍfóâÅ‹ù|¾Ñhàjä±F¡PÈçóo¾ùæ… ŠÅâúúúÌÌL½^Ç{\¼ðGü¦û;Å“ Ž+ñ|£˜Ëç3%®8?¿øëÙ ×¼Öš)ø¨#¼¾PšãCE‹¢@Ñä=Qh†d[𭦂¢7ŒŒvD«+XH£«õ›7ïl}¶=¥ÑSœÄ¡SÇÆ­{å{ö¥âÙâï;Š/G>ª8^«oO4 NŒÑhÉÕ%Eƺ¯ªª"6 #y¨o48‰dÛs(¨ÝJ–A4$– ‘ Ô‹\3 C`ILÐT›D (² B=ïP‰áyžiê€Z^èÜ;°lèÛð™àt• 5±zºo(>µ#@Ф]ÒÏÇlzì^:ŽéY²à±/ å’éPˆJb懊Ñ£¬Drì–x„—Å’;g§CO<#nÇ¿‘Ú¢›Žô)5xž‡<>ÝÚS@º(ß³ïÁh¨Ú6‰ALÔáX‹…•ç_yç\~ó_ÿ{8×X;i“‘¢)º¢(’$¡(š¦íííI’„Bß÷'“I§ÓA{GI’°6ŠeY¨@sAЉ[C]Äh4šL&¨pP¥ßïãYpg¬X„Ê Y–UUÕ4 åXøiwwW×õÁ`0 vwwMÓÔ4mwwWÓ´ýý}EQPƒaYÖÎÎÎh4RUµÝn+Š¢ªªišãñXÓ4I’z½ž,ËŠ¢à¿’$麮iÊ?Æã±2Å#áhཿ¿¿¿¿ïû~¯×EÑuÝ­­-ApÊ$ÉuÝáp¨ëº ‚ L&“n·‹¿²(Š“ÉݾeYÖu}8ƒoúú¦x<0™Hã±(ŽCQlwy¾ñú¯fòõ·:‚½-‡›Æ™ùbŽ_ÛŸHÔ°ô=Q𨮸¶££o´ObÑ »cƒ•_©_~÷öíOÓ†w‡ÔySS| ñ¹S«bÈ|£%ËÙÚ·Æ*„VdNDÕÖÓÚhT`Ið‡E£Ýö€C!P<è$Ó‹’$$J\-qd NºA1ø1Xv€ÂŽ„‚mù¦áJ²Nb qF1J>ü0ð× \Ñtîœ7ªï½<[ÿE~õ\éÚ›ü;ç‹+ë]ÕqI·]' –¢EŽƒ¾Ñ˜â#Ë2ó„Æœ'Œì*Š¢ë:‹R+Š€GŸlÀl<@{f8Šæ¢u¤,80\ÇjšÆÃH»%I‚#Î „dóãñ8…Ñk c+Š‚VƒØ äߘÿ„ñu<0Š¢©?ë_@Ô÷=ËlïîþþÏ´ô"±@ †".WüÓk§nýóígV®ifz”„±ã8éy‚½Hac‡Ið !ãñ7âMˆ <8J¶ Ã,ÜÎnQ̲ Gž6'W€¸ˆEW;’áv6Nå²:ÞÃá³0«›Â?â8FïNÖÌ4`éÕï@’$ý~¿^¯cEtÇqØ«9|<¦_ñ,í´'é7}}S<8|ÎEH|°ßçùÆ«g~5›çF£ÑðÎ"Þ1§Ž)žâ«ã©_œ­ÓÝ>éÔA,Û.F£#› S‡èhX~ÓDF«ªŠ”ôK£Ñ¶ãÜ|9€Ö@‘$@‚Ð1b߆„„ahûQ @âÄ÷Ѧã0¾Õj ƒv{kss£ÝÞ:ôì´·Z÷ÚO?ÝÞ¾X¹òóùÊ ¿þëêÚk‹•7+—ßàøÓ—–:cE‹©àE ¡þÓ !¶e|òÉ'X|AQ6—ã <6yã§è«•ÎÁÇíhQ‚Ûq SAº_áßi#J)òò$I˜iF¬Çí”RÛ¶1t½³³ƒõ&à(ÆŒYVxvv ÚŠá,õdžâëDÄuCu} *I\/>Ø·%ÕPÄliíÇ?½öêÜG×oíeWß6ù‰m:­V mËZ­V»Ýîv»ãñØ4MÇqаã¸ðÅW1È\äˆà2”ã8HX¿tÂÝPV„/mLÓdù¸¸mÛ8"¶¶¶Ð´®×ëõz½V«uûöm|õû}H-’qI€+XÛ¶IJ¸eF:‚¹e³;Å£!ŠâòòòÞÞ.æoÞ¼‰îÍÍÍ{÷îµÛmæ[‚a…»wï¶Z-˲X ÜÍÍÍÍÍM C ˜mêË;ÅWDÅQÇA4™ãÕÕ·Î_˜Íñk£Ñ;ÂxdXf:¡kZ*óžÒè)¾:i4óÆh´¸¨æ+›@½«*¾iX¦iš,^뺮¦iŽã<ŒF€=`Wþ¸¹ûak¸¯…CÕ!qûžƒfhl'Ôq,JHH“ðÿñ»¿{þ¹ï~ï;ÏüðÔw¿÷¿üþ_<óÃS?xæûõ×O?û·ÏœzæÜʵKõUîÚõâúõìêÛ¥·ßûÇRåG/¾ø@­˜ø”øq„Ž0èñ¥|§38Ø饗^xá…^¯Ç̞юƒÙÊbåmUU™èþ‡½kjòÞÓûO´3½L[n;v¬;m½¡p´€` %" Âè¢EQ,ÖZœkÇ³í®¨mWk•NµŠ ×p“Kr{s!÷ûõÍû¾ÉûÙ>šr<ÕÓíîì5ÏON†$ñ›ïó}¾Ïçy<O$Ñét¸£‰âá/ Y,ô=£ñm?gð`°˲hqÆ'†ÃaÔ•Q°Iˆp éâñ8A;vì(//Ÿ››óù| ²ž`Û¸-ág‚8–Æt$‘ t(äs9C4 äóêhqÁ¼h5ùæZEíÍÏŒ·O˜Î\ýÑêØÌÎ ß~ûí-[¶äääääääææòùü#GŽ8¬_Q(øÚx„KPj¼ ÁÃ$Ë`£xìQ}nb _/m¼^o¢Ú)AjI’ …B8Ùl·ÛñþgvvöÃ?lkk«©©áp8X<´qãÆ¢¢¢;vp¹Ü††‘HT__O~eÁ£I†a03–”(áÒKäÊ“$ n«¿žNb)H’ôx®ZµŠÏç+•JxDD°g‡Çãcï:ó—CI$ñtË01`Áj¶\¾Ütøhé3ž®FcýJ’F'ñÛð Þh…3‚Þè_¤ÑPMHõÛÒSh4¥Cq°G sRûÙw­ÇÎÿùrûP÷„Ò€ŒÒ‰5ÆÆY`–d^t[íNsÃgÇn·þ8=3. ˆ{D½]½=#Ãý¢Þ®?]û¡¼zߪÌMo®]·.+kejêß¿ûo§¦¼“–21?CÚtÑT(Fs“6£N.›ËÍÍ}õÕW±(•à¥÷ËÈMÀét"'F~œwñ›¯}ñ߉[K–e±yéÍr‚%ãö*…Ä›’$‰K=øˆÝnÇg-,,¬]»–Ãá¨T*æQœ3êШI?ö¡Úl6|µÿµIüg‚¿Óòy£, X°Ù]š9cntåá[Ÿœì˜4Ÿ¹ú£Áä"T†Í™ï§¥¥]ºt©»»{hh»¾¥R)ÉeggÇÚã›$´Û¥÷!‰3¡ÝngÕ 1 ³´s~é]P"‚Y8>ˆkÿçkµZ733#‘Hz{{ïÝ»wíÚµ´´´Ë—/ß»w¯§§gbb‚$I‚ l6>ãh´ø±ž„dþXèÍcQÐIü"b±˜Ãáøþûï- (•Ê÷Þ{ï™gž¹téÒèè¨@ H$è“ñù|{öìyóÍ7_ýõÕ«Wc]T0 ‡Ã###•••Ï=÷ܪU«–6à$å€$þ}ˆ³À‚Ãfÿᇫ}òÙSL:»Ã%¼ÑISG¿iôÉÜc#†O1u ,PÑA‘õI4:J†™˜1z¾¼.àmäìÿ¼ú‹ïÏßì^ð’d ¢Tâj•ò]Q&Àƒ¨Ýi.,Ê0=NÑá8û°q%‹Æ€ ‡|3sÓ¹¼ÂMœüÌ­y[x…k3÷NêšLNöêë¤ZÉFb%£>šŠxõjÄ¢„FµqãÆ¬¬,•JÕÝÝÝÞÞžpà±, …Äbñ;w„Báàà`ÂÖIQÔàààýû÷'''»ºº·“‹‹‹gdd¤­­íþýû Þ€¡Ì£˜ š¦%IOOŠs¡PÈï÷Çãñ¶¶6@pëÖ­¾¾>$ëøŽ`ttôÁƒÈÔ­VëÆsssÅbñÄÄÄèè( °‡\¼½½] 477‹Åb·Ûx<‰$þ,øN`è@4*×ëÜ0 4@”qÙ-gÿµy÷¡Ÿ>új´mÜxöúM«#° ³¬]½nÓ¦M³³³ì£œu$µ^¯×b±”––F·ÛͲ¬Ëå‰D"‘¨§§Gx†¡ikÃ'&&ZZZB¡FÇ0 c46EÓ’áúC´ªyË’’’žž~îܹÊÊʆ††¡¡!´ @ÿñãǹ\nffæÉ“'‡‡‡‘4 }ñÅyyyEEEuuuÃÃø³ºÝîÎÎΆ††Âªªª¶¶6Fƒ»8ÎB€×ë•H$UUUuuuƒƒƒ Ãøý~†ad2Ù¾}ûª««¹\îéÓ§ÇÇÇQâ,ÄÌLÿ¾´\o0’Ñ Ž0Qг‰°¡PŒ¦Š=¬ €‚øøôÄïËvNÏN`©abŒeÁXüO¯!t„¿ÄËÊÊD"Qÿž={¦¦¦†±X,‡ºté’ÃáÉdB¡°ªªJ«Õº\®Ý»w·¶¶*•J”²N:µ°°‹ÅúúúöïßßÑÑa4…BáÁƒ?ÿüsœ»B½eÙêêêû÷ï }õÕW“““`4Ož<ÙÛÛ;33£ÕjïܹsðàA•J¥ÓéÊËËÛÚÚÌfó7¾ýö[·Û­V«+**^xá…Ý»wwuu‰D¢ÊÊÊ™™0 ååå}}}sss&“éæÍ›»víš™™ ƒÉµÄã`Áït’Á€76,:üŠ¢Y‡Áâµ:°Å°òð­£ÿ2Ö1i>ßrËb÷k”úߥ¦¯Y³=r¹\&“a4#‘ˆÑh,,,DŽ;>>ÞØØØÑÑa6›ÑÞºgÏ»ÝNDZZZIIIkk«Çã¹råJmm­V«Õh4•••b±X.— …¯¿þz``fffø|¾X,V(mmm|>¿ªªJ¥RQZZ:66&•J›šš8ðî»ïj4šD9¨Z­æñx8½€§_©TºmÛ6­V;;;[TTôòË/—””…B‰D’‘‘QRRR]]=<<,jkkûûû »»ûÔ©SMMM6›M$åçç—––âZKâ)ÀÌ“––µZÝÓÓƒl¸   333%%…ËåÞ½{‚¨¨¨8þ<‡ÃyöÙgÓÓÓe2(•J.—[TTtñâÅÍ›7¿õÖ[è™ÆÛdpg¿ §éXœ¢jtÃÉ?>%ðNk³'G “øâïê„#† 5ëW¢,í‹ÒrCÀ;rºœ~·;„‚~¿?Ñå‹é­$I>…F€Êè8sµí`ã ±†¼Ð¡W¾þÇæžûcJÝïô…t:R!·ZL*•*ÎB8BG©¸ZkÌÚ’/WêC$#Š@˜„H"Q“Œ@ ®K² ’éh ´:ÃVNT*e(XˆGc,f½åýÙƒMGèy<ÞŠ+h4šüü|±X ýýý%%%cccÀ²¬H$âñxƒƒƒ‹¥¢¢å+£Ñ8<@®<::š““ÓÛÛ«R©¦¦¦Ö­[·råJ«Õúßû¹ýÏzÏ®]»fµZ²³³7lØ099922²jÕªW^y%//Ï`0 >|¸«««¨¨èùçŸß°aƒÕjEó†ÕjÅOzzzZZšT*ŻǿÎïO"‰'ááŽÇÄ€§cñʕՊ-†j-Ñ8° Üâöb¸Îî°øÞ(Œ†ü7ªÑaŠ^ ÒØbøÏ›wýãG—ÿtkbBªTG[º«âXHrŸýާÑhW(2­v¹öD¢^Ÿ+àqF|P À11MÒ8¡ÿ„C¬ÞH‹p¬úôŸ^íßÿÍý½gïÔ}ÛÞ2¨’Ù"®DˆD"Á€/J†Fsœ…XX,v/o{¹Vïô…À¶HY]1› ìnpxÀî»l^ð„aÑ„Ùo÷РݘžSäðóŸÇÏR#YˆƒAm.Ù¶K>§Ñjõiii†¦i™LÆçó;;;c±˜N§ãóù˜àáõz»»» ñ‰D ÙÙÙÛ·o¯¯¯ÁBAŠ¢úûû+++7mÚ´oß>T¬áÑš4ÆÆÆòóó&NŠ¢°’`pp°¦¦&''§  `óæÍ©©©z½^­Vs¹Üááaš¦fEÏÏÏççç¿óÎ;&“‰¢¨©©©¼¼<‘H‡§§§ÓÓÓwîÜÉçówîܹuëÖíÛ·/ïL"‰ŸÁPT4 RÔ¼AOÆ Šúl®°Ë‹4º¢ö&š:Î5ÿduô„iCJjjjê7t:ÑhÔëõR©taa¢¨ùùùììlTÑO\WWÇãñrrr^zé¥mÛ¶!.++ßù¿±w­ÏM•ûúŸp”/Œ3:ŽA6-PÛ‚-½·@¡­-ÐN‹+ŒT’loow8©TÊjµ 8p ¶¶¶··wzz:‘Hˆ¢HÄúõëkjjQËÔÙÙi0L&Smm-A¡P(•J-,,ÔÔÔ( éÍá01™LýÒjµÕÕÕ.—ËétnÚ´iãÆh\#I²¹¹yll “mÜnwss3NtÇÇÇqªP___XX¸nݺîîîüjô¿ÇóñÇëõúT*‰D0T”¦é<ðÀëÖ­Ú¿ÿûï¿?66V\\¼bÅŠ5kÖ  Í$‚ PURRRRRbµZÑ"9¹óÈãŸâ^ÑèpF ý Ê—D}î|Ÿ\NIkžFçñKü&Î 9<© }þ(d¢Y.‘ '£h1D!G"‘@i–íýf3É\.—äA¿˜<Ý/{ù£«{Þúüà§'¯LÏ:R1€$Ì© ‚Àñ¨jâyà8ж’­õó„ÃlP®”Õ•¦ÜŒÅÃZ=åz ¨˜Ü9“'K:ã‹aÁ—pRÊyr÷žV§c1ʉ,ˆó–¶æ—T Òb¶ÕÕÕmÙ²wÍfóÖ­[U*fi•——c΀ sss;vì I³º ÅàààÔÔÔåË—{{{¥:îd2ùÃ?ܹsgdd¤½½ýСC>ŸתqIlxx¸ººÚf³a2ÞãI’>>111<<Œ™z½¾ººZ¯×ãMËãñTUU©T* Ãáyמ- öª¦R)ÔF[­VƒÁP\\\\\¬Ñh8ŽÓétUUUhÕ“ÉT^^ŽsƒÉÉÉ­[·ž::ÚÒÒât:C¡V«ÅbN§S¥Rµ··Ëd2¿ß¯P(Ξ=[ZZêñxt:]EEÅ70& ˆxßš÷žù¶ßµ±šìÖX,)Nb6r¹¼´´׃‡††êêêH’Ìf³ÃÃëV­*,,t¹\KKKÅÅŃƒƒ¸CÓtQQAétzzzZ­V˲V«µµµU¥Rù|¾¹¹¹†††ÉÉIŒÐQ«Õ³³³¹\Îl6KÜ×f³;w®¬¬ÌårIòDŠ¢p+FŠ€ôxñÄ<òÈc=ÖÐÐ Ù²1åsýúõ6lÀÞœŸõæ‘ÇÝqW£m$®F#–Ë Ò`ÉÓè<~‰»‰:¢ÙœÞ‹¦!N§Cá`<ÊÆ/‘D`b"ÌØ¢GÎ\~ñèG½^4ø2Q€ +°,‹m¹\ŽÉäñ –~“æ…š†¥ÞNP¾ µmDE)éA9}]n»¦X¸&w\W8¯)nλ¾Ÿ£®NjFÕ&«/|kbê…š:¥f^Qåž§ËWRRárüþ`}}ýC=ÔÙÙ911!“ɺ»»qMk~~¾»»ûÊ•+6›Íëõ^ºtiß¾}Çb±œ8q¢¿¿_§Ó-,, õÔS$IŠ¢ˆÙˆùKYÿ"î¦8j)nõ&~ª6©<Îã—¸Žç8£3Ë@2”J‚X(Ì$~§¨rL† #@ÀƒwÏ]{éøÇG?øÜ›‚$r,¿Ü(B6&ÇFKEýî¦SZË4¹0ipêÿ£wîëç5­ûªÆu]ãÐ8¯«¬ß+Mjç’Ÿ§¦ÊjÔz \*—ÎqŒ@ÑöÊ*õ³Ùlmjj:tèÐ_|QQQÑÝÝ=22"=ètºcÇŽÕÔÔ”——>|xffF\“~çw***;::”J%ÞYÊ=+**zzz®\¹ 9Ž£(êôéÓ&g2¹\ÞÖÖÖÞÞ.—ËÑ J¥rÏž=È FFFöïßn­W^y¥®®îðáø°MêžÑ_…ÐññqH§ÓmmmÛ¶mkmm}ã7FGGñ“÷x<ùã<þ"¤"‘x8„»Œ |âNе ø>øø+¤Ñ7Õ‹h1 ø"Í{ZÖ®]»sçÎ;w¶´´ÔÔÔìÝ»÷­·Þòx›Ê$íWË­ß Þì»=uK®×ú>…á[9ùÕ,y~Æøåò¢ÜòÕ,ùÒx鎺ÿŽ|`æÎ»Ÿž.©}Áè0qÀ¤¹x–O§ÙÔÕï¯y㨕¦À`0h4,‚ý¥K¹F£±Ûí R®…¸Ýn‚ 0ðN\Î@•’žÍf³Ùlöù|’Ž‚¢(É“N§)ŠB÷!>³X,J¥ç!Ø™L& Z­Vš¦s¹\"‘‡Ã¡PS;[,A¤¦C£Ñh45 EQ(çÀSó‡~uò¸ÿ B.™dRÉ4Ç9ƒ4+D¢I¯Í•ð‡Â~¯D£®¿ºŽå2IÖãZ4 A$i·ÛÕjµ^¯w¹\øíÂi^&“ÁoÝÌÌ ~3m6“ÉTUU5??ŸH$ŒF£R©„å,HˆÅbv»Ýjµ¢ºÃårI„5X­VN‡n]#-“É0ëÆãñà8Bo"ê:`¹ÐŸÇápà/&“ ™@·Ûíóùb±²7‡Ãíß8F£ÍfÓétÒÃó¸;ð2õå—_Jåðh0µÙl4Mãõ#ž€eY¼ áº€ÔÏ‚g~~SAñLá…îÏz_yÜ_¸W4:”æº}î|ŸB¡Ë[ óøUÜMH¤”Æ%´²ñZ gàp'$>%Àb‚{ó¯Ÿüûûg¾½1ìXôçDNELYf¹ ›K°Ù\Êî^X÷LQË­ÏU×”6ìz~Çîçw5oilÙÔØºqWkÁŽ–¿4¼¸¶zÏÓ5M%­]ÏîjùKYå³õµeõU»k v’4#¦2|b1à~÷Ä ßAðâµd‚(òï"fž!ˆ¢ÍÑï €h×ÃÜ Xî7ÆB´Ie³Y©\\®!„åêožçщˆã #½ •JIµÞR@,ß9ð±¢(JõixƒÁ­Oé–ƒÇH ‡Ãx¿ÁzøÇ6æ<ò2±X2ñ†ßÞ¸ns»%m´D£_;¥¸>çøàÂåxJ9àr<D£Q$Ê8uDÎ*5e ‚ vn‹Ëe‡ø»V«íééq»ÝÙl7p†Á¡ä÷ûEQÄÑ!ô€ÓB4'àÌÿ…4X ›Íò<<m»øw<·`Ùà+½ZüG …0#ŸGœ¤ÁÅ#EQÄ¡„«¡øTyÜv»ýóÏ?w»Ý8‘꟤ ^ŽðR8¦s`] ´ObÕÁ`P*ŠÿSÞQ÷î¡ÅµÑy‹aÿ¿I£Yà‰ÔB€_Š@.Á¢Å0”‡£$Ð(çÀŸð›ÚhDÈrbŠáb©l$‘Å’¨~þ5DzL<™pû¼fÚ¦5IŠš7U£Æl2Ðvm×Yi­•&hA»t”CG9t´]OÙô4ER–X&ÁY8˜4O¦"L. À1l–Q峜Àˆ ›‘6" ÀŸ‘à ›È€,ËþÔ_…BÌ?ö´ä‘ÇÒÑh6™ðGƒ¤ƒ^ŠDRélÀéM‡¢¡¥ÅSg.ì;Ô÷Ú)Å­ù¥Ïúnݙէâl:™ ƒ^¯7ö¿ìkså¶ÇýVí7~_RJ<–%µwQµuƒ` Ù¢àÙ•XFE@¹©ñˆ¹1™Ì5É$Dnõx€À† $!·Éä2¹Mf&dn=ÓÓ÷{÷ó¬óâ!mÁâlO¬þUWª3ÓÝóÔL?ÓÿY½Öús˲4M ‚Ïç)ŠÊårE …B¡°ôß|>ŸÍf …Ã0Édrbbbvv–çùt:ÍqÜ­[·HC4rLš¦³Ùl6›¥iºX,ær¹l6KQTÑaY‘ÉdFGGC¡P{{»$Iù|¾X,’{•¹\ŽÄþiš¦iºP(üÞƒuøÃ"в HtŽ¢Ò™èøÄÑ£ß,¯¨ò5O¦…c Ä郵 ¶ŒæEn¾@çD™¸Ê /J¬ªÊ:´lMgØ©WSrû;»š[ûûGû‡_å‘]Ÿ|šìï®o<³ù÷÷ù±¬‘b§Ìl4^·ûFY†¤D3 #Ã0$bzoM´$:{ï> ™[Ä}˜ÈÒ ]ÑT €#´hLæ¢I!‰ß^¬ÅEÖ5L &C7$U,K0È6Ò%]T Q·dÍÐÙ0uPe@:€ ¦‚°Ž›¦¡ ¥&ëδqXž ¬it.Í)‚ÈGÚÏ\ 2MU¹n{ïôîêþ³ÙÏ=MU5 A_³Çí÷xü{×á‰Ûíillj CÁú¦ÆPEEÕÁòŠºÐɉnº ÷ÍRûjžÐB¡hÚ/¢Ñ‚Èp<-I‚b˜ŒŠgsütš?Òpjû;»ë›N÷õLFgíp'÷’Ñ/ÎåŒ *§© [à‹y™e8–¡Y–Eñ·ÌÀá—f÷¿ º­¡±….‘ÒÊišbÉ[&‹µtÁÈÂ…‰’Æš…T :€‰ tÓ0‘†@µ@±@C``@ºi °0ŠA«V΂œ†²†ªAºò9­L–-Hb˜[ñÙl‘Òɦy½»çÇ“ß-Ì̲© WÓ¶÷Nïs‡Û†¨ÏêB5®ûH èo …B~¿ßåry<ŸÏçõzC¡û.gý~¿×ë­ªªª¨¨ðù|uuu@Àårµ]__ßÔÔD¶!z«®®Îív9rÄårÕÕÕù–Á`0|ðÁåååuuuÕÕÕ ÕÕÕn·Ûår>|¸²²Òï÷777“ÞÁáS]}¤¡!Tïó{\µuµî/¾ørïò¯ëï&£ç²¹Ë3ª"¨âÒ¤N‡%€> IDATÌd"£ƒ¡o{{ÃщGF;ÜÉ#{öïžžꄚ۶”z?ñü3š—)MÖ°‘ã[F+E&ÏÑ”Äð¢ Ë2I&+ÇY–uïh4,ÑÓ¿¥ªÉ—ÓuÓÐMðLYdÝD–‰,AótáXÝ4±È_òˆnjø¶´6°¸`léR5ËDDak(:È&’)é ë IÀˆÑ ­AƒfŸDÓŒ‡e ÒEéJQd'’3ËZdšÇ²–¹•¨ñ|³ãƒx"?ö¥Ž´|704“Y Ó 9’°‘H$HªF2™¤(Цi†a†!wêíõ………B¡ËåR©EQd_†aÈïéééL&C²>²Ùl2™dY–ì[,IB9ZÚaYÁ²l$inn%VVét:™LæóyR’NN†a(ŠŠÅb¿÷xþ˜0 §(šÈrùL6:>qêÔ鯪ÜîÆwKêHPù /rº&é²vø¿ñKý©÷âdA¹‡Œæ^’$òÍHd4˲÷ê½È/ò:î6 Ë2,Ë ©dgk˜‹ ‚¢fhš„Å£ ÀKþ.¦| ¬[HCXNjٖeX&VÁLÍ”• QK°LJ”hMã±Dc†" Eÿ$£ÉÈ9ã°lA…tZd騭ø…kcÓÓKÔ ^*ROý·;>øaŸ;|ª+æ>þß…¢A&)¶³O{bHŠÃ`I[ âçL¶áy>ŸÏÛ4M#vr)–%Â>Ru@ZÈ;SlÙJ&“---¶;Ôƒ’õ¥ØˆaYš²éLSÓÑöüòHà~etA2§ÒÌä[SrÇ»{H§Ž™é¸#£îä‘Ýû>žœž¸n}åݺ½uH‰á=’:xž/‹ ÃÍ0Ì=:uÜ/?Ͼ-£`aÕ´t„u EAŽg¨…"+#0‘…,cÉ¢‘°Ad´…4 ë ë&2aÀ€°®ê’¢‰6t@Hó a^€YÆŠÅI–Ÿ¤(AÓìŒÇÀa™ƒLYY:ÏÑ)–be9›Ë'¢³XÖøbÞ8ùê;'ö¹Ãßu'='ÿ'—WVy%‰ÌÍÍMNNOOO§Óé_mž ëúôôôÒ§ºm®D&1,Ó4§¦¦âñøÒ—^L‡eð<üøñh4J>ÁX,‰Dˆ{2™´;‘¿÷xþ˜B·;up ÛÒrlÏþ/Ü'î7©#/“©¢-£IßèØlÒ‘Ñwò“Œ&:–Êè_-1dyŽTr§iQÒ÷ˆFßgb4,ͶS;4C·ˆ†ÖÕJä¹NѬŸíe˜€M†‘†Á@X·°iaÓe `Ý´EX&¯ÁTRl=7V鿲¿âŸjºöÕ\þÔÕqÀsåPíyÞ$A5»%8ÓÆa¹‚Äb1ŸY(ЬºbY“S3½W®3éix·ù-½£?ö¥¼§Î¤³Rr.³é¥Wüñ’’’7Þxcݺu[¶lÙ¾}ûÞ½{Y–ÕuL 2/t]gYÖår éºnY)¢ íç@–å±±±²²²ÁÁÁh4Z[[{èСb±HšN†AšÝ»Ùáÿ'<Ïg³ÙP(”H$8Žkmm-))ÙºuëÆW®\YRRÒÖÖF¶t"ËÂH7Cz! 6”}¼ï°»á~K ïŒF Œ9Ñh‡_命öÌÄf‡ûF›Žžõ=ß>ߥɂ’ו»5¼$‘HgI’ÈÅÜó}PÑhË2LS'ËíìŽÅ¤ÅÄ’*­¢Ùʹ¹¹ÖÖÖ_|ñìÙ³Eõ÷÷S,ËÞ¸qãúõë}}}W¯^•$‰çyMÓ†††È·M4½zõ*™™LFÓ4]ׇ‡‡/]º422bvÚv-äÚ{°¶á¸#£wMêMµ Êáš@fåŠ"[Ô„ûMê¸_î&£yYá4ƒÑ‘0[Ô/Îô'™(¥¦DCÐEÓ,„iºDҪĪŠ`š:œk»ðéÁÏ_*yùo/üõÕ-/oßñ÷W¶lZ¿aݦ-/ýðà¡Ã¡Æ½¾o*ý}Õ¡ð±ó™æ¶TÙY³y×ølcL.ÛäÛ?—Ëuww—••mÚ´é™gž)++;wî ºÉÆDŽcŒ?þøã 6¬Y³fÕªUëׯ߸qãš5kJKKãñ8™ÛD•ʲlÿేD7Ûf‡¶µrqѶíüùó}ôц V¯^ýþûïwvv’ÇA ƒ!Ç'2ݾO䱺ÄS5<<¼sçÎëׯ“Ýí͈aÜö• o5Y/:îqTH§s©ù*ÝzþÇD:Ír¨–Ήw“ÑÏ>³jÍš5DF“O™œWÉdrûöísssä4N¥R¯¿þúåË—ß~ûíX,F^¬££cçί½öÚØØX</--}â‰'Þzë­îîîÍ›7oݺujj c|îܹO>ùdõêÕk׮ݿÿ7 ~øá‡­­­ Û¶m+-- ‡ÃäÜ`¦¯¯oçÎëÖ­Û¶mÛ¥K—‰yQQ …‚m¡çð/#ŸÏ‡B¡x<ÞÙÙùì³Ï®X±"‘H$‰Í›7?öØcÏ=÷ñ‡ÇN¡¡ÃCãn2ÚIêpxxܵÄÈèáéÂRM«üÃ.1ü¹Œ¾í±B¢Ñ:€’á»Î¡Ý5M{ÜÇöûO¶vöÇXEH±¢d Pdi i¦¡X¦ €,˘››ûÏ·ß=qúûÈh4OŽNô÷÷…§f&GGŽÿã¯\Oÿùŵ/ýÇó›KŸýÛ¶áÕU›^fÃæûËêh,c¢àøñãÁ`°­­maaa||üÊ•+555—/_&Ò°»»{ýúõ‘H!499IÚ€aÙl–4."ovØõÌ™3Ï?ÿ|GGG*•":`bbbhhèæÍ›Ä œì;==­(ÊÔÔTooïèèèÜÜ\[[ÛÚµký~?©K¥R†a ܺu‹hb¢Óét8N§Ó$ôKŽ©(Êììl$I$vþ· ]]]ååå.\ŠÅb—.]ª¯¯okk#DMÓH{²±±12$XrÉçóäÁL&³qãÆÎÎN˲FFFHÏó䵈¹]<‡ÃäÝ ªÚ‰Z=h ¤I¼V$Õ 4ó¿ìûoåšÇÏ_±¨´«ý‘+Ä I9iÓ$ ñ%NÛIš&´ihmÉBJCoBÚ¥,§-W"š“-¥¥—#š¶¤¹Ù‰“8÷«/3_Æ÷k|·ÇcÏxfžýá=X¬Ø¢ÂiÙ•ÖYþÁ‘<ãñ›ñ÷}Þïû}¨H%uü2ºªrOEEÅíÛ·q·ÙlAèõz¤‰{{{‡‡‡ãñ8ŽãW¯^=xðàÌÌŒT*µÛí™Lfxx¸··w~~^­VŸ={6‘Här¹‰‰ …B1>>n±X”JeYYI’‡£§§gddå i4šÎÎN³ÙlµZ+**ª««Ïž=»¶¶655µÿþ¥¥¥|>¯Ó麺º¾ÿþ{§Ó©ÕjO:õÉ'Ÿ`†lÖE­_lC]â7 #===½k×®_|‘$I«ÕÚÒÒ²mÛ¶Ý»w#]ÒÐ%¿TF—¶–øÛy`à]ødž5‰8t"WLêøÕwɃd4•gr®Xö;íêû¯üâúû¾×wá›ãŸþçíc°äRYZ¾P` ,Ís,“§WsËÞM=&ÐÙ-Ðt¾ÀsððÑtw:\[Q_,M†¢+ë¬i]½>óÇËçå›^­P3 Ã0 A‡ºsçüÈ®077÷þûïcÆ0Ì‚L&s¹\f³ùøñãËËËù|^“ÉÔÕÕµ²²’H$xžßØØèíí•Ëåýýýü1Ï>ûìÛo¿m·Û†{óÍ7Åbñ믿~ûöm‡Ã‡Nç‰'Î;wêÔ©Ï?ÿ9@ÖÖÖd2Ùèè(Ò²ÙlÖn·Ÿ9sæÖ­[7oÞüì³ÏVWW5MOOH$:tèR©4™L™L&—ËÆÁÁÁÇ777wwwÏÎÎf³YŠ¢ÇÉ“''''‹ å~¿jjêâÅ‹¨~ÌqÜÚÚZkkkkkëéÓ§¯]»†~&)ŠÒétGŽ9pà@oo/’ÎV«uÇŽß~ûí­[·ÚÚÚŽ?¾°°€Þ6ëõú¶¶¶–––ÞÞÞ¡¡!ŸÏ—ÉdJú1À§"‘øV0N%)`Òù)‰$‰\.W©T­­­ýýýv»}zzº¯¯O«Õ=z´©©ibb‚ ˆÚÚZ4&çææª««766Àåry<žd2 %I4››“ÉdÏ=÷Üêê*I’ …Ã0ÔÕjµ¶··;N£ÑXUU500‹ÅÒé´^¯ïììôûýÙlV«Õ;vL§Ó¡iäêê*†a…é°,‹²5ÿ7/öÿ'8Ž ‡ÃÈÔ199¹sçÎíÛ·»\.‚ ”J%ªFM%J<&~©©£xWâoçíWXàŠ2:§‹2úW·_yH$£SYš0¹Ã|yõÝO¯Œa[k1øvÙuì‹ëÿöõÝ:àN³ VÈò¤2¨Í!“§™<íñøš*£‰o%*› †þ-_‚Jæx&‘Ín%“áD,–K0@3º§½&VþÁî$Òé4ªo …µµ5…BA’d.—C¯är¹x<ŽÜ½ápX£ÑH¥ÒH$¢×ëe2™V«E.g«ÕªT*#‘2~¼öÚkׯ_÷z½###}ôÑéÓ§Ýn÷ýû÷;::îÝ»¦¦¦Þzë­……»Ý>::Š4®ßï7™LÏ<óÌ‘#Gnܸã82xX,±X¼¾¾Ž®!Çqv»½¬¬lçÎG5™L8ŽŸO.— <$IÖÔÔ˜L¦h4ÚÓÓ333ƒ|P6›M¡P ô>óóó]]]{öìAs¿â ‡¦iä_*ý¶ý–°,F¯\¹B’äøøøË/¿\VVæõz‘©ã‰'ž¨¨¨@Óì%JF—Ú¯”xxØ œbé­4µFlER‰Rt,޶þÒfà¿”ÉhV€<0ðî'W7à˜p2§GºÿãÏ_O—}©iÌA£žh|E¯'¬ŠJð<Ç &±Xêñ€\(ŠKgòT6ǃ€r©–ã…¿:“¥bÉp’ Œjn´¶KÌ„e0 S(Ìf³\.·Z­híyP™ y êëë=Åbimm-Ö\ ƒT*E+++r¹Çñ|> …L&“N§£(Êf³I$’$Àl6K¥ÒÅÅE$Á+++_|ñE³Ù‹Åª««ïܹócCóææfCCƒV«Åb™L&ŸÏû|>…BqáÂ…ÍÍÍ@ àt:[[[‹:{ee¥¦¦Æf³mnnVTTìÙ³¹,ìv»X,½^_]]M’$r£¢ÏKQªÍ@$Áq¹·ív»D"©©©Áq|}}]$‘$‰”:èÌÌLCCÃÅ‹u:]04›ÍJ¥Òãñ0 £Ñhº»»ÏóÁ`°ººzÇŽA …’7úQÃçR©D$Dz—¾½ŒÛíŠ~ƽ{WD"Y__Gfý\.Wt±ã8¾wïÞ¡¡!©TúÊ+¯˜Íf›ÍV[[‹¦”æ•W^1™L@Qš---555ár¹vìØ!‰Ìf³ÃáH$6› %èy½ÞŽŽP]WW7>>Žçt:‹özžç———¯^½ÚÝÝÝÝÝ㸠E”v©þ†°,‹Å¾ùæ$£+**ÊÊÊ\.—ÏçÛ·oßÓO?]YYY4Í—|%ÊÔQj^âáù9NeÖˆ­­ä_et$‹ÐÉX"žN§ÑŽ7´×ý¬>N]€¦Ó1º`öÇ?üË;_ùnÝ?Fæ¾Yò»4Üwéîõ§–ŒMÆ@ÌŒ¬q«Ý¶µµÅäiÞb±íݻτY|þpŽ…‚yâéLx+‰ÆÓ™|. é¤â@§ ƒ|€ƒiµV\'Bú¥p SGCCÃÚÚ26äóù\. …ÐÉçr¹¹¹¹¦¦¦ÍÍÍ •J¥Ñhhšæ8Çñ}ûöaƲìÒÒR__ßÝ»wgffL&ºV‰DÂétvuuAQ”Z­®­­½víÚôôôÀÀ@yy¹H$"‚ ™L†ãx*•*n$I²­­M­V£ïÙ²U*ñÉdÒd2‰D¢Ë—/«ÕjF388(“ÉH’Ä0L"‘TTT\ºtI­VõÕWJ¥rbbŒF£J¥²Z­ét:‘Hü¸ƒ#*ø±,KQÔêêªZ­(++Cgh4»»»ѹ!>77WÔ@E!Y¿¸¸X(fggËËËoܸqÿþýÁÁÁ矾²²ruuõן? _Èfs™¤o+0pýkÌfËÒyà ³{Œ~á_¶WUU!5Œ@c •J…ÃáþþþóçÏŸë ¦æ–½Ó3ºTšb |:My½þööWÇÇÕ¹>_à÷/¼$K[ZÛ”ªQ½L"•9]§Ë#oRþý?ü£X,miÙ[SS+—+66ô4w»½­­m^¯~¸7¥Óøý~ކ˜›[xíPª¹UÖ ÿ»'ž¬—5nEbzƒ©^ÖètyŽ*›c |x+*oRêæ8Ø›-:»Ì„U¸{oxOmH,mÛ×!­ox柞Ýà n¶Ñ *áúù× 9¾øü€ÇòÓ?ýá¿ ÝÿC Y:›¤Ó¶€'œH8!ˆÐ±ä2úæ»ç—¿›w}úÍw¡-ÚnñÔÕŠÊËËoß¾½¹¹¹°°@„Ûí.š\GGG;;;‡††PoB ÃêëëÝn· Z­¶§§gjjj||üƒ>@™N§S"‘èt:‚ ***ªªª‚°Z­‡žžž6›ÍAŒŒŒ¼ñÆÁ`Ð`0466NNN ‚€f°Hm€N§»páÂðð0I’Z­V¡Ptttlll³bhš.zúKüð<_LêX\\”J¥/½ôÒòò²N§‹ÅO=õTMM ªF—4t‰ÇÇ£ ¼‹f9K Aø’¾úÞ-/,Y’Ñ%~ÊïúÏô™-¦Õ…õÁûmo~yês5ʳYØx޲¨­0i&Æ2™H–J¦2Å…¨ÇQ)âo—ÑXH³æ„ÏPøáÁ@a+Θ‘»:ë‰sCú¯ŸøãȈÎ쎦sYräy`x€eÙbè•Åb‘H$ËËËNO÷¤1C„€ˆ°¹Xð8lø9l‹ÇB9Ü—°‡×îÜ«¬“n_–a€`$Êñ0¿¼ÒûÖ¿j¦µ@’ʰÃ?®ûôOŸXíÐŽiÛ›ÚÜvnªÆé¥É,¤3Ò®hÄJ‘ÅeɳLž.Øq—ÇôÚÚÉ©F¹´ó`‡?à¶X1…²! 2,5¥—7IGF‡‹Þ„­-¯ÌŽßÍ3”×푈d6«SHeó@,McV‡¢µýû1 ÈÐÁ@¤^Ò87»˜ˆgÜž@U­èö÷#é"ý!#éœ^ZM³¼ÁL¾P¾«ìåªÉÉy#­÷ä”.Ž šÉÙþwÏ Ý¹/8` ŒOþûê ¸0¡Öè<¤žÒ­¬c—¯=óìïÿ°«Öˆ“¸Åõ_ì]isGšöرcw'üafðÇx0‡$c02`ÃÛ`lÌ€Øc33ž€g8d$K!„Í%„t®–’Z­îVWßêCÝÕÝUÕueVï~HhËŒí`7Œ×»ÑO¼‘‘ª®*UDwV>•õ¼Ï»výf¿7¤³Úôl€Dâ™ýů&§¼ºÅ‡H<óÄ“kRyÂáêõ¾«Ö¶_»1㑼óÑý#½£ñdÎa`bÊÄcÕƒµŒ›Ä>@8Dqp–)Ž#KŠûîppJ…ë¿)]ÿMP`ôçŤ98†¡åsYµ€ŠùçCý=J*£È‹ŸlÙòÆù÷OÌ^ŸJŸüª}ÆŠªŸ}nùòå=öØÚµk««««ªª6oÞ|àÀ!^÷ù|555###âƒ×ë}õÕWÅ ´¦iÂðn×®]£££BdÇׯ_/|9^zé¥mÛ¶ W™¡¡¡={öTTT<þøã¯¿þºË墔úýþ]»vuvv€mÛÑhtëÖ­¢¬´ªªããã;wªÚµkW[[›0*Íg÷™——ñ@UÕÆÆÆ@ Ëå“®¨¨X¹rå£>ºiÓ¦žžA Kª°2ÊøÑ1aìî=_8uª~ßÛï®mú¾ÕèH&»PPeCW U+æU-§iŠj˜²A¤E-*~úyËÖû¿hº811[^.ã;ñÈ›ïíñæ®ÞùùÛïõfœŒm;àȆ2˧J ¢geŰ3º¡¨E]×{6 CQQmáÇ£ÑrLp¸à§`J6ŸÀ@‡ºcÆ_>ïÛ¶ï¡ÃýÞù¬¸/ í&”J{âb±8??_YYév»çÉŽ[ž¶[Ò•‘ð•±ø…ÑxËpìKWòòLöëÙlë¨tiBêœ G’¢~ÕÓ½jÃúñ)7 é,&xäÝ IDAT ½ƒþôÆ@¿O H‘ðÕ®kŸ|öiûõ6Ó±#£}·v<¿-ú}s5v7_8íòŒŒ¸‡*7­ù÷eÿ&³Ø„kòäÑÚ+-maOh°·ï™u•kVyæ¦ã±ÈšŠ§®w´#lŒ»†wÿ~çåKçSéX_×›oí=ôÁÁPÐ731±qݺ€×ŒB8@ѲýÁЊÕkÚ;®‹Ñì ,ùüÕk×twEÎyܳûöíûúÚÕP,:íõ^èóƒ÷cétbQÞ´ykUåºÞž÷íÙ¾[Ÿ}ö÷[C#Œ=vüdkkëœÏ ….\ºø·¿íéí&SN:;;~¿wÏÐðèù‹W^Ù¹ëŸþù_ž\¹&K±M[^ìèîõø¥¶kÿùÉ‘a×d4‘޲핷(¢àöίyz?¡£wv× FzúoÖ¼ùÎþø¡øHÎ('”cÆñCiQ°0(¬%( ÆãxI ‡æ 1Ž)C„ÚŽƒÝÏ¡)ús¤Ñ¶¦!CWL3XÈi†ª™ÙXÊÑ -Ÿ=zâìÆW›÷2zi$\wùÚ¢lƒD<9;;+Œ#‘ˆ°½“e¹äo(¼œKI(Z:©Ã’8¥TdÍbŒ§§§%I*Y¡ ÁÒ;wÜn·8!ç\’$¡SŠç@ †¶0)Ÿœœt»Ý"ݹdàKîeü4PU5ŸÏ×ÖÖ&“IfD¡PH’$á¦R²Š/ åË(ãa€R.V£ñ…'jwï{ç“s j 縂Ùw|.h4!XÑ ‰‚’5m›"Ë.š–fÛ&¢¬L£Ëxp<òöû5 o)öe‰Œ1’)ægB™dÞa³ ŠfáÅ¢®jú}åWDç!Óh€tKÆàä1ö¦¬ƒŸ\Z÷RíûŸvgrw]çJ†ÇÂ!TºYg³ÙÊÊJ—Ë5·ÿ?–=~`Ïþ€_£Ã#öÖT?»áåß½xþ\«"çãÙ±›¿·?æ»Ã °Æͱ ñˆwÏ/ÜêæŽÜr´Å°gâ¯kî vµÀTiÝ_µ}ÛÆMÕ•O?ýÔ¦MÏx<“©¨Yèí¸òÖVïÙ±mÎí*¨IÓ”9˜VWÏ•;^xnSUEÅò—·oìëk0 ‡*š–œœؾýùwßÝ÷á‡ïmÞ¼®¦æµHÄ€<žÉÝ»·oÙ²aïÞ·P0èÙ½{ûðp/*RÏäÁƒ5>ßçV&ëînþùõëÖ­Þ»wgWW[.—0ͼm+8怸µtE€â¶L6[´÷€ —‚æ€8B¯÷çÀØ7uI¾Å¤Ž4:›HèJa±P¸Ú׊'  €”b&ÿìdËK5—ÕÎ^LœøòJ ”**–¸þÒ –j¯†!~*Œ1ñT#¶—÷Ë(ã¡€q`ŸWŲ¥b•Àá¶ffX·•RK}Ç@@0S2yÀŒ# ÀáX·™MÀá@­ÒÎww` Ê !Ø¡ö¶÷â¿|Pûû¡QfS@œc¸«¡¦ ÈÝÅèo«¢–ÚhjYØ4TËB˜ƒª™ÁY?3l5—9zâìæ×ZÞ:2~a(øqÃÙ—{Î=/Í#‘H4•$) ÆãñD"!¶D£Ñ¹¹¹H$299™N§…Éc8–$)‹Åãq±<w»ÝÑh4 Šóx<žp8,œ=$IšŸŸ§ºŽH$"IR( …BÁ`0G£Ñp8 …}‡$I‘H$x½Þ™™I’‰„¸<á]- Ž•ÊøIàóùúûûO:år¹ææædY‡Ã~¿? ùý~ŸÏ'¾ý`0è÷ûÅ÷^F?:¼^$ $߬gÂ5ÞÒÒúÑ‘ãÇZË4ºŒ‡‡u¨Øe̼ Žî¨éÅ‚nå,û¡Š:Ëëh4æ`2(j8+[…pNùËÉsû?<}¼©;-%s+¡˜,½Fd÷Þ8K’TYY¹bÅŠ_þúW¿yjå«Ö@€5©œÈ JREÌa–N #‘52  6ú&7N+Ú ‰Œa:6b¦ÉR©‚ª"ÎÁ4™ªYˆ°L€B° 1bpf¨F‹@ ²šmsÀ‰‚Šd ©L€”¦0I»ü[7 Š(@,™Jee̸ âÀ@$§ê¢/BVŠ‘xÚ´àÀê€YÄFÑVrº ÁÙÅ|2ž!˜«y£Ó(‚90@ဃØB1XâXSGEÅʤsZÁ^zyРÿ£àKÚ€Åú9€ ¡%&ýÝ4šÿÌ4 lMSälR–€fáh,9Ô3PÌäRñÈácg^ÞwùÃz_›+öÎácu ­µÇëꛚšNŸ>]WWWWW×ÔÔÔÜÜÜØØØÐÐPWWW[[{áÂ…ãÇñÅõõõçÎkhhhhhhii9{öl}}}CCCsssCCÃÙ³g9ÒØØxæÌ™Ó§O·¶¶Ö××777755Õ×ÿ{WþÛÔ±ïû'Uj¥ b§b‘ª.jõRAyéë"q¹´”%Jri¡d%[!8!q¶¸¤…²ã$¶“؉׳Ûg_ælß÷Ãý}zéÒ¾^壑åLæÆ3žÏùÎ÷ûù¶ø|>¿ßßÑÑÑÙÙÙÑÑÑÒÒÒÒÒâ÷ûñ….\¨¯¯ïêêÂ÷Ç⫚šš|>ßÅß ­­­½½½½½Ý·„ß~¿¿ººº²²²©©©¥¥¥³³³¡¡¡½½O’ææfŸÏ×ÚÚÚØØxþüùîîî?º¿Kø÷DSSË¥KmZÏ757768q²âȉÓçZ—œ:–ðêð…òš8“ɳKfؼ¢³ªöJC \Úõ”F;¸¸žkšVÎA4™8„îMÌ„Ò:§ƒj<óÚÄo fi|RŒ+ãñx$™›››™‹„£‘È|4_˜ŒÍÍÌ=‰ÆÃ‰L0– -¤‚³±`xvv.‹Å棱d"Ž,JjšÆ0èše[ *“_E6eÉГ7T\¶¨*ªªƒ à€­Ú˜¦©u]W[ÈyzÞ–DK@`ÊŽÄëœê)LÅ•5Pófe2 Häõœîê–e-8±L›ízÈÛ²uË1%žÊ:– xÂc»hÈÄ”ÝOÓ4žçEp‘mÊŽªxº ¶ ¶`ÉŠ§›àrº¨x¦ .oHY‘U™àš&¸ªkqª ˜š`*+Y†âÀè`ɶ‰ÀE¬’GŠc¨®e‚ƒÛ#ÍC‚©áö¸¿*Ž-»ðREñ,ÅÓÏ|¡W×QPlP-PlPmPœÿ¥üÃýdÑÉÅÝ}Ê¿ŸÒèÅ)û¬ü¿‚–ªšªÂÂE²‚,+OÛ²&繚ÆÎÿüÊ_Q5võ1QÕÞœžÏ$é—Ïçó¹\Žã8–eyžç8ŽarŠ¢°,K’d*•!™LÒ4Ïç9ŽK&“óóóÙlVQ†aâñ8I’ø&4MÁ²¬$IÇe³Ùl6Ëqþ Š¢8Ž$IŽãxžg?½sG’d.—K§Ó¢(âétš¢(Š¢H’¤(Цi†aè%üŽH§ÓÁ`°­­mll Ad2™\.‡'O.—ãyž$IœþîìþmÁóyžÏSéLšH<Ÿhjj9|ìTÝÿRˆá^^;pðëg‚w»šÊk~ ÓˆÒ4ˆ–øà<á‘­ÚŸTƒ’äW*xà:r<äxØ£shÛõLY§ˆdtÙ³‚ñ$k‚hU¥-ëù%ŽÿÔu+ß!„\l@tÁ²Á1\[µ, ï©-ó©YØvÓÏõ¢“(ˆ¢,I x ©È4Óp5Šn›Y1TäxŠ$;¦á"SW5d9ȃœi%d}4‘M ç•´¡ÏóÜ<ÏÅ8–Ôµ™MJbŒc„AHˆâ,My)œ¡rde3Ík)ZÍpZ‚‘2|‚ÉÇRtŠÏ3¢§x‚äçRLŠQÑÊrF’RHÞ"²J’Rò:Y)–Î3’C f’Uf“l‚‘iÉÌòz–WÉœ–dJG±œ0™ÎNg©(ŸŸeóQ^¤-/£9s¬4ŸS³ºô0™›eĸ ÏçÔ„¬g4'Lq*¿ (a2—ÖP˜ÌͲùŒæDyq†¢¼ô”jÎ2â‚ ¤kAPјÊÐó95Bó Éœeó“)&˜"§3Ü “›c¥9^Ž‹V\´â¢ó‚¯„h’ö¢E4’y/™‡Åâ%ó!àâàòÜý­¸hÄE/ž·“‚E˶`‚é>6 -0Œ.Ki†ééÌÌ8`I*K¦ÏÔú>ùkGùÙÑ«‰†žÞ %â'²Ú$°ß^–eä™q¤ xž‡Ãþ`Ñað¿ÇÁ.ÎØ ûOãúBä~j}þ–a80ßK|àUïý# W-ùàþžày¾¹¹9ÃbÒ%PˆOŸ5ÄßÝ–ð/‡ë‚iZމ°ntsóÏ_í+ÿ¾ªiIðn ¯ÿl.«ÓˆÖubHp%8¦dš‚˜“5^7$YEQEY– IXþuÖhÀêÏ퉎–†ºé‰Tf) ž'%ÃxIžâºžézºr4Ë1Ù¤a[6xø4ÞóàY|˜ã€ãºŠµ2TU-lÿXˆ@Q4×]³@‘ ÃÅÎÀPMCVÇÕÐ9‘AàN‰sÝ×Oœï.­ëüüxKycEuàÔùû{ŽûËN_ÿ¶îÎÉ–û{Ž]>\}§ô‡þokï®þ¥äT üôíã 7<ÜÿC`ïOò†_JëoW¶ ”ÕJêûŽœ¿ó—ÚKëû¾:Ó½¯újYCÿþê¾ý5ýeçö×ÜúæLÿÁ†{{ ”ØsºïøÅÇ%µ7¿>}­¤öfù¹[jn쫹^~îÖšÞÝ?t•ÕöUT÷¬º^zúòÁªëGÏÝ,?síÀ©ž#µý‡knVüÔw´~ ìtßÁ³ýÇ~-û1PzæÆ‘†;ª®•Tõíù±{ß™k• 7Ëj‡o•×ö—T_/­¾QQ¨¨»y¸évIU_E} ´úÆÉ¶û9Þq¸éöIßÃ=?vWÔÝ&ζu ²m¨v6MbèB\ ±°°€é,MÓ¹\óæd2‰)o"‘Èf³XsÇ'`±çÂÏB&“‘$I×uœÓ¾ ™‡C4MÃ$ØqÜÌuݧ“$‹ÅR©ÔÌÌÌôô4ASSSøÎXÖ¿Á+Ôó ·Ý‘“p%èu=±/ JíCbË=²ù^æê´æ:Çò]â•)½{B½8(ø†ÔÎqäÒ:Ç­¶GjÛ#µ7—†ôæ_¹Ÿïò½Ó^Û x; O_꣒LöÉ=jÏÐ7.÷Ž*—寉ýOŒãzÏ`þVÐñÝazGÌþ §ë¾ÜóP½<¨õM¸ïs½!«cXè“{&Tÿ¨t%¨×ß&zCVÛ#¾gBí«±+AýòíâCöÒPÞ7È]›2¾GvŽˆmxܲ{\i¹›õ rþQéç{¤Tê Û¾AúÒPºõ¿g{§˜®‘¤ïA´?’ûù×p÷hªwŠiœïIÂüõIÚ?œè":¥¯ K7ŸØ]÷žQ#0nw?nŒY÷øîÒÕÇÊÕÇZÇ]îÆ˜GmìÍ £ã×øõát`œº;#õ™ö»³×dzþÁ…ŽÁ…_çÕö1ÿp¢wŠéMÂ|Ë/¡ËÄÿá\`4qéÎØw5¾”`*äãÏB£Á²xšâeÙPLûÑãÑÛ×Í岺¡ãó}WJÏ _Jœní ™!s•‡JKK ‚PKg8Ž355uôèQš¦ÇÆÆpú…B'OžL¥R$IîÞ½»¤¤DQlwÄÛÌàà`ee%N‚¸ÿþÑÑQÜ)ÌŸTUÅ oó˜úˆlAšƒ¦é½{÷¾ñÆëÖ­[¿~ýªU«6oÞ¼qãÆŠŠ š¦qc¬ÂWáå©ëúôôtyy9î@eeåôôtA̳.,”©(JÁŽ2Msi›|Ð4ÝÔÔ„óòd³Ù;v,_¾|íÚµ[¶l©¨¨H&“ø‹Àʤ¶m }ùå—ëÖ­Û°aC!kPµmÛ¶µk×nÚ´iÕªU[·nݾ}ûýû÷wìØ±lٲ͛7oÙ²eÆ ï¾û.¾ ³UUU3™ÌÎ;ß~ûí’’’T*UH¿}ôÑîÝ»I’Ä5¡P¨¸¸ø­·ÞZ¿~ý'Ÿ|ÒÛÛ‹ë3™Ìûï¿¿iÓ¦Õ«WoذaÇŽØ/%‘H:thÅŠ+W®Ü²eË믿¾}ûö‘‘‘±±±;w.[¶lÅŠ|ðÁÖ­[‹ŠŠŠ‹‹±~6^/###ÅÅÅü1l˜ç8.‘Hmß¾}ttI’Ÿ~úéÊ•+W¯^½iÓ¦mÛ¶e2Üx×®]Ë–-ÃYÖßyç\²,mܸqÍš5kÖ¬ùüóÏ>üðÃ7ß|sùòåkÖ¬Y±bÅ{ï½÷ÙgŸÑ4-˲®ë!žç¿øâ‹ââbœþ³0çÿÔxÙdà ¤œ®Iº,J|^`eYTM”ÓÝyFŽQ ¦Ñ-­=ÃÓáÐÜ^ÂoñÚÞ²¿†"“¿¥Ñ#çÌÛº yNÓ_5þ¹€\0<0-WµÀ´Á– %ÅÑ´(Û/O£Ÿ¦Õ„ÍvŒt†°lÝÃ)^ LJaƒ´Š$#Sp¹ŽàjªèØÈÐe]“lŽÍ’ÙD‚ˆ&“„(éž&rm×’5ÞU²(ÅËi 2Ÿ0­ÒÚÖÝg:oÌZã*tŒ«cÜÏ¢G ½Þ÷í™G(ï0S3cicñTê:õö{ªÞSç×Ïóþ0Ô±¤8ðœ0¿–S„Qð­À6Ç º(hB ²å05a‰-‡8@lž1Ë7âÔí°¨–Ý sÂ4e)³*7ð´YSFùNm”8¬øAd‡ ®Ø|'°,i¦)#ñ€ÌlKže!b°Í €m†Éå8âID»ùA¥€aJ ÃÀ0%ÅQXr–¦¤,Lˆ" KÁMŽA^d) ®èú(Ûl Ñ„¤q„€éqFÕ!¾ È# ðÀ´.®LÛbx.˜7eønøaÑ’c; Èsº”<³š´<ŠÌéR2¦Wc" N¸iÊs­ ñÛ R7L)ü,}|5cŠoÏÏj¨xˆ¼0gLsœÀqÓ’ 04Q‡å ¨0ÊÓýñÏ=f& If„ÑÛǽ˜“pêHEÈmÜc±û\Žëßüþ﨟|#žØ®õ šm¾5¥ª²º¡¡˜õ yÙlö¡C‡ …Õj½zõª@ ™LvìØ1­Vk2™öïß_TTD¡Pp€@  Óé:::ÚÚÚôz=†a_~ù%‘J|èóÃóùh4ŠaØÃ7S,‹F£¹\ŽhhV(ÍÍÍëÖ­Û¿SSSCCCSSSeeå¹sçÈd2‘W#Ü©“É$‘L&“„²‹Ïç=z”©øÅ_Èår·ÛMüú`‚Ìàâÿ6ãd2éóùº»»-‹×ëíêê*,,¬©©Ù¿ÿÓO?]VVÖÝÝMp-!Òëõß|óMyyyIIIiiiUUÕwß}g³Ùâñ¸ÍfÛ·oßï~÷»ÊÊJ‚›ÛÛÛ9ιsçjjjjjjª««KKK7nn„`= IDATÜXXX8==Ml 3Œ­[·>ñÄZ­–P•JÕÑÑQVV¶wï^«Õ Q½yófuuueeeKKˆ &&&¸\®ÑhÞ½{wIIIaaáž={ ¸\n$Q(¯¿þúSO=µuëÖââb‰téÒ%±X,“ÉÞ}÷ÝÊÊÊêêê}ûö•””lÙ²eóæÍ‰$N»ÝnEoݺµcÇŽŠŠ ¥RIX¤çóy£Ñ8;;»k×®mÛ¶±X,ˆÅbwïÞÝ´iSCCCiiicccQQÑøø¸Ïçݵk×æÍ›ËËË_~ù劊 ƒaŽãjµº¼¼|ýúõuuuMMM‡>þ<Ç»pá‰Djnnnmm­««{þùçwìØa0ˆ'+‘HÐéômÛ¶.--›ßÀ9ÿ¹0ڟȯy"z,òçÛƒçÿúÊnŠP(W®hcôãx4~pê@„²®îéGµÑWÔ§ãx÷{ƒO4öËc4ü˜¡‰¹9Hä!Î&ÒÉB.Ù=o ˜ý MLŃt*OÿWÿ òd†">*òOÉŽ{°±Y42+ .ÈC¨ –C2 ˜ºØŒÒ7µì! ÍSZœ®pŽ(d±…*sL*q âàYÆ–ýsºYâA|ã+Ñ~Žƒ"õö ­¥§Wbáø€ç‡™cD÷J,óŽŒ4l/WÜ#j|Ξþž¥îGí2G·ÈÄÅ¡“o^vQU^ÚªoxÙ5¤ÀÐôJ,‚ ,b¹ C¸“oèۆה>ÙÅ2Qäx'ÓH–ù†¥ž¾Œx‡Ï”61¦ w±L´å€Ð < ‹Ý uäÇ:ªÀÇÁ~mN—¢¢ž%cîß:¥ŠÒW½,3}%0£Ž“E:â˜^q«B@šG¤v–%=oˆIp[Æo-hÇU¡ûÆ Uá'+üƒ(>²ž3f9@¼@áè?¾Öcðe©<Ñh˜ûõctÈëMF#¾pxÕlráÁd*‡ÛÝîqÚ®Ýè=ûÞàGׄ Äq½Ÿbuø×t–ÖC¯]VVVSS³²²’L&gffœNçò;QÖ'jèð֣ѨÍf“ÉdR©Ôáp¼«Õj‰áäd2™ õýÉÉÉ‹/š â2­V‹a‘ÿ€`0xõêÕ'N°Ùl‡Ã±´´ät:¶= (j6›‰jþ½VäádÄÇó«ÿÛH&“~¿¿¯¯Ïd2‰D¢ãÇWTTðùüéééúúú7ž;wÎjµ¬f³ÙΜ9óì³Ï–••±X,6›]QQñ /œ?~mmmhh¨¼¼¼­­L&kµZF£×ë‡\.·Ûí~¿ßd2uttÔÕÕ½úê«J¥âñøìììñãÇ·oßþÜsÏ;vL¡Pß»\®³gÏîܹsË–-UUUÄ1ûì³ÏZZZjkkx<^ccãºuëÞzë-.—ÛÔÔT[[»°°àv»;::jkk[ZZÖÖÖø|þ¾}ûž|òÉ#GŽp8ƒÁ`00 ‡ÃĹÅ0ìÛo¿­©©Ù°aC[[›Á`Èd2çÂ… {öìÙºuë¡C‡t:ÝCWV£ÑxäÈ‘²²²âââ………D"¡R©>ùä‰499ét:GFFÊÊÊ><99ÙÖÖ¶iÓ¦æææû÷ï³X¬]»v‘H$­V›Ïçétz]]]KKËüü<Ñß¹ººFQEQÔét.//õÕW»wïnmmU*•‰DÂãñLOO>|x×®] ‹Å~½?—¨ã?ÍF¯ªt1úq<OüãçWôkT$¿Û3ókÃè܆ÎA* ™d6“ÎC O8ÜÜüßÞ<@:ŸKçsÈ&²Éd.eµ[ˆ:þâù,!ðH%’™Tòùl*Ȥ].³ZÐéƒÝÝ7h´žÅ%ú½çFÇ?úÑ﻾¾LûþcÙ\·[Ï‚Œ=³&2x 2k^o@†Eþêóޝ)œ»‹ú9CnJÒ¹º8’^.Ò+ Ü‘;,qSJ•ÉÝ=¡f|ÅÙÇ×öq4T™­£’:‘Ð=I¸Gà¥ÊÃ}|ûí M梢Ø=îÚˆÜM–býB' öKwùø »'ÝF—³½E¾»`£üd¾wD¸s_ϵÁ„*ÔÍ1}¿ B°Im¤sauXdPà±skE½tÔc¥£Áo'5T!>ŠDîÎY) ‰ ñ}Ý‹f²Ä3¾»NŠÈüTÄÇXÝãÙ‡ÅnŠÔ;$r ±1E†â‹kTÄ7 pÞšÓ ±~¾c@àœÑ&û¸¶Ž)Yâ™Ñ&‡D®¶¥—c_ 3Ô‘! ÖÃ1Ñä>²ÔÅÐDû6 ê¡É¼LC¿ÀFW‡%XßJE=£òYè# }#âM ‰‚4I€* PÅ~2ïš7ó|C|EàCÃÏÇF—Z¦ä–ž9)M¨“i"ã0O?¥òÐ{ÏØ/°Òä¾^ží.×B‘ã]lk'×AFƒ+Œ#®/oð\(xöQŒ&äA?í)ø¥"ñ`0“ˆ›Ž6kE£ÿaüJ*ës9®Ýè}óoú¯ü‰Ï@7iNwØbtÔ×5TUUi4b:™Læ©S§ÔjµT*½t銢çòåËUUUï¿ÿ>•Jmnn~ñÅ›šš8N<GQô•W^)--Ý»w/¾~ø!‡Ã!Öœššúè£Îœ9sìØ±+W®ÌÍͯs&“yéÒ¥óçÏŸø€D"µøùùù‹/²ÙlH$ssso¿ýv{{{SSÓµk×&&&ˆ¥R?øÛ<^ý?‰@ ÐÙÙ©ÑhX,ÖK/½TQQA˜¨œ1É2ŸÏ+•ÊãÇ—••ÍÌÌ"xƒÁðæ›oïØ±cûöí§N"dСPhiiéÌ™3%%%µµµÄ?§Ó‰ ‚ `0ŠŠŠ Nœ81==]^^ÞÐÐ —Ë`||¼®®nûöí<ïèÑ£GíîîÖëõF£ñÇ7žL&­VkKKËúõë‰ì2‚ $©°°°¨¨¨ªªª¢¢‚¸—x<ŽaŸÏß»wïÎ;[[[E"¤R)…Ba0 —ËMOO8p ººšÁ`”——H$Ng·Ûëëë‹‹‹ggg¹\nkkkQQQgg§ÅbQ*•V«õá¼Oðûý\.÷7Þ¨««›ŸŸ'6ÌårOŸ>½sçÎ={öü dþŸÆÏ…ÑžHZ‡µŽÑwûF¤R¥V³ö£Ç£ñćŸ^^Õ¬ˆyRbüÊCûÿ;QÇCŒNå ‘ƒØŒÎ%³9££±æòý4ŒN¤Ò‰L6™Í™í¶T&ý(Fçó?¨¥ ϸL*LÄr™tL«U0cX­ëôm¤k`°cdì¶™±'n_¼óióÍOŒÞü[éB·ËŒd2‘T¼Iðd@áLýýµÞO:Èã¨}Ùÿ6Î}ùÂ{o¿[yêìá‹Üžç-Ý •uTfiÆ6®-±!–¥Ë‘eDîî8g,ÀñÏêÀè²sÑPb¯¼ûÙw³ˆÐ b?ð<0©OÈTe¢O„Sd® d!y€*öQEîYel€e:zùëŽIy7[?¡Æ'´þAÄüåðükï}Þ³°,uå—ô¡mHäÈ mùEm”cÌNÊpšÀ5øùf˜S%G„¸Ò†Lɽ£Cæ˜2R'05Q¡57&´—´\xõÿDå[îLÊO¼ÿ¯t‘C`É L™ Ä5*°©C M`í›SËÜ0·8yùO ªÐ‚:Ì@ÜžYÔ" º´Ó«áî%}?×|_ícE.b0&óR%˜À ò0”Á¦aH`£‰= YPá…gïÌ‚Ú:Ó4ÜW}•»vU»+N%mÇ.¯í`Ì* … 6‹A l¼‚ƒ!ÆKÜ^’éž8ÓKÛq"6f_%6!!B¡]B ÚW@B´!$$!Ð?Çö¤z™šžJfÒ]~ë\ÿç”–:Ïÿýï÷~*;ÁÔ Šœ½‰Dâóùv»}dd¤ººzqqQ§ÓÕÔÔ Ñh¡Ph41 d«5 ÕÕÕT*2ï>}úôúõë&“éÕÙw ø× ŒUëëë+++MMMjµšÃá$&&ÆÅÅ)•J•J…F£·nÝzäȽ^mÃL&SeeåöíÛ<Èår¹\îþýû·mÛvéÒ%©Tºk×®·ß~‹Å–”” ‘Èœœ&“éóù^™(Ê‘#GNœ8±¼¼¼¹¹ ———+**ž>^¥RA[2±X|÷î݆††>ø ))‰L&Cì mÛ wkGGGbb"&‘Hyyy[·n…Ãájµº¯¯/&&&''G,³X, ¶gϨU ==‹ÅR(h¯× »ºº8pôèQNùþAeeemmíáÇ> íuÁ¿Ä©Ëeêp¬„´6·Öêýº®Âh±X¥×ͼÆè×úk½¼qÅÑÿ_-†/3¶ Œl‚@# ¼@ò*G?°nw8Ýnï?ºv€p°‰7À¬Íö"©ãï\Ðó„këkÝ´ZŒÃäÞ>BKCýC‘`T!Ÿ$>çrˆÄºººßãî_˜¬&ÝOýÃùjog|u½À2Íë¡ÍMŠ‚éٰپjúä?žP$zÕ6!{xz²ÔÚ8*íåÍô æZé깫O8×ÊÒ’UŽ‘©)Ås§9f»Â¹ÒÃÉCµÑ'§«SšÖ I7_ßÎÕµLj ž;ÓÎP÷qgL-‰o“ÌMÍøãÒñ™ÈK7(“B›[¹îæ‹SNŸ?Ž#i|aÉ¢—¦žmcð›hÜqµ¯é˜ }k蛲 ì€,_”9ꆥݼÙA™£›7;ªõ–gÔi<ßL”Ú{„sÊÅ^¾(2âÙ*<[Õ5©èšTÄ3CRóà”©•.!ŠŒ™¥—§ídÊ„7:*ÑŸD"‡‡‡1ÌÄÄ›ÍÎÍÍ… f'Ož„(V©TÞ¹s‡L&kµZNg2™ ÊÔÆçó-‹Ùlv»Ý###Fƒ@ Þÿ} –›››ŸŸŸššZPPpçÎ&“ ±‘D"ÆæY­Ö±±±ÔÔT¨Éçóssse2™J¥‚à,«Õj e±X ¨@64¿ßÿ*¬íµþž‚Á dê0 \.7..@˜L&NWZZúî»ï&%%éõz¨Dj·ÛétúÇDbbbbbbzzúƒh4šL&+***))¡R© …¢±±1666;;2ç„B!6›]ZZ ƒÁxÙ?‰D¦¦¦\.WVVÖÖ­[ ´ZíÆÆ›Í.++£Óé“““ÉÉÉqqq‹¼lc…3===))‰D"i4šÕÕÕÚÚÚøøxh\Wfffll,‡3iii;wî,//g0b±xïÞ½………Pk ßž...Þ½{w¿Z­¶Z­ZFÈ…¼oß¾¬¬,«Õ‰D¬VkKKKqq1t„’@£Ñ „õûýkkk4 ‰D&$$ÀáðáááöööC‡íÞ½ûСCùùùiiiõõõ~¿ŸË奦¦šL&<ŸššúÆo ³ÙìóùœNgIIÉG}300}SápØf³I¥RǃÁbbb8ôÇù~Sæ?©~(Œ^ô…u ž×Õè×úŸègŸß»mšÕËDò¦*¶ªîÎwc*GÐD@dÙ¿2»¶»£/ïVWk?º©ã{ýÊÔŽ€õ¿Æèÿ­©„£ €#ئ›scDÿ‚¤#/¯µ`Yè:ëZ¥ˆFÆÕ|ÛŒ„:ØtóJIÅ…l³SÀîÀ·‰¯«˜¬G²¾…=«ú%åaÖí’Cn#lD¢`m¸ƒ`n\¹W¯žŒgir˪¿ëTz#Ü9¯9Ö@-‘sýëæ^¶Z匎ˆÍ=LåÉï¤"˰ÿþÇÚ~ÎGdéèJ볡ɂOn:Ž*¾vk7¥sz{™“ ¹'žõÔÉy—*OV]N¡³g—¦ì>šÜÜ0ÄÂ\ù –_šuêrcÿ˜@3§·{Sª} ¿IÌÌí¤P'äJÙÜÜ€Ÿ˜b<õI?Aíq*–—ÛéŒÂ+Õ9eÏ)´GÝ}¨Ë×ùŠQ™!çlåžuŒN©ÓXdéůg˜v¾ú毓~Sx¦lh‚K犳PÅ1Éé¿ÚSPrO¦G—މäb£¥“<нxùIs'æBE^ñÙÚÖn¾B7Ì]¼¼/!µàLÙ ƒ£·»F8¢lÌÙÜÒòã§.wŽO‘%&Á¼oLãȯüü~çHÎ¥Ûe¿<$id—ý}õ‹:2oT³È›[¥LéZ‡' Ê«“ò0ÉùXLÕÍA®T:ïìe O_»{¿©û9q4çlÅékw;iì~–°êw÷~–öal<ºìãq™’0Æ*¹r39{üÌåôù1…Ef ­®IüôÕûøÉÒ/Zëè±¥‹·w˜<›–|—ý}côOOQòù‚«>§Ï_q¹}6£%ì]]´Í=ø®ål5þÖCI4ÿMÂè D&ooo—J¥¯0·£££°°pffF£ÑdggCGØ …¢  Àjµ*•ÊcÇŽedd E£ÑÐh4“É.--U(333'OžìééD£Q‰TUU…Åbsssoܸ!•J¡|ƒQVV†F£Ĺsç ”Æ””ƒÁÈd²Ãáèëë+++ûâ‹/ Õ iP‰rƒ P(‘m0677ét: …R*•‡£¸¸*ÚA'ï¯z(i4ƒ‡!ªxöZÿ½l6[]]Åb …{öìÁ`Ð0v ³eË–””襰±±a³ÙΞ=»cÇŽ„„‡ÙŽ?üðà .˜Íf¥R 4 @@•ìP(´°°ÐÚÚzðàÁœœœWí¤Ð­‰$&&fûöíЭ‹‹‹[[[§§§  ‹‹‹‡V^ZZ"‘HEEEÉÉÉ Zavv6!!ƒÏÍÍÕÔÔìß¿F[­V£Ñ(Ün·Ïç3™LÇŽ‹‰‰ …PðŸÏ?pà …R(¯"—––¢Ñh0äp8IIIp8\¥R™Íæ’’,[WW722’˜˜¸sçα±±ÙÙYhç&‰233wìØB¡ @.—çåå½óÎ;ÙÙÙ€Á`>|‰DŠD"—Ëe±Xìv;4É¡°°p×®])))ÐÈ`0àpø–-[222 „ÇïOTÐh4±±±0lttÅÔüþ^~ýHwÏ›{„BùëÀ»×ú›úÙÛ×T¥-©oÂTâîàFUŽ5ÇÚš?â_ô­ˆ4v‡ø–W×\n×êŠ;pyܯ¦®ø|>Çãñx666~0Œþ/m¾Ìp~1e=^z£Ã¶Eç²Ç÷·‚õõÍ(X E‚8#!“sqy=¸ 6ÃàÅTgèZ‚ Ðdÿz(°¾VÝv±ã»%ß¡áh8Ä–Ÿ~Y]Øò°JÁ¬kÅ]l{€¢?Î~Vö‹†òŸO~÷øÊ!‘¶æ±ÙžXµxÿ†ë½ú€þ|l)6÷Ò À _Í“*¢ÌF’Ú)²ÅvºŠ>½¨r„»©üsWnÕ4ãû†èßàš0Å—>¾|‡Æœ¦pU¹®6™:Hß´ f•VPEªH³–ùÁá”Òk·4wÝolÏ¿P1©2H,öA®øÔÕ[ßvôâ:zŸ÷ åž:—ÂòšþzNaÑÓÆÖBì©?¹J=}᣺'Ç1(\W›Ò±ÀÖj2O~:Hþ–ПYzñW‰i;âS‡…ÓÃ|éÞø”}qIyØ’öÞþºÖxNö LÑŸŸ<é$â›N+›Vêš›ºâãR㎤4·õÒ&áH4™'R/-7÷ô%#2âÓàÝu­íè³ç+nÜã jZ:âÓ3>o2»WðÔ1Ì¥Ëõ„Ρ\kknIIéÕë*m=‰šP€M*,ù²¾…0Áa*µèKWj Ä{µÍØkŸuLJäÎð [rñúg_?këÇ05tž®üt„+¥òd{à;c’K*®ÿñIãÓ.bñÇׯD2*ÿ§šXvv;‰4&¢/–ýîÑ£ž±±n½߃­ü¤‹Æ˜Y ÔàûÞ;—„:ýM¯‹3¯^¶þÞÓ.ýòª;ñ†× ¼ ÂQ~ÁЛQ° ~rqÃQ°æõúÜ®§óa]­Æ8ë„ÁpÎ/x‹~ÔpþZÏgߊ ,ÃæÎEg@§6%'Â’’’t:Ý÷§™0™Ì¬¬,µZm·ÛóòòX,V0ÔjµH$R©T †ÔÔTA"‘222Èdraa¡@ ‘H'OžÔh4Z­…BMNNBNY¿ß/—Ë) ‡ÃÁáþ“½3 nê\ïx¦_/Étè„@fBçR&¡ a1ØØ’Û«lËØ–l †“ !—Ц7L{/)À\‚6/ØoZ¬Õ²dËZ,kߥcí»µïÒ釗xr›ÎíL›;7òŸóñÌèÌ™sŽ~ïó>ÏÿëܹsF£ØÕ …BF§Ó‰Dboo¯Á`0`w´kF‰ÔÝÝÝÛÛ ¼Ì,ËçŸ~ãÆ *•Êf³‡††H¤\.‡a˜Ïç:tÈjµªÕêÆÆF€Ñr¹…BŒN§Ó$©ººÚh4&“Éõš%¸Îú‚a8 (•J&“‰F£ØIÀáp›6mB ËËËàL°°eË–üü|àÁœŸŸ¿eË AÐzp¬Ïçc±XÀ¯ì-œ={vÇŽóóó‡ ƒ‚“ÁÐ^^^ÞÖ­[U*•Ùl-Úååå¹¹¹ mº­­ ”Šy<^YYYVVÖøøøz„V«-))A"‘J¥20ŒŠŠŠ·ß~<`Ê6‘HhµZ$‰B¡VVV@ëH[[[NN™L+@pa P Ãðììliié®]»L&“R©Äb±7nÌÎÎÞ¿ÿŽ;Hdnnî™3g€}5ƒyë­·nß¾-•JÝnw<w¹\YYY›7onjj’Édz½~Ïž=(ŠL&Ã?Á0l2™Ðhô¶mÛ²³³ctSSÓ믿ŽB¡h4ÚúÅD£QðH †¼¼¼]»vq8œõÛø—~Žþ·J§áx<¹¿òý÷.^þâêûr³_錱•ÖK_}óû[÷Ìno"s{] ˜‹ê^ IDAT~Å xƒk>¿Ûð„ÃÁp<á‹ÃZG@m \»;tìôÅoï /,,ˤªK—.g2ðA€˜ɼÀèÿßzéüozåJ—µ|÷ÞTkÏ­K·ˆ2GÈ Å2 w(²¬r9}pЊx¼î€×õ{}kÁ`Ðs(ô¼2ýçÁèçÊüp$’éc´s-ø?ÀèL< Ãp8žˆÂ°#‘ÚLÏc ù¿Ûç÷®=?\~¿#à·ü‡}-NÀñX“ˆÛ"kZÚä]“„5 t‹OGo^üê|ý¿R5q·ûñ×ì‘nÒW¥wŽýjâ“_ñoïû׿ö¨¦áT  $ãP¶\®O¯~t•=²#jÏ ÒV™q…gH`äZ‡9¾ &®8U^˜¶¢ïì¹ 5˜ŒNÏÒ²´¢º]Ö0…g$¥Mïá9R¥36É¿ð/ ‹d¦¬ñøßîÎo>y–!R1DÊ| vO2Gbñ³ùì¢\­(\AYCSusËàÄ_*GWaFŸM¡"ù}}}í­mÄYRÅáºq*Qj^}D 761$ wåÈGÞB v£-ÈÔæÂÖog#‘§?<3M$,‰…E•hLKãðÔ¤Ùëa ø¸öN"²È\ª,©ªF× øRžX]ÓÚ5¹È•;]3tFVnn®mE¦$ϳs Q‡Ê\“¿Œ=ÒÉ‘È4']°\ÑÒÂËd««\ÉJ~ÙÁ=…E³ü•qæÒ[…èêc§§9Bdã*tyèêy±Š-‡NýÓ•Á9ñ²%#ÓŸ’ȵÍ-ß=”ªÕžH˜À W4`o}߯µÛí¡Ð³YrYeÍwY,–ä—¢yÜUÖÐ4ÍYR:ÝÏf)‡[ÚïŒé­ÚÂÒÁÊš’*Ì•Nãp1¸VúWc³‰ëêžÎR˜áÈUE ?c¬ÐE¶ú—ï<™[P¸Œ˜>ÞÕÕ%‹Õjõõë×Éd²J¥’J¥T*‡ÃÉd²ÕÕUQRR¢P(2™ ¨A?~¼ºº\†Íf;{ö,ƒÁ€ ˆL&·´´H¥R¿ß?33sìØ1 …¢Õj[ZZètz<—J¥gÏž3N§³¿¿¿··8~€èDàò›z¡?)†!ºzõª\.çóù­­­H$’H$ÎÍÍÕÕÕ½úê« ÃÃÃ,))áñx;wîD¡P …B¡ P¨;wvvv‰ÄóçÏ777ŒŒèõzàVÅb ŸÏ¯¯¯ß°aÃúî@m¯×FS©”B¡@ ÀðÎét¦R)‹Å¢Õj ÅÌÌ Ø·oßüü¼J¥êîîÞ¾}ûþýûûúú¬V+¨ˆ[,¥Ryøðáììl"‘(—ËïÞ½»mÛ6733sâĉ¬¬¬úúz<Ïb±òòòp82™LûöíÛ»w¯B¡ €Ü™d2I¥RwïÞ@ ÀjÍf³Éd2ðT"ˆÁÁAƒX\\üÎ;ïܹsgaa‚ ƒÁ`6›!ª««{óÍ7 Øl6@سgðÒ^YYùì³ÏÐh´\._XX¨ªªÚ°aCee¥ÃáÐëõÙÙÙ¯¼òJuuµP(\×›”AnnîÞ½{Ázrýíþ?­çß¹DÎÀ6‹õÞ½ —þùJ_¿ÒÔx’KZç?^ûö«o¬^_*•Xó{!§Ëêz£‘@4 ­ŒŽ¥Ò®PŒ^ýÃàñ>Mj•þF¿ÐOõÒÅËŸ(TJ€Ñ-Ü툅ãpr£ÃÞÈÂèµµµcôÏÙýýüyŽÑžT\å¶[#AW"êFüáH0 …"ÁPĉx£o4OÁ±x: Ãp"÷XÍ2 á¡N1gTÒW¥ÓÚ7“gˆOW¾T.|4q;ÿfÏ_]øè RÔ_xõƒ,‹'ᨆÓÉT, Ùü_}‡?4@57¶ÏJV¨&w¿@7$¶ßç›Ç%Þ>’ì ÏÌsfF9Ò§\Iyç‰Úã§JšŽüzoa1¦czN4'T—5´3WTbÈAáÊQØ€Gg³Kê&™ÂÕ@Jå‘*1äe±Üî!’·½»· ¼{ä(¶­+·]\ŽʵãxRûÑ·¿û>ëYXBg.¢ŠÑ†F1¸ÖûcOUfëüòJimýÈ Áäñzcñû£c\ ÃåÉ'ÿa`d”'WJôÐ…VÓÖ>Ãf«NË=Ã`–V×α–8ìe¢´0÷ ›Åò _ÖÐDe6Ç…Q…ÅçtV×ìPªËÓá¾ûÎåýî¹çž““³mÛ6‡377 …ŠŠŠÌfs2™är¹eee¹¹¹¹¹¹eee\.7™L¦ÓiÇáp`Ž˜}ž&t8§OŸ†!È:ŽEEQ(ŠjµÚòòò]»vÕÖÖ †P(×?ñx\«ÕÖÖÖ–””¸\.“ÉTWW·mÛ¶;w–——×ÕÕ8p`÷îÝ×®]óûýãããå›’““SUU% axIYYYvvvaaa~~~qqqOO4 F³oß¾úúz‚~”!Ñ`0äååq8èc††9ν{÷Aï{8®®®Þ¾}{MMMEEEYYYVVÖ©S§4ÍÈÈȹsçvìØQXXXPPP^^.‹a•ÐÑÑQèØÞ·o_FFFcc#\O¢7ÞxãØ±cjµž Å0 ¦ï iZ§Ó555Õ××ÏÏÏ“$ yþ-mæç†4ýF{Ö7ÚÚÚÏ_ºöå·­ËqˆÑ}ÝÂÆFÇâQˆÑQKàÉD2 ƒ:0’ ¡”Åý1F_ºtl†¾€MŒþxñ_ÉÿE^»ðч–«R¦e1z)€úq4Eá¡dJc …âÿ££H ÆFG£QX :^Œ$ @ÓÄèÕH0L¥SÀÄ4`EpÈ€dš‰ÇHlxÌ‚É^ùˆÇ%w®LF\Èú€^|¥ë^v×·;†îµ¾¥lÏXáîî»ò/-òÖ5ÓD”¡S´s=ñÙ½s·†;E–†3§ÛG» ±I®¤Ê¶ŒJN^ÿzLi±FоQqãÁÞ©1\rõ«/3ròŠ«êÇgçÇçÞÌ-.êmþ8_ªë—>©‡¦ÕYeïp²E›ÏèCÆô; Ê%Š%´wbº®ùä@$”©º‡ùϧÄü9ù¬\;³ ©i<úèi_ýáw‹«š¾¸õÝñ¿\ìè+ª;Ü9,p†â£³’Âʉ\éGGžwvsKªªÆgDÃSÓùåÕ]c“k¾JŒÊTYåœA±\åòZà BŸWZ=:%‘J5¹Y…ûóJõz›Ti,ä4ËUFoÇŸË+áL/諾±YùöŒìYù³ ]ßÄôÞ‚b¥Éj\])T¹Å¥CÓrEŸßËt Dc‹†¶qñžŠCBƒsJm]rED ÓŽ=ùše—'t F¥¶%/Ù=*:xìLçÀļÎjóD‡&%Ïù“RõÈô|e㱇}³‹g096+¯j:®0­:‰žçSù¥œ9¥I8¯=PÑ ¨ð&© ©*«¸zD¼hZöM‰3‹*{„ utÈ\r/V¹®Þï4ú$á4†’ ˆ_8Fc‚„C8ìü$oп¶A'±hÐ÷Íw]‡ÎôBŒ¾õ„§°$ét¸``1€ È‚À Î0Œ^¯g·°­V«Á`H$n·;Àvèƒ%'Ôàr¹Ø-fÀüü¼B¡€¥•Á`¾¥är¹Ñh”Ëå0/t¤ù|>¸ç¶Tƒ0 <§‰DVWWm6›Û톀™}Nçêêj8ÞØØ@’@§ÓÁÚ+z½†s (Ša›Á€~%ÿ«ÀGÙÙÙi0à:N·Û½´´d6›ý~?ŒÀ1™Lf³$“I—Ë å¸\.6°‡$É »Ý®Õj¡a(Š¢i@ds6£(‡aikز¼¼lµZ¡+z«á§Ói¸ÅÄÇÆÆ†×ëµÙlƒÁjµÂøx¥Ñh´Z­‹Å`0À³zÐòU*•Á`0›Í6› ºÃQ…'aNèQ›˜E’$EQ ÃÁ`0hšf«œ ( _£ËËË‚$“I†a`0üÓÐMo– ¨Äb±¨T*˜y& A[]YY¡JƒÁívÃ{'ÂjµªT*«Õ ù˜þíÙl6³Ù Gå—ZPÃq‚ÂÓ€f|ïÓ§^ùäæÝÇ&wÌ" 7fê Ét,]õù7bqöˆa4L$AF0æ… Ž…­yÙöFá¦_aôß·¼vñê«m匤S(‰¨Æ‚Þh<c1Ap8‹Å`y^A^o4 ’x:˜@:e ùC$ž€`MzËÃ4h,h@b8`ˆ4‘$è¸É¢˜“÷¶ÌL?qXG°ð„Çr—{?§çþ›Â®lõ@©¼3OóôOC_ìÔ FÖRQHŠÐ€./qé«ÎK÷G'–Cz>½ƒ'šà«í£cÜIÑ'w[<P˜W­>?wdðSÍm½íƒ‚¡#'þÃ?ýcfvžÁl›SNœ}:81&Qµþú¨{pV3­qd–4ñfÔËaRëIô‰µYœæ!¹aÁ᜞¯8x´¥³_ª1‹Æ£g.}xý¿2¾Xóû?é×$ÚÕ¯[{T5LʇgU8ÇÛ¦]btfáäÙŸõôñúûΟ?—ù§½ÈØ¥^2OÍ+vå?äYc´1„s§åo¨\ÐëÃ)Sû~|fOQ _ª•»÷ÿöõŒöÎAÑ¢á…\áÜJ4õxp:¿æ˜`Ѳhõ÷O+2ò+÷TõOJú'%ÅõÍü9½Ý=,z÷ÌÃÎnÃêÚhú?o|Ú|ábŸlqjy5£º+^´ÄR¶`|R¶XÕØÜÖÓÿd`âêíVÞŒÚe¸câ¦wÏ·÷LIÇÄÊýŸ}óhL¬äË4ûJrËêÞyïƒÞ QÇ îØ™Ž!¾u#Ø71UP[¿“;<+®?ñî-#bÙ˜tánW÷Ñ —Fd ÊÕµ.þdn-g@¦ÖEÁ€6ªÞ‚ýÊÝN½' ˜1š¡Íü1:‰ áÓëíynu¸0œi‹ ûnK÷á³¼oÉGU·žpñ4œ,ð-KÌÏÃ0 ôfA•dq‡qœÐi ™a˜D"·ÝÁ–Ü´~ õB°`¾ï].Žã[·Já{+ Âs„8ŽG£Q ÃØ/B%Á`² †alýBøo @Ó4I’°>9ì( lWYm †Ýøxé~nÁqÜï÷wttÀ+€`0‰Dhš†ÏÃ0èÚ›YêÀæ‡×a ¼æ<†-‘HÄëõÂÏétšõ§2›‰½$IB+M¥R4Mã8‹ÅH’„É ©ÍJ×EA{óx

ÿ‡}uÒåà¬Þ“]óîi›UZïµqkWTÖÞ¹sçî½5u£ÓâŽç‚ÜÚfžÌ´”³dPe³òÈS±^æAM(x&ÖíoúK—@ÞÏWä7ýóo¶—rŽ?í)®ãI•¦ùT ÜUx¤ePª÷R£‹öÛ]òï™%ý3®`aÛÞ·÷rfu+jÛÆ€P–SÊÙž™³¿¡î£wžÌGK­3¢Uuí³ÒŒ0zƒã2ùc^_qÃÁÎ;Ý“RÖ®÷¥¤fÏÕ;ßW;¿sõ®‚Ú·žºrûñŒÎ1$Öí*¨ýô~ç­ö²æ³µ'/|Óù\d°»’øÓÑñßîÉ,j:$Ô韌W?±«°è÷ûò46µ?W{<æX¬ÏÏâpÚ"‘6ÆÔÀ•X>~À5ú R`€&õ#Œþ…y)H0t$™T˜ŒÞPô‡*†ô®ß{ÔsèLï]S{n>îp®‡Ó)š"h¸áû‚¦­o\6æEdH0ð3K*XǤš¦a2MÓtØw’$¡Z¨ö„õ>Âl›Aøý~°ÉgP3$H`[Fp£ŸeØyH3‰ C†ý:2üÜX__‡U áS`Ÿ#;˜A¤R)¶ý…P¶êkK0ˆ*‡™[Ø‹Y%,Çl5QöA3  ›ý-\51›ÎlYªAðeÍ•¦é­%Qà]„Ãav¡Èz@YSa×u`³ ={_l=<§Ë0 {1´|ˆûlÏqggI’ñxv^‡á°#ÀFe¼0¼pÐØa[v€a˜­C÷77œÿgI§I’¤)DS>çàë™Ù» «÷–4HŒëN<îjA‚OÌ/6ž>½» hhVZ}ä½ê#ggÕ+Ö&YZË®|çõÌÂ’ÃgÇ•vwôɬǮ=hŸÔ›“@<…£üÜgýš5…·E¾Ü¼#·²¸ñtFAôÆi ý:>ºè8ññ½.¡QçeÆ”ëÜ\Xë—­ÕŸ¿Ó+¶¯§€n-¡^ö”ÖÊ/(3[ì#“5M‡Çfå“*KãùÏ;D‰—VÆOé.ÿ†p-)ñSÒÕ¹°ÒpùkžÔ2,1¿ýÁެÊ9Õ<¡êà¹ë"»OêŠ}/нÖ*0†•À“ØÊN\¯;s£W´4mðd7eW˜ÓÙ"Ô°D›_sdçþò?q>“ÈŒqBD{ÔÖšË7¹‹Kª`JlY×8ƒ"½u_Í¡¬ŠC|µÃ<éŠ!@ õîÝ¥‡~ó9Û²+ªN^ž1n¨ÝÉ~±qo域/Xšµ7 f×êÝÆ®u#£rcA㉷Š9cÊå)½½àS¿ÛW¶-¯$§þÏ|£}Þ4Fñn‰ªîüÕQ+[íWùµÐ#5þ˜g$ý8`XŒ¦~áX_GÂáD%h$ž2kŒt‹¼÷[{ßã¾ÿ¥dTµqýÞ#Ѿîô9.X)Ðf³Y,–¥¥%‹Åb6›}>ß³weÍmdç5ÿ)Ékªüç)•W¿L?Lâ‘(n"Gò¬²6n Ü7;Em3v¤! ¸ci n,µ÷ @o÷ËÕ–eÇ5US¶4æ©.ؼ¨¾ÝøP7ÁÏÏÏ)Š*‹ù|¾X,’$™J¥pª]±X, •J…z š¦ñ¯A¤ÓiÜ 1•J1 “ËåðåR©T¹\&âôôT„t:](F"‘ÀS¢(ª\.Ó4],3™ ž9MÓA$‰3r¹“Éd†)—ËXrÿAœœœLOOcå6†ær¹B¡€Ÿ*Nh¡( ·¢Ä?y‚ hšÎçóøÃ½z2™L+$IüãPE\$Iâ(•J¸¨ …B:®V«ÉdË6²Ùl©T"O&“‡‡‡år9“ÉÐ4gEÖüàª8???==m6›ØwHQ>‰§‘Ïçqý”Ëe’$ÏÎÎhšÆA’$ñf³Ùb±X*•R©¾S\u¸°ñ3Á·€ O¥Rø5þI’$AÉd’aŠ¢R©ÔÅÅE&“©ÕjµZ OµÕjáaøê8͆¦é³³3,ª)•J¸ãz¡PÀ_d|õÓÓÓr¹Ì0L*•ºœüŸ­€~ d2Y’,Ðdž)–.ÎÎçæî~ù`rq3ÍÈÑ9.‰wÆ<÷]ó–wKk«t³ÕÔÚm@25]’dNÓ”®í4#Ãðé²€i´gÑŸH$ódñ÷ÒèkïÄ_8þêÖ`‘Ë&¢§³sO®ÞéVGêšé’*h rZGM⻊¢©²,˲¬ª*öʲüÁh£ñj´]Ã2DË V´Œ6€ùV Îy´ ೆ–¶n¡NLÐà]’¹f%ódýÑðÇÿ0óÅϲQw቗Bßå¶vr/姇”?F<9+í”Ûe¢9¶“ÒÓ¤°uƆŒoŸzr„ãÔV<ÿíEuŸc”ðò”ò¾> ÄÈï(íT‚Ý:lžˆ‹ÑÆ“L爃Ð>³ŸïÄÉÎv´¼­ÅH´›‡À>{Ú‚(­>=©xw2ߥ#‡ôëœpÈtýÑÜ_ÿý¿j>?å‡5ï.í‹–"‰úo³Z¼ ±²ó")†kÁx5xT?jÂQà ÅÉÁÑùLCÒ4€ý¦máåñþ_¯#Ó`e›(ºZóq•šÈ6¾|0ó½‘÷ö¶ªÃ£SS‹÷âÂÜââââôôôÂÂÂÜÜœÇãY\\\XXðxúÚÓ?û¢ç^ ÷«ÍÛ‘¾±`Ï£àÍ‘›÷ŸöØ~¼ðéÏgg¿ó|Ü÷ùƒ™µ_{|=÷Ý·FWg·&ƒ=®Pßd¨gbóƤï¦;pÃøå”ï—·oÊß7º18²6ôpõöÃ¥þGK·F–n.ß_ëŸ ÷MFú'Â}㡾‰@¿Ë?0¸=è÷Nx‡§üCSÞ¡‰õÛã+îµ;Sw'ý#ÞOz9ì™|Ñ;ýMÏÄöG;®ÈÐ#ïð£Ï'‚w]‘Ç¡ÞÇ[C“ÏzGÖÇV?ôÞöÝö »6ÇVû/õ,ÿâîøàØjÿÈò©Í/çÂÿý•{xbóŽ+2< …‡Ç#C®­ÁñpÿhðÓ©í‰pßX°o,Ø;èñL„ïÌlÝøzvàáìðˆçË©¥ÏGÝ…Û~³ —KÑèJѾ_@Бe¶^«²ìËÝ]îØÐd™oÝ{<÷ÑÍàÇŸ¿ ”¦ý[ VÃÃKG¦ApÕ#…qõß –cbA*Þîü½=¬eüSÜõ5þ$a~~>•JÏó—H.…¹Ò[\ Ó¯qØîb¸´´r³ÿÓÏM}_ÝRMœÔi4u™ü5¾Æ»ø#«Ñ'9®)‚Æë¦¬ðªXW8Q–pL¦Ñ8©Ç4ÍŽFÛïÐh¬èûM^™`dÒtªºl Åu †ºh™ªi«mE º@dž®Àé€%‚Áƒ.weY×t€LKŽ×u²yÊ,9(p@µ€lB¡hhÈ–CòV·) ÑP"ä9 Y ETº”`((Óè¬M+@ðèÍ!:„èdD+-›9åy‡PQ‚¢ ”yÞÉ‹@ÉàQ†sRœâì¤àd”•€Òà!Ý4s¬UQ¡ÙªìP¬ÆêÐÐ ÓP2M½¬C²iW;uY2º *èDv.$”–€€ È£BËÊs6%CVBg¬™• ÃCšƒ )¥yÈ• Ã99 åÈI(ÛiÎ E“VQ®ÕÉq]‚G).8ã!ÉCFJ¢¡“uµ$š´ˆÒMó¢‰r¬™e,kä83Ç™$o‘¼•ìg2m(kpR‹ 4L8-K´ Ù–™c­ëäX‹`íLË X;ËYÎÉðÁ£¬èd”æ­4ï‚Uí@Iìy½Ì«ÉBÙèX¶ÖÖ¯Ê9ÞgÝ–$U¤v;]¤9Y“•¶PmIõ–Ä5ï.ü[OèÆ½½@”YÙ¬ÔÄ®n׫ omL (J±XÄî=I’&ÖÙl®´p»” ʲŒÍX?H‡…k¼‡Àén·;™L"„pžw:Æ™k¸sûe#øðÛ|\ãý˜¦muº8©Ã㙿Ñ7|ß5ÿ}i4«YWW£=‹þÃËLš¼¦Ñ×xD¤e^ý?m4ב?xmü.–L£óFoi´6AÇê€ê@dzݱtÝéè`oBôè"Ôµè8еmÛlCW„¶ŒtÇq 6€Ðà8d,,l,ì.XX˜&86ž…i#ÃrlËÛÓ¶@4@肌@lhµA´A8 8 ‰@@Pl-PPT䀄Þ€ "`‹7ߌa»ÀµA±¡À·A2ßÜ¿ê@@CP“LÙÅ €h´XÎA ‹@· @8Ä«×}{ˆ€xå$Še¶ÁnH,‚–€   [£ƒ:šÓU‰'€ÿ¤: ™ tA4@±qŒ¨h4-Í‘Lh]ÐTô·OIE (€è !@Pd€wdÚ¸`Ú¦…ÞXòmç¹ðÙ;·ç¨ª=ûÌ Vù2U>ŒUZey+´¦ ÄñBA ” Áx-=ŒsD,dô rñrFä.A/œ†@L8BH‚réN'éîôý²{_Ö}­ï<¬ã9çèh!LjUjwWõ^kwïìúîßþý~ß‹2úç45ÿµ1È'“Ì÷ žgo/ô„?ß{ ;œÈ§GÞÞ°õ©—,ߨöe[ü½=)'Ïþþ¥——.]‰Dl_0Û@ µµuÍš5Éd²¿¿ÿÍ7ß µ4œ)Ïûú¢=áhªs•ÇUÂA—zEO(Gˆ@R–Àp© ´‘ÐRCàP¢ž Íx0.´Ma$ ¤26…€iðQ­Ÿƒi|ïF-¡¸/ˆí¡0Ú‰Í" 7v¶ïƒ¡Ð?®2|‰ö q=j¿öbÑŒ+¥$LãÃø€¸JÂ0n‡TÁ×ð5èWÂPŒÞøf4ïܾt¥€JÊI %ùþ}F1`ªLUÀ!)àÅýÉF÷¯Æ€+®G7ˆÐ àÚ(£5”‚VÐJBIè±A oÞT·?•õ"}C“'ÞóÀtvvŽuÅÒZŸŸß¶mÛàà mxüÅ_<ôÐC·ÝvÛ„ î¼óÎ}ûöÙžÇöžêr¯·ÂÕ‰1BYN¦jj¶.^¶rý·ÿŒÃhΤýJ‰a…¿Ë%e4Q¬Dùù¨“wáå}Vr ^)”®øÃKÈèÑ6ãd´­2¤7 D£lP 0%Ål*6Œ ¶œ<µm÷î÷·nÙõéÎÖÎÆh»Ê&A9” ì¼ €¯•¤Êò0%Ȳ’„rF‰ã¢ÚRÆ}!=eÊR;\:\øJ @+ͤ¢Z ¥¥Œ Æ•Ì÷4'P*€öÊa$«$µ.¥äœ*%ŒæJR­˜VÔhf 1†ãkíií„ó²ë|R–P  Z—‚@~@}ßçŒ0ÏaÑ"Æ+€•(SæîPP¬ #!(„„ÒÐà”q) ´V ŠB3sq(M•¦R3. ‘‘UŒ(F$¥4eð¨ps§àŒ p¹¤ ‚‚E=Έš*ø\qN5' Z@ (nÕœØ #(ŒT,ăŠQpéKHHI”¢Z0-˜VZ‚Qå{ŒBIhÆ„RBHHF=Í ´à #a® Í=ÏöŽ$GŠ)»$ÖWåBöÍÛŸX´wùƶONDÞÙ¶;“z»#“'Þ3iÒ¤óçÏB‚ Bh­[ZZž|òÉ®®.GÍf³žç544<üðÃMMMvª\.×ÓÓ‡ûúú´Öc]/ëñWøµ „ oݺ5‘HŒŒŒìß¿ÿí·ß>|øpCC n½õÖ^xÁf©qM»+Tøe½Ù7€A!—¯«Û³âÍ·~F‰aèHƵÑh[bØÚz®RbXáGùi%†Érîêhx÷¿J ÿF4ši÷eÒx E,L&î;}øOõ»vÕ|ñå§Ç޽ÿÓ=>X·æË–¯X°í£•M_±¨q]M9gZPc .´K ‰B‰#d K ‡û¢}CÉŒã0&¡4¨TBq nÀ ˜4”K"WJ’õ#¨ ^1› ÊŒÌ$†¹ïÀ0˜ÀÐXÊñŒ$69%ð'Ÿ ÊE(¶ÑgåAyÐ>à¾QL0.e‚ %5L®£xàBqH¢ÝþüñãÇí# ëQr¹WZáªeô¥ ¤ŠnÙR»xÙÊ·6×üßK ÛÚÎWJ +ü(×¼ºbio_Û_¾3ïÎøpȴ㆓l$÷½‹aÚ+8nÙZ’Z›à¡µ¾Rd´a ”r ”¥*åKF2€Ûê^£FýÀmªƒ6 ,ùb9d».;ôçí;wmê½pòÜw-ÇîØºùø‘ýç;ÃÝ_öîúןxwÅóÛÞ]Ý~¬ÁÍd%Z Ä- á*ãx,¡òiùêÙçžœöo÷=Yýô¼ùUÝÝ=®p}/­% …`vÃ> ‰Ïûh*™t{[ëãóçÆ‡cCƒÏ?÷Ì™Ž6@3êÒJ=Á4ñƒááÁgž®þîL;%¾õÑJHÁ™I'”¤€,;y£9 Ê6òÍ9:*‹Æ%9£¤°±XßMŽ ÒõŠ„øvµ€.rv®äH¼êñyÉd’sªÕ¨ÏŸç:€¶kÐZJÉ¥´÷ š_)¡µô¼òèo¯Ç?:ÆŸ.?è:÷÷ÆßÞ­ÛçøY.µ·9i[ßwò¹x&3˜Iû\Dd†F´O3#±õïíXøRý«ï¶~Ù߸«>‘r†£#Ó¦ÞwÇw8p »»;F"‘¾¾¾;vÜÿý‰D"‰TUUÕ××ApæÌ™{ï½·¹¹¹££ãñÇŸ?þ矋Ś››W¯^ÝÛÛkmØ!ãÞ.ë×Qáƒ1V,wîÜÙ××רØx×]wÝ|óÍsçÎ>}ú7ÞøÈ#=zÔ&ùX?íË½Þ W'£‚V*Û©cÛ¶‹—­ÜTS×›ôúrü»˜³lÝ›jê†ÒY@[3ðÇ- îó ìÆÌÀ3.ïO9c}£k·ÒÖv~¼‹áÅéÌØß ÿo¹fÙë¯ö…û;Nž‹F÷äH–•){i‘̃»‚;å¼[Ì¥B©X.—m•aårÙqœ+É \„rfLž‘H.4ïbࢋ¡`@¡ ×ðË~¼äüªvϾÍ{ëßïë=qîLÃgû6=´å‹}ë>|çùm›ŸkØóòÞÍ¿öô¿®úýì7þca×ÉãPF3hà (—ç%€|Ûrô×—þó¡\.wîܹo¾ùfíÚµÍÍͶÑýÏ<{öl<ïèèH$„‘H$ cš››gÏž‡{zz|ðÁ#GŽh­c½½½œsë9744d ¡P¨®®.‹)¥(¥ñx¼­­íÔ©Sñx»˜%“É\.'¥L&“¶¨¿¿¿««+ŸÏK)Çœœãñxoooggg?›kM …H$‡3™Œ]óÑ£GC¡ÐW_}ÀóýÙgŸmmm5Æ …¾¾¾3f„Ãa¥Ô˜Œîíí}ì±ÇlZªã8kÖ¬9räÈ‚ î¹çž×^{-¦Ó骪ªë¯¿~âĉK–,‰F£±Xì™gž™:uê½÷Þ»jÕªp8AooïâÅ‹;::Nœ8±bÅ kÃ;wîÜiÓ¦-Y²$‘HPJ­†^¹r¥­ÁŸ5kV©T"„xž×ÑѱjÕªC‡ÍŸ?¿ººú›o¾ÐÓÓ³páÂ믿¾ººÚ¶t®ªªš2eÊìÙ³Oœ8a¯AÑhô­·Þjhhxýõ×—.]šJ¥ …ÂXA…_ƒB*E=WÖÅÐxz0&|fã‡uO,Úûʺo¿l‹oªÛ?’.GGf…fO™2¥¦¦fÿþýMMMMMMµµµ>ø`4íîîž9sæ©S§ Í™3çÛo¿-‹ííí6l¨®®ž6mÚ¼yó2™ÌØÓÏ11mƒÓ®„ŽãìÚµ+‹544ÜrË-¡PÈuÝÇ{l„ wÝuWgg'[~¹×[áêä§ÊèÁL6íž’T1Ï/Y3p*UEFWøÇ•Ñ'ÏÉèž“Ñi‘*€»ÂÊh[bh¨¬ƒRÊ>¥½Rd´¶ILH $y´Í êA_*Í(´§P€Ng†?;¸c$ÑY*ô~²{Ý¢§§V?ró…Ö÷Þÿhݤ?m¿/Ö4«áýٸ蟎m¹ú…Û“ç÷Cÿ{WöÜĹgù2yK ©âa^ŠÜa ¶‰l¼ïÆ&6Ë ˜pà†J('sS0!³x0Kˆq06;`lY^$á%Þ$YKkß÷n©¥–zÿÍîÔ$÷á2dRÒC»ÊªÒÒêïôùÎï7°!Y6žˆp  ™4Õíué‚LŒóz½ÑhTÅX,ÖÜÜLD,ã8®½½½¼¼)mmmr¹œa ÃŠŠŠ-“É*++Íf³Á`(..–H$,ËšÍfÄž‹‹‹×¯____ßÝÝaX]]ÝŠ+ÒÓÓõz}ooï–-[6nܸqãÆÚÚÚöövp:yyyßÿý•+W>ûì3›Ív÷îÝœœœÌÌÌ;wöõõQÅq\ggçÞ½{³²²Ö®][PPÐßßã8ÈåòŒŒŒsçÎmÛ¶­´´ôÌ™3f~~~ÇŽ+V¬8tèÐøø8I’===›6mÊÏÏ¿råÊôô4Aóóó[·nýòË/kkk÷íÛçóù*ŸÄ‹„A€Àã±ØŒVãôEHðg‚^×™ïnÔ¸uôkEÿŒë»Ž»ni3»2֭߸qãìììÒRAQÔÀÀ@NNŽÍf3YYYR©4‘H˜L¦ÊÊÊ`0¢(ŽŽŽ–””ß]Ñ IDAT¤¤¤ÔÕÕݹs'0 ƒ¢9àÙöÅøI$ñb!B8¾råŠÙl–Édééé©©©³³³ssseeeË—/OKKÓjµ(6ñ~±I¼²x>5:Êsq.I5:‰çÀSoôÏŠyä>qþ‘ÆO-y£Æ: o´/†‡ÉJÚ'IÑ8ŽÇb±—…Fó x`Xžó¬1àuÅ"EÒ4§ãÏÑMÒ4™` 2!ð¢@Ñ–x<†î®v§mÞn˜œ¾ÝÝ~êÛÏ·ÝiÝ=7xòÖ™Í#7rnþ{ûe?ÿ×þ¯Ó¿Øýo!õ ÍÀ‡XŠ‘cã Žb€‚_Ŧã¬!A>5ŒŠ¢Èq†ah×[­VõÕWr¹ÜãñôôôïÝ»W­VkµÚüü| Ã8Ž]R£sssÇÆÆxžÇ0,%%å“O>‘H$J¥²³³³¡¡A£Ñ åää´µµÙl6“ÉtðàÁþþ~Çc0îÝ»wèÐ!µZmµZW¯^‘‘qôèQ…B±°°ÐØØ8<<¬T*ïß¿ÿÅ_h4­V»gÏžîîî@  Ñh¦¦¦ª««- ˲w0hözðhœÇ©#èu»øÃ®ºŽŸz0í8s½Ãêš0ÛÚ”ÔU«VÍÍÍ@$AßÎØØXqq±N§Óh4ÅÅÅÃÃÃ`±XÖ­[÷èÑ#»ÝÞÜÜÜÔÔÔßßaX__ß¾}ûæææÐþÃ0K5‡Éï÷•ÇqápøúõëV«udd¤¶¶6##ã§Ÿ~ÈÈÈxýõ׳²²T*U"‘HÎ<$ñû៥ÑF·'9b˜ÄÿOG x§ñSK#†FãaЈ!R£‘©#‰ ‰DxžYh4òFÓ Çé¸Îãt„Šþ¶MÇ|„-Æ{¸§õyD0dèí¹fPM,*§Röä~Û½Ö¦ñŽû¦¾QÿôáýÿüËýþåÑǯ9oÊ¿Ýúݵ¤®¯Hûèå AHhf&j ¶DÜvxÇQ/èt:Ä6ÕøðáÃyyy o¼ñƆ Ün·Éd*))Ad2™¬¼¼…'””” F£ÑÅÅÅ={ö Ê‹Å$IUU•ÃáÀ0¬¶¶V"‘à8n·ÛÓÒÒÊÊÊÊËËËËËß}÷Ý‚‚¯×»°°––&“ÉÐsM&SYYÙââ"„Ãaô*Š 6fgg¿ÿþûÕÕÕUUUEI$’òòr”«Õj³²²233F£N§+,,\\\Äq|tt´¨¨H¯×³,«Óé6lØ››«×ë•Je}}½Õj€x<ž\kˆÀÆb±0AÒt˜e"qF§7MHe^‹=äs{©ã¯wÒ2yoÂzæz‡ÛGúÜ¡šêÚúúz›Í†– Žã(ŠnllDÁƒƒƒǹ\®ÚÚÚúúú¥äà†††ââb´³áñxÍ0Ì’¯#)K¿2 i: ]¾|Ùjµ’$)—Ë+**rrr²³³W­ZµsçÎôŸ8Žûýþ?öÕ&ñªâùÔh’c)6ž1LâùðÞè_ŽþÚíÂáp( …B$I¢À»—nÄ0A³,ÎÒ<€ lâ$u0 ð@'‚æC"D9w94w;¯êæVådIJ·Nzf~´ËÎuÉ¿w6süòFë­ º «•ß›¸Töí¡ ÛÔÀºŽ`…8 4\˜"‘ x{·×<î½' ÜR;îøøxKK †aEMNN644ô÷÷»\®–––ÔÔÔââbƒÁ`4KJJ~™Ôa˜F£ÉËË¥R¹4ƒ2™lË–-n·{aaa©Ãb±ÔÔÔ ,..ªÕj•J544D„Åb©««BÚ’Ýn¯¨¨@Ó$I¢»ÝžŸŸßÚÚêv»U*•R©”J¥¨˜c||¼  àñãÇ<ÏÆÜÜÜôôtF£Ó銊Š H$’ÒÒR¤nêõú·ß~{óæÍ†aVXX¨R©§H’LÒ¬ (‚ˆ8AQþX4ÁC8B9 VޤünÇ7­·v}Ôuìô“ž'–o¾ÿ‘ ¹DŒ³šm¨f% ¢2g IÝ,€N§[Ôh4 ÈR¥×ë‘b ÏV´É€ž˜ŒéxÅàõz[[[].úsvvÖëõÆÙÙY—Ë…y‰’ ’Hâ÷Às"otrÄ0‰çÀ²YÔj¦d³mWü¯2p_$:­ñzñ§eà¡(3Ñ“$‰ã8ŽãˆF£З†F³â’MpŒ„x†z½ôs@Ý+ NN„x‚#£ˆ¢`ШîÜøï{7/Ûæ‡Ò1Bê/µ7¯–uäÊ®§ng讯ëk^yéð[ÚÄÝr€P8êg"‚€Z¯Ã ²á©½;x\^´Ííóù$ j(@Z†¹ÝîÜÜÜ7ß|³²²Y“³³³Fc,+--u8sssˆ³,ër¹²³³‘ÇZ¹\¾yóf«ÕªT*+++Q¨‚R©,++S«ÕÈI299ùùçŸ[­VN—èx,s:¨ó0 Û¿?Žã:nûöí‰X–õz½ÍÍÍ6› >|¸fÍšªª*›Í¦ÑhÒÓÓÓÒÒ4Á`ÈÉÉA§ÄÈÈHEE…ÅbáyÞjµ®[·nåÊ•&“Éívoݺ™C–’ª“—§ âá0‰‡ÜÁ`Ë¥‹Z£%F1ÀCÐáö¹ìç.þð×»?»0ß­0Ÿ¿Ýms†@ûí¾n$*£ƒ%füËŽ•$þT`YÖn····#¿V<_ºEM”r'Š¢ è ‰$^8€çEžf@vÇÕ«×>=õ÷K7»4ΈÎOËuî§[ÿëüUggY:ˆ,>¿û£$%‘`„ QT”bØ0F‰¹É³m·wîoº|µS¡˜]TëOœøTEt¡ƒgTbrúscÙÇÇhtÚ_ÓhZdƒ±ø¬>àC4‹‡ð ‰ÝÝÝÝÕÕUUUõÚk¯­_¿>ëtºÒÒR¤>yò¤¨¨H¥R¹Ýîüü|0‡ÌÓ‹½”æaµZ-K^^^cc£T*µÛíMMMwïÞU©Tr¹üôéÓÇ×ëõ‡£¦¦F*•¢I “ÉtêÔ©Û·oK¥ÒóçÏŸ~üxÿþýMMM/^ܱcÇæççÕjõ‘#GT*MÓ2™ìÀ“““N§s÷îÝ2™Œ¦ééééÇ †h4‡gff4MUUUvvöéÓ§ÕjµL&Û³gOIIIeeå©S§P8†Ífûàƒ†††Ð‡F¥Ré‡~XVVvâÄ ©TŠ®cccMMM555›6mÚµk×ÈÈÃ0¡PèñãÇeeeÿÃÞµ=WUåiÿ õÕòÁË÷Öš)쪶ʖB¬š©Ò–QTlÛ¦¼0*CðRØ–Ó…(† B¤i)ZipÚÖj ƒ$9IÈINNNrnûìû}ïuÿÍì>‚öÌTA«p¾‡ÔI²÷^k_Ï·¾ý­ï×ßßÿ裮_¿þèÑ£ø†wjjê¾ûîëêêšœœ$„œ9sfÓ¦M<òȆ öïß_,àüùóÛ·o/—˨q¢£“‰v…¡@dYG~š.YfLx¦Ír5÷B«YÛñÞÀ#¿ýàµ=“§&Íý:;_kTM«e›¦Ùjµ Ãh6›F£^¯×j5Ó4 Ã0 ò,˲ð¿­®WT*•R©ÔßßîÜ9œÂa†ã8†a˜¦iÛ¶ã8–eµZ-Ã0~èÎvpÍÂ0LËr¬¦a­ùâÜððá·ÞîÞ7üÇb3.¹|tÁÂbàM/ÀbàK–Ý c/Ï¢<Ži4ÒNx±áëÁÎÞ6üæåýG¿þº0?·Ø¡Ñ\Ž^êzqv®øIšF§^v FK´¦Ñ„k“F%¥ä QrI™0–†GÀB§2þÉ;~ýÐÏìÜ07²¯1yÌœ>•Õ' mKišä”p€”ËÂÒòLã \./..–Ëå±±1¥¾+Ç‚‚BˆÑÑÑ©©)Çq*•ŠeYH+ …Þ½¾ïcç|vvM‡Œ1|¯zqG8×>éB¡pæÌ™Z­†$u~~¾Ñh”Ëåb±DZ”’1f¶ž$ š+°"6DÁÔ]"ñܹsØPÇ_}õÕÃ?¼°°ðÍ7ßT*4XcŸ«Õj¡Pð<"¥¯V« ¨7sÎ !…B›ŒÄêÞ]a(ˆ]×6š)ç€LLÎüíô_Í¥Zà˜Ý=‡{îÃmÝç‡?¿ð›;ööõì90Ð7888844tèС¡¡¡þþþÞÞÞ½{÷öõõõ÷÷ :tè`×%ðáÿÌ3ÏìÞ½{xx¸§§/›ƒâµÑßßß¹N:¸z8tèƒááÃúöõïïÝ×Õõʦ¶¼ùnÏl#BýÊ»½;öô7\_D>ÒhŸä1IâÄGSGÎ…“й¦9Þºu´•‘Âo¨Î÷ÔuŽ¿'uh¹Ñ™ N’Ï;Nô-í‡z£uàçyyž_£4š©©<ÀeÒâ2 ‚Ó<ÊXB £%ùéÔ,˰©$*à8¡¤–)Ÿ‚#À¥Ê¢ÊRdRQ$µxgj‰ƒüœç9–®!DÇœsJ©çyÈ}ßÇŰ<ŠRJáº.~†ËÂz³,óY–I)±Æ{{.º,°c:­ ÇÁøÏóšÍ¦eY˜Q +±}”R¤ãyž#)Ç'ö' ÃöT;ýH’R†aˆMÿ¿Noÿ äaºQjÉ29@Ó°Í¥†Lr³QÅÜèmÝçOŒÕz>:Q^2ã oÖF£Ñl6QD4MÓZÊ?Žã8ŽƒŠµÝÁõ ˲*• VSBmÅqœóP­V«Õ*^Eõz ”þÐýíàÚD³ÙrÏ5-§e.Ì—Ž9úÖÛÝ»ûÏÔ‚y‡¡ýûÝj¶Ë9Åò+ (IhÚžÔáåêSÇÈÈDqváß¿}ãbRÇ%4Ú¢YÊs;NÇçT£‰h†¡ëºAà||L“µ¯EÍ”`ŠGJø |žJŸŠXHœ €)‘e±gK.°!AAP Ñ 6¡6c( qL‚Š,¶ŽŒ¹¦.U`òcµR ˰#ð~n7°âÖ0$˲$Ip¢n ´ïûaFQÔl6íoWèÅÇD£ÑPJ¡¢Œ&ø,Ëø"ÇÕátØJ¥¢”ŠãX¡9º8KÓTq4M¥G¸¸ÙömvpÅ  ñ¼Ðu– ãÈŸNÌWª9@Uî…‘gw÷^¿éð‹;Î~açàáùr“¤o0¼r(¥—ŸtNëÏ\ŸB´Z­þþþ‰‰ Æ^z¬¯¼Š~èÎvpÍ‚s ¤b¼¶\8¸yË«¯¼µkºêÏ;ìë²ýÊ»½ÿÕ½¿j9Œ‘KʯD±§s£½\•Z¡¦Ñ½}GFF&f/”0©ãÝùžºÎqÃÖ×þSÓè}ú=M£‰b^F eßOÿ^~ÅJ},¿‚1B”¯Uo4S (` 82W È2žd§H A¼¸E Š0 $ 8NWÀ„¢dJ RŹʽ°äF-W4"épH$äyî³Ô™BæƒÌ€¥"N—« Î.Ö¡À’œsä»ú§”“+ ÙlbŒs$»HCq<)íÉ H[åʤ=Ȳ,Ïs)%ÒY¥T«ÕÂ…Û•`Üš¦M(k&­—Á>Ƚ·ƒ„÷Y¾ö[ã8Þu]\Ð ì²s˜ÕÁƒ«^<×c2FYq|Z&yèZ»ö?òÛ^Þ9zìÌâž#/.[4“c£çfggmÛÖ_išº®[©TÐα´´Ôh4ð”uŠ<_ÏX^^îééÁ!¼¯}ß™œœ4MS¿ïÜ×\%p. ¤ÎÞòÊïí¾P˾<¿ì¿Þ}`ÇžþšíJÉ“,®˜–™d(ªX’˜M„4#:[w/T=¤Ñ?:wnz¡´ô4ºc>¼ÎqË[ÿcv®ˆwÿö›î—ßûtºYŒIÅZj R[¶—XÅPÏ,D¡‹ðýTh´" „ YÎA„,_öŒ€ç#HL†RFAP@ÍIšJ’Çi’Ñ4Îò<·Ãq)µ°rð¼ó€È@’$æY ’¦®E“Dn’EH -æ€>@©/Ë2Ô˜ÇA>tS:ôåaRhÁœ)í96MÍÓȃµ‰»ïûšÅêlW)áê8)Õëv÷¶~ŽÔëul[AÁ[·ŽÇqÚW”Rj?·;‘„µÿÌó\—¾C#¸ž|é8„afox4ô¯i¬”ò}U|Ü 6`ÛgŠàqÐb9v? m î”뺸–>eØnØgì°axLp0ƒËh7<:mpõ†¤¡ °,³Q¯[Òè…òò矜nUªžeìÜ}ð±ç>|yçèÏVÞ>j:iy~ù‰ O>ùä“ÇŽÃÃK)]ZZ:uêÔÆ———Ëåòo¼1==Í9Çá|›'iérΣ(³ ¥Dû¾>Y:žßošp×u;ÅôPJÕëõÞÞ^×uqª ;vìÞ{ïÅ*†õz/è¼µèàªá’ò+}}Ï¿´mçûƒ3µ`Φ#%³kçûèfŒø¡W1­FáÃ0rýÀŽ¢ !ÔNx©Î7£·{†ýõ‹?›Ò¦Ž¶æ:åW:€^ÞöÒ\i¾½Š!N1d ì8½°·<à‰ˆ-ÛOC4u$I‚ôùt†RÊŸ †œ€$O9ˆ¥U×xš— ” HJ(%$À„0Æ¥¡”1@Š‚’4!faþÅg}÷öv¿ûñLJ¿;e‹’$â8$Y @pàDЄå(fÛvžñ0äqÌ]'Dy –žJˆöbXqnà5“hç͆!FmhÃ1Ú¦QÂ%[­n ™hEAà)¥H4qÚŸ>ZA ÕÆUÐò 7ÖvÆ bs(-c•rì˜vlk‹ˆÖ¤‘Sb2 þKï¬~¬©m{mðöV{[8lÀ½“R"ÿ¶,«]'HÓÇ hGÑ/šñ˜ãÐ%Š"lB·H) ÃP;m°3¸#„8Žñ¤ Ž®yvžçzÂ97MÿˆcN}äJâ vïªÓD$ŠÉý4^,ŽŸ¤ÔoÚ,L¾F¯]óÀ­·ÞºzõêF£Á9¯Õj7n¼é¦›î¼óN „9uê֛ij<==](ŠÅ"&—ãƒB±¼¼¼´´T¯×qІ¡cÁ8šR©„gJ©‰‰‰¹¹¹éééÉÉI,•ÚùºúÑ‚1–$ÉÐÐP¡P@ïÖñãÇï¾ûî;î¸ãæ›o^»víÂÂÂwzë;èà âûhôl#ZðÄØ¢óê®}:©#ŒƒeÛiÅ)z£Û“::4ºƒÿ;¾—Fç’ša<] XÌc˲è’Ühü€Ôä§B£a Ér*dù’ÓòX–dJJ)AHR )”dJ2àW1W.¦À•Ü ¼êüLáèð‘½ïôž55…šnÇ333ZLE?4¬h½(oŽŽ" Er¯7†!.†cŒÒÃn£$ŒKjmxll e{Ý7!Äââ¢Ñy»ÿ2I’b±¨µp¤ïI’hiWÄãŒ^Üv^+séâ8ÆÃÞÎzµNŸç9cÌu]¥T£Ñ׳*qSZÖªy¸@»ýª@oš¡ë4lûÄ_þ{a¹ž€Š@£o¹å–{î¹çôéÓ ¾~ýúÛn»mÕªU…B¡\.oÞ¼ùìÙ³A˜¦9>>¾iÓ¦‡zèxþùç?ûì3lvdddË–->øàêÕ«±Ä=ÉÅÅųgÏnÞ¼ùþûïê©§Nœ8á-pæÌ™gŸ}vݺuëÖ­{çw°¾ö~„À×5CCCsssP*•Ö¬Ys×]wÝ~ûí7ÞxãÚµk+•оƒ:dºƒ«„îàŸï5uPàV”Ì,E-ï"F5:Jâ4M‘='IâyžmÛišþTh4P òœ2P>%KNËc$É´7Z*¥V¼Ñ ˆJ„>‡€™ÓÚÌô_þåø‰c‡—f‹AÃþôÃãýÝ{ÿ|ôØÌèh³8þùÑ?¼ûêÛŸÿ÷ûvžþ$¨×Pà$M]€„2 TŒOþí¿ø—_þòç¿úÕCkÖ¬YµjÕã?~òäI즖„uYlt€öWN컼†\š¦øþ])5;;ÛÕÕ…¥ÅÂ0ÄuQTÆ·ðzŠN¬T*Û¶mÛ¾}{©Tº„&¢Ä¶íR©ôôÓO?ñÄ“““õz}×®]†aA€“ }ß×îJiãØ;· &ò=Ÿ­:5û°û<S35g^æÅÒñˆ"wH€@ˆ¹(‚*õ©i IDAT\¼{ą̀;Îå8êŒ;ƒ ŠÇË( "H0„H¸äÚºs%BÈ=étú¿'kM•çeoå!¤’T'ݾýý~¿ŸÅrâÄ HL¾êna¼W€¼^ï«ÞüJŠÃ?[[[á´ð`0¡¥RyàÀµZív»á$"Î{Àj4ø˜R{<ø_Þ»çø‡…®ý7a`¿O\‡‰_Äë2áš|ÛÉÉÉÚÚZƒÁ›Âãè ø•SÀm~ itÈãqÚmV§Sc4x‚‘çšÃh¥á×Ùè===ýêÕ«ÇŽ‰DG޹ÿþ;wJKKáLʼ¼¼ÑÑQ€Z­®««XJ¥ƒƒƒçÏŸ×ëõR©”Ëåö÷÷#²¼¼ø€ÅbÁ“4Ñg7¡7ª„NèíëµP,1D—‚¿)1Œ_Ùaô|7l4±(DÈN‚Àœ+"âu‹Q/oq¥@((üAA¬ÙìÚþ§]Âá{ éðîïþó/»×ï°kûåã×þë»Çë$ÈAF)@ (çÔœ¢Ý*(SRAvîÜ  ‚ ^¼xQWWG§Ó«««ã®†» Û¶mËËË;sæŒÑh„˜‡Õj=uêTzzú¶mÛš››a< §OŸ6›Íóóó/^T("‘¨¥¥¥´´Ôív›Íæ/¾ø‚N§›Íf‚ 9|øpkk+܃ƒ¡ªªjçΙ™™\.W«Õâ8¾oß¾uëÖmܸ±©©ittô»ï¾Ã0 ¶ÑëõÕÕÕf³¢†Á``~-¤‹ƒ6›­µµ5++‹N§WVVú|¾••[,.—ÛÐР×ë¡Âq¼¨¨ò¸$IÎÌÌ?~\$À>ÍÍÍmmm£££\.—ÍfÛív—Ë533·¶²²Òb±8N¹\¾wïÞ7îÙ³Æçð`>{ö,Ç;yò$Z<Ï®]»L&S$A²Édª¨¨HIIÉËË;|ø°Ñh$IðsçÎ-,,Èd2.—[UUç’<99‰aØŽ;ŒF£Ùl.++ûä“O8΋/$I}}½L& ƒ|>¿¤¤D  (ÚÜÜÌ`0ø|¾ÕjåñxõõõãããðÍSSSëëëE"‘Ïç›››«ªª’H$ÏŸ??wîœX,ÀQçSSS‚à8Þßß_WWg0´ZíæÍ›i4Z[[›D"áóùû÷ï_\\´ÛííííEEE|>ºù{÷î!¢V«ÅbñÑ£Gá÷Ããñ233KJJ”Jå?ë”yýÑÂ^oÀ³†›Í÷Ÿö*Q,&A0p®¾ÎFgel£Ñh½½½>¬©©‘Édcccl6ANÇd2…B!Dqà®Ä0L§Ó ‚ÊÊÊÙÙY€T*]XXP«Õ†‰ÅↆëóùüíÛ· …BN7??Ïãñ¬‹Åq\­V/...,,ŒŽŽîÝ»÷UŠ&¡ß¡–––ºººàå7€$I“É”ššúþûï¾ u$”ÐRêHèíëå0ðÙr˜FñOµì·‡üA2ì‰DÕFŸË<¯ŹâqA6¶éðz½°›¬ÇzWl4L£¡v’¤ÊfCWV´‡Îî0Z&ËË›ÞæÐÙ:»Íl7úv …ÂÊÊÊôÔ#*Á`pçΙ™™åååSSS …‚ã8¥RYVVæõz½^ï®]»222¿ªTª²²2}3™LEEE>Ÿ7õ­·ÞÊËË+)))+++,,\YYÁe>Œ7E=zô(M…ÓM4:.###??¿ººº¶¶v÷îÝ•••f³Ùh4ÖÔÔˆD"äÁ*•êÀ‡$É™™™Ã‡K¥R±X\PP€cޏªêêj£Ñ˜L&gffªªª´Z-ZÉÿ¦Ê_?ûŠF£á' BÑ„Åêx:ùç€Óãs;¾»~·î󻯫F,ß >py#6³ópц†N'‹;::VWW†Q©TxÏÖÚÚŠ’3Èd²cÇŽ•””TVV¶··ÏÌÌàñXüå—_¢›åäÉ“SSSx¬0Ç£¹¹¹¸¸øÌ™3ÃÃÃxkçySSSvvvyyyGG‡P(Ä ú7{|¶ð7 ‰ôööâ§¿dl6[qqñ»ï¾{êÔ)¹\F·z.·ðFAÓ,ËòÈÖ×|}}ýÍ­þð]νi1/­ ßö|}í¦;fYz#1{¼ÎP$Eâáˆ?\‹DB› Ò£_§Ñ¨Fô–-½…ŸâGS‡dQÞÛ7Yõù÷mW§Ó4:’L)-áÀ&ÄC z3Š#†ÿ4:J¦TÊ :¤ŽW?,@ ÀM’~€Šñ.Å£Çw–'íFqÈ£Šy$>ÃcBÒwó¿JF¯–N}“#éÜ­êÎÕÖ_.úŸÆüuí,0~ŽÞä ÅMñq†OD£>½úeEñ»VÍRI¤8>ÅqœÕjÍÏÏß¾};j®&“I(:¼2 ­V«V«M&Óèèhcc#Ê<A¬®®]§OŸFF" ‹ŠŠÌf³N§«¬¬|ñâzÙM&ÓþýûU*†Êi4ÔŒåryFFF^^žÃáðz½Z­¹²Õjžžnii‘ÉdƒaïÞ½¥¥¥V«ÕjµVTT,--Àôôt~~~0ÄžÌr±Ûí¨\ÂksúÇ­®®–”” ³ÄÙÄ}ûöiµZŠ¢ž>}š~ Œ‘Õ©TJ,cnšH$ÊÊÊjjjòxù䓼¼<SNç¹sç¤R)^¨¾úê+½^œûÑ£GÅÅÅèâèèèJ¥XÑât:KKK‘è,//Ùív³Ù\ZZ* 9Ž£iÚd2íݻ٠†¢¢"£Ñh·Û³³³³³³-Ëúúú•+WðºˆÜ´¶¶Öï÷¯®®¾÷Þ{YYYz½^*•¦ßknnnß¾}Æjµ¢rIÓ´ÅbÉÉÉÁؾx<Ž-ƒ “É***ÆÇÇ •JFäî ‘HJJJ°÷®¹¹Y©T€Åb©©©‘H$kkk;vìXXXðù|eeeR©T¯×geeíÞ½Ÿ)»\®òòr‘HÄ0Œ×ëE>–={ö -3™L—.]R(4MËåòŠŠ p¤iúÙ³g(«Ûíö3gΠ֮V«1,˜ÑhAh4š;vlÛ¶ ÇÍfsMM\.çy~ll¬´´Ôl6+ŠtèEQø(M «««{öìÁ}|ã_Çûì³ÑÑÑ•••‡¶··Ëår’$NgSSÓýû÷e2Ù‹/z{{[[[ƒÁ Ífkll¼sçŽH$šžžžššjkkCƒÄììlaa¡N§C¢œV5MQQ‘ÕjEÒƒÎt¯~ôÑG™™™ …‚ ˆöööû÷ï‹Åb©T:<<|úôiŸÏ‡ ïܹsllÌh4644˜L¦D"!‰Î;7999;;{üøñÅÅE–eFcvvvww÷âââôôôÂÂÂóçÏ%‰B¡]]] ËËËSSS—.] ©TJ­VŸ?þÉ“'ccc—/_~úô©Ûí–Ëåû÷ï×jµ˜%רØ8<j´oÜ3úó4:´îý¶s°îó»g¾^š;=Y&€ŠL±,‡‘@S…wAÇa{<¼v-I_Nð·éRÆ4©â^ ˜Â«žìŸÇò {I°âž$É-+í?‚H›:$IbErðõ•Z¸…7–å~d >ïZÿÀÍm-ÿ¸E£·ðæð+3uðÀ%SÀ“¢Y€ió{ÉXŒ§àyžOGsÀ3À3À“6ÀÃp4Ã¥z3¶AØMS?Üéü¶}òþïͲîûÝ&úöŽtç<ì/z6rH9ÿ;·n‹)xŠ`RAš¤,ËAІd´2[Ë_äE2¨À¡H<Ïëõú³gÏVWW/--ÅãñT*ÇE"ÑñãÇ«ªª>þøc@€^U†aVVVŽ?^VVVPPPSS“V¬E"Rj»ÝÞÜÜŒÁ(Ÿ>}ÚårQ•L&].WKK‹Óé´Z­555GŹ\~âÄ t:tH§Óy½^FSUUõöÛocÞ‘#G0/‘HÌÌÌ\¸p¡¡¡A¡P`TˆÁ`(///--­¨¨(***(((..X\wéÒ¥ÊÊÊ={ö`’V ƒA‘HÔØØXWW·´´„;b6›Oœ8 èõúÑÑÑ+W®<~üøèÑ£ƒA¯×v€J¥ÂZrš¦Õj56pûƒÁ Ñh4 xg‚‡W.—£9c`qqÑb±à»ãe,³Z­6›M.—£%ƒçy‹ÅR__?66Á`_gYvss“ã8§Ó‰}àp8ТV«ñp±,»¾¾¾´´„¬t‘ä›ÂÏz¶ÿýþö©ö©ŽkŠI‰ëwW»¿¹Ò;pónWgwggg___OOOWWW______WWW___ooï­[·îܹ322rïÞ½Û·owuuuuu  ܺuëÆ===“““===ׯ_¿yóæƒîܹ300ÐÙÙyûÿØ»Öç(ªmïßà U–¨ýÀ¼u¯%”Z(X‚J ¯ƒA1ƒ<®ïåå=‚‘šò€„ 1IB’™0yM&Éd=Ý3=ý~÷^÷Ã"ÛŽpLïü*š°{÷îݻӿµöo­U[{ôèÑC‡íÝ»÷àÁƒµµµµµµGŽ9zôè‰'ÊËË+**°üx}}ýáÇk3ø]¢²²²¢¢bÇŽ‡ª¨¨8xð`uuõ™3gðWNMMMUUUUUUuuõo=Þ þ˜¨©©«©©;RQy´º¦ºêȧŸþ÷ÖÿõõáúLˆaÿÏÞË×u,Û7m¨!.’¶4\e@\ à:lQL(„Hº™tA'`ÙàðBš˜ºÄKâÆz¤X—Åy¾ÓQz&‰H›jˆI\Û¶M‹€ ˆr @ÐMUrlwÉqG\×EQ©eY4 3ÇáÞ:ºf†Á¢ÂS]×UU¥ªDÔ•º®‹1aš¦Q1+Ã0¨ÆÜi%„`·„$©¸ÕS’VJ¾1õÃ0x]Û¶‘ÄÛ¶-Ë2ŽÇSÑ`x_TùîdA°ê!úÂ@’$–eñ—0•‹”cK]×ñ,ì–þ/ê°j:UñÒ¡R/*èÈQ€])Š‚æJ:¦f^s âxðêø›h4úæ›o¢M‚Ù1ó1¨ªŠSMÁ7y3ö)6Ã)ŠB­š‡…{'¼ãÙø—åuooþß’ÝmgoŒsúüu¯_ .‘ ‡ÃŠ¢°, …Òé4Çqáp˜a˜x<.Š"ú’#‘˲Ñh4r'ÂÄÄ„ªª‘H$™Lb%]×9ŽCïµ$I“““<Ï3 òl"‘ày¯‰DR©T8F?4ž’H$"‘H"‘`3ø]‚ã¸ÁÁÁãÇ{½ÞX,†¯óÄÄÏó,Ëb‘ËX,–H$R©>ô 2xàˆÇ,›Dot¿ÏäHÍŸþü—û”_ù1áeN%¼Su›L%¼“÷~s<“ð.ƒûã±²ÞÜlïþ¦âܪ÷ömÿº«Þ«üŠ(K’$¥R©t:­ë:^EñQ¢ÑLÓ¶ËãiÛ¼—6Ú ® –eYŽí¡?ª®Û65]°4]M—Ó”E°­(€®Ä° ]2 €  qÜ„mi@@W-pÁqú€‘8â0gç•Ä|RøJÛ¶ýO³ƒ!Iuÿ>;ŠÁ¡DLi÷Ïi$©Ó30BplÈ:wœÒb šÔ‘?#àóÂYr]“o<Б>4’IC‘à SîlÿPÐ%6ˆ —d¢=ptí–ÓÛ¿ôžöŒþOemZ²Á×&8WhÔQ+‚Š\UU¥FÎ0M©¡ë:5Þh "žNMY– !–eáR«ƒ¶ÄfÔÌ‹ÇãØ¹,Ëøø¨I†Í!4xÑ4M\‡`ŠÑA £ÂØYìµUht¹®Kí(ÇqEÁñ a€m‰Z¿x.c|õ¨¤cp$š¦À²,L·aJ;ާüjïÅêÁ7b:·$ɶmÔÊg*°dðð€_!bÙà–ITVV•”íÜsàȽ´ÑÁIË)UUExžOJ’¢è´Š¡‚4úpÍw·ot?Ƕ²Š:°ø`Rç,ý^ÅÀ%EVEdBŽãà§ëÑ¡Ñ\0LÛHÛæÏ¥S°\dêÇ@m¹`;ø{ç‡2i`ÚVZ–dUUEÑTÓQÒIQ3¸aM—lIæ,G$ š–äÓ0¬TR‘%˶Ýé¤Y–åÙ%Ç¥’ôLãñý¿X„ôøÂqA±G2™¤´€çyü¨Ï´ŸéWGŸ4š ‚ P×;¶§㇠]ב! F{cv_ôé4,˺¿™ñ;S–¸¢®LŒß)®;à†•žÂ*†ŸWM ¦¥ €»Èü0Qv†¾y̾"I’ªªhÈI’„ù:°òiÐ4Íqœ|ÖHC‘³š¦‰¾|\Tô¡¶ç<â;BÏÂÍJ¦aŠÜ£‘t@E\ÛÔj¥ >™Lâî . ¥ƒ˜&‰ mÛ(Ý¡LïZ×õ»µiñ7ØwŠðŽ`ÚÎ&P§¯F^ÎúQÿÊ@Sª¼¼«ڶͲ¬eY8«Ø†îº¬X‚íÈÂ]–å 7NÅ_R]?ó¦iâgïÓžºaŠÓòèL¢!0åeœQ?h_¡;i4uRÞÕ§û°“%Oaú1MÄìú¹‹Fïx ¿DŽ“Ó<Ãó@JR™DÊwó–-©©DìËòºÂ’“í½qîæÄÞêúDRRJÇ÷žöööx<޳‡üUûÔ·Š˜î=ÅéE0 ‡€ã8dŠ0%’Ñ4 ‹qNïW 5êÐè '„ <¦tD¡PŸc*•êììüá‡Z[[©Ãt,ëéé9sæÌÍ›7yžƒ¦iʲ¬ëº¢(<ÏŽŽ2 ƒ‹¶¿¿_QìW2Çq’$…ÃaŽã†1 Ãçó¡U ( ¾qÔvUUÞx㺮 ‚àñxÚÛÛ‘jÃ4Z؃NŠGŽh*Š299yüøñ@ €üEñz½^¯·­­­££ƒeYÜÄ ®ý 2xà˜)žàRŒ¬K–£Y&†åèºi:Á pGÝõCoÅáó˜ð®Ÿ‘]±Áå5cpRî¦4:•Jñ<>¤\T­Y¶Kî8¡-Ûµ]Çâ‚CÀÅ´,‹,>•O†Ò<«©€ Y¶¦º`5K±,ÙTM°’g‚¡:Šb+’¡1É?ª@—Ûìj}aÂ]êGÕÀ? ê*»Ë—ŒqØ`|||º{lFýàhŽXª*F›ý—èöûuÒ™¡þÇ€®hÃ0fçEž~§تʳ †ç Ý¡áPÛ¥+éXµÑÿñÁ‰­{®ŸïœükÝÉXBІß{¿  (é³öx<6lèííµm[Ó4”F`Ív†aðS_2>_I’+Çb1dÒøÏP(ôá‡vtt bY&FšÂ4ûh€)^ÿKE¿ßßÐÐPXX¸xñâ²²²ÎÎNŽãhØkSSSvvöªU«vïÞ=88 …víÚåñx$IÂ###ùùù˜©­»»ûwÞ ‡ÃHdÑeñx|÷îÝè’omm}ÿý÷1§!}Åp¯ƒ&¯L¥R8W’$Åb±µk׿ççOLLÐeƒú¤qY¢ÖœJAè+ó¨`hh¨®®nrrŸlKKËæÍ›.\8oÞ¼ÜÜÜööv,bJ÷Í2Èàc¦¢ŽÃF%­›²®I’$‚¢hšI24:ƒŸEH£·í»ØÏÈqM6Áf%åö¸Äð?j£QÔ¡ª*bZPžç©ÙÑhÓ%w®îà7ÒE'2Ÿf'ƃ|*‘æ×Z¿Þ··ªbÿù³':o\›2t$Ý b„S l´˜UAÓÀ²tÓ˜þ±œ 8#Lß§”…Fûý#èEQ´úã b•ÃÂ?ÓhÞ§< ÚSʧE¡”Ýç¿“Fª‡÷B÷Ó‰Cú‘dÒLY–Ó|J–ãc¼¬±\:œÙz£ 6ߺçú…®È¾c§"q!4^ñïYO=õÔæÍ›[[[ÑþQUµ§§gåÊ•øŽ$š(žÆä- Âàà Ïóápx``€eYœóH$‚%r$IRÅçóeggŽŽÆãñ¾¾><Õ¦iú|>ŸÏ‡iUpUô…û|>AÚÚÚ>ûì³’’Ç …¿úê«ÆÆF j<}úôÚµkÏž=;00pëÖ-¿ßŸ——·ÿþõë×÷öörG¹xñâ+¯¼ÒÞÞÞßß_SS“••ÕÑÑFÇÇÇ ÃàyþÖ­[ñx¼££#Nó<ïñx^ýõ@ €‰ùÂá0ŠÜ\×M$hÍâ$ŒŽŽŠ¢ØÔÔôâ‹/Λ7ïâÅ‹èÀ…B}}}===###wíA ß¾} Ò~~ÿp]—eÙƒâèêêšEý›)È‘#GðOÙ²eËžþùëׯ÷öö^¾|ùûï¿¢Qdð0SQÇX‚‹‰ª`Xot³ÆcýçVZ~EH£-p8Y LÈ ÿw!†\*)Š:(Ÿv]÷J£‰ Û!¦CLÛqÑàà•æ†sßÕ|7û®Ÿ=QUuàó¿¬ìò4 û®\8ö—½íùÓÆÚC{Ξœ9)YÔÀ’\5¥'M0l°&bãoe¯z+{Õ«¯¾š½råÊeË–mÛ¶ÍëõÎ.îÍï÷äççƒÁÊÊÊÞÞÞûx£é›O“l†‘H$Š‹‹qw; îß¿ŸêVgÚÏöíÛ±8­þýàv¿(Š@`ëÖ­¨Å-Ï45‡išÁ`pË–-CCC³3W¦?bni"Çé²””¤¾‘áÇ«š%%x[R¹xdß¡ú¢ÒS[÷\ÿ›7üuýé(#‡Æÿõ_þíµ×^kmm-((¸xñ"¸®ÛØØˆÞhŸÏ·|ùò––ì~xxxõêÕýýý„S§NÍ;·´´´©©©­­-''ç™gžY·n]{{{[[[aa!ž¥ªêO<±nݺ«W¯ö÷÷;vlýúõ¡PÈçó•––677ûý~¿ßßÜÜ\ZZŠ©Ê—,YòøãoذÁëõÞºukÍš5X¦Ì¤®®®—_~yáÂ…ƒƒƒ---YYY­­­ ÃèºÞÔÔTXXxíÚµâââ“'O¢HãĉÙÙÙÇŽóûýß~ûí²eË.\¸ ªê‚ ^zé¥>ú¨­­-‰,Z´hppP×õ–––çž{.//Ïï÷Ÿ?~õêÕ¹¹¹~¿¿³³3//Ïçó¡944´|ùòþþþ'N,]ºëYvwwz½^¿ßß×ׇU“‚Á`4-**ZµjUww÷øøøñãÇ+**¼^ïo²LfŽãjjj†¹téÒ¢E‹æÌ™óÖ[o-Y²dÁ‚ëÿ½k‹jêÞÓçmæ¡/]«·®v^ZmW=UQ”rK5PQn"à*ŠÔRgµ==§£Ö^l± <¢ÇA!È5!È Iv’½wvv’p IH¹ÿçáÊé´cgÙ™sflùV’µXYdóßáûÿïû~õõ}}}ÐÁ‡ÿ‘âü:ñó"†ž`ØÜ‚ûð@`k+¼ãÞÁSà7-mïj´ˆT8{£óQqݾ†¦1D­D Ý^ßø*@¿T±8ˆÄ@8"D<î5!oü‹+Ÿ\ÿü÷VBí^ÆÇÝú÷®+Ã÷¿¹ÛùÉ¿¾_ñ¸»å_ê’ÚN¥üþBùg•‹Æ†c›QÞŠo†@0 ÂþM/‚ %ÌÒë_~-KÇÇÇ'''AOOÏÕ«W9ÎϸJ&“éèÑ£ Åh4ÂpÏOK¤?ˆÏ¼^¯Á`(,,äñx°è@"‘|¿$î©Þ'77w›]ïŠá¾_Ô Õj‹ŠŠà¼î„Õþ€òuEÓÓÓ¡jø´x–Hóa¿Õn3/-ð#*µŽÝÛo3FŸjéƒ4ú«??\ZÝ ‹¹ÙÔhµÚû÷ï·µµÙl6¿ß¿°°@£Ñ¬V+ŽãT*U$A»ŽZ­.,,„–Ëåæääô÷÷»ÝnAŽ9’””488è÷û ‚ÈÎÎ …[[[J¥²¼¼üþýûÐ <77G§Ó ‚Ðétpˆ=üÅ·_Úl¶½{÷¦§§÷õõù|>NW^^>99 Wܶ †´´´#GŽ˜L&­VË`0‚Èd²óçÏ ¬­­ 544´¶¶jµZ¯×K§ÓH¥ÒÌÌ̵µ5©TJ¥R{zzd2Y4E䨱cð|†Ïç3ŒÇ{<‚ 222Þxã EM&Sqq1œäñxŒF#N—Ëå@`Ïž= +++8ŽÃ±ó€x<®P(˜L&I’J¥2+++!!n Ün·V«ÝN"þÿTò:;;ív;—Ë=xðà¡C‡º»»ïܹóæ›o>÷Üs, ‚λüÍ𴦎5zÿsüe¸\¤ø7úK££àû4št9\ÑÐ&Áx<G¿{„bñ`<ŒG£ ØÚðoz!¶YM]ß^¸?ôð¶xr@'Ÿè¼vQ%îëûâƒfæ'-ÔÞ+ù£7˜_´¾Úšõ^ÍQœ ¢«±È:›áˆÏpox] Ì:ò­Ä¤%A §Ó M.—kccò’ÉÉI™L677g·Û¡³Âëõ"ât:•J¥X,^XXX__÷x<ƒ!!!!''Çñ¥¥% Ã`W+Àï÷K$A´X¸Ýn·Û­R©FGG\.‡Ç‘‘‘ìììÏ>ûŒÏ禦¦àe …B‹E$q¹\©T ¡Ž»±±¡T*Ýn·\.Ÿžžžžžöûý@Àb±°X,>ŸËE"‘×ëU*•£££ …ÂçóÅb±Û·oS©Ôëׯ«Õjh¯Ÿ™™2™L*•BÅ: -..šL&»Ýn4¡)_¡Pp8©Tºm±ÛíB¡P.—óx¼ÙÙYØþëñxT*•X,v»Ý2™L­Vß÷z½===ùùù½½½Ó¯¯¯OOOK¥Rh…߉ápX¡P™L¶½˜Ÿy-‚Ž%»Íá°±žW¨î=ü BI¥ÑhÓÓÓ(Š~øá‡+++ 33Óf³éõúÒÒÒééiì¸`0‚D"WPP)5‚ <þ¼N§“H$­­­………Ÿ~úéèè(ü—,‘HÚÛÛ¡=¦ªªêÑ£G’$kkkOŸ>ýå—_–——777‹Åb›Í¦Óéêëë_y啦¦&’$].×ÐÐPQQQyyyCCÃÌÌŒÏçÛÜÜ$¢±±ñÚµkß|óR©„ÄÏŒúIˆÃôûÜ€ÆHØ. äYv†<¾'™:ì?H¥Ráôʉ‰‰ÊÊJ•J% KJJPÍÌÌär¹ÛExo¿ý6†a¡Phzzº¤¤ÊÉ‚¤¥¥1 £Ñ 1 c±X jµZ&“©×ëá] ‰òóóggg­V+…BIKK«¨¨())ÉËË+//'I’  …’œœ ­D"¡Ñh:îû“_¤RiZZZZZšÅb±Z­ï¼óTš¹\îîÝ»™LfQQNñÅKJJ‡X,.--5›Í™LVZZª×ëÕj5“Éœ˜˜ßqÄììlŽd³ÙÐÅát:ÒÒÒ’““ ƒZ­®¬¬„÷ÜVTTèõz•JE£Ñrss5ÝnOII),,d2™™™™ÇŽ«¬¬4ñxÜétö÷÷×ÖÖ?~¼ªª îFþÊÏÁÖÖVOOR©d³Ù©©©‡"‚$I‹õÒK/%%%i4šÍÍÍíá;ØÁÿ:þªFÇÁÚª£»ûOï^øà'¦þ„©Ãì âKþËw!–HvÔèü—x"þ5D ]¡-|uÉî÷:ƒ›þpx+~÷„ÂþpØ:½®8ˆ…"Á O8ð¸V&†úVÍú Ó¼ÿ~ƒÁ R©ètúóÏ?Ÿšš Ð?{ìÑvðßâÇ¦Žæs¯vÜzÚˆáêF„tlbvß去§Þÿãí~©T…¡äÞÁñDSÇ/¾ð.€cÓ¯µYH—ÃæY'MæEów£e‘X\Ä­‹f›5 †B¡Ph D#«6Ë@oÏ ©ÕÉ8êéþ±Þ+¿;Wð‡s9:N}þ~æ{Çÿéëšøüøo¦¾:¨íçw5»\è°…ƒØ2¾hhÄ# a°¬µúçýÕE'KòKÊËËsrröíÛ×ÔÔ466ëlçççi4Úèè(¤§»víÊÍÍEQT©T–––B¯@«ÕæææZ,ø$)) RÂÂB»ÝF‡‡‡«««I’„ôNes¹\v»jiZ­öÞ½{YYYf³EQxD°X,4Íf³9N …B¡P `nn®¬¬L$Åb1±X\QQÁãñ oàp8©©©ƒaii©´´T(Æb1½^Ÿ——ÕeÀÔÔTÜ)..‹ÅápØh42 ‡cµZ5MoooVV–\.÷ù|EEE]]]°RW¡P”••á8c‹p’œÑhÌÌÌÜž²ݺ‹%###99қ͖‘‘ÅEhZ%IÃ0ƒ‘˜˜ˆ¢(¬6c2™R©4‰,//çæænW|ü2ht$ð¹××¼^Ùä ]ëvÂâYvüDá]BB¼ ù|þ¥K—¨T*•J››Ã0¬¾¾þÁƒ‚<~ü˜Åb%&&Bê<11‘——ãª8Ž>|øðáÃPœ†n‡ññqø|Ïž=uuuG£Ñ 455ñù|EkkkÇÇÇm6›B¡H$7oÞT«ÕZ­6111==]¯×CQsjjª½½½¹¹Y"‘˜L¦ááá+W® Auyuuµ  ``` ººº¬¬ŒÏço{VVV†‡‡+++ÇÆÆèt:—Ë][[+))‚~¸`b±ô4///G¡Pª««Ùl6üÔEEEjµZ§Ó544ÀeÜÛÛ{æÌ™·Þz‹$I›Í–••µoß>h.‚,f³Y§ÓiµÚË—/a6›/]ºôÑGÁ’8¥RY[[ ¯ä³«ÕÚÕÕE’¤@ øö®ô¹‰;MçØO©Ú/ùöÃV¥&U©T88ÄqÙØÆà#Ì|`ŽŒ ™e²2EÂL’Ý$Œ¹!!†Ä÷-ɇ„l뾬˒º[GŸ’ºÕ­w?¼ÐC1 [“ÝÌ”Ÿò#ºÕ­v«úyßßó>OqqñÊ•+»ºº°d}饗ð†á8nIÔ±„_v£É(qöìù÷ÚŽ|þÍ…¿Õð.ÊHB°‡X¤Ñç.uêõsNÇÂ^Â_âiÚèç2~%'* @:#I±LÊ ˜D”gEYåá” £.ÉfñÛ ‹>Ï »QŠ{Hçètÿé›ßî뺸ß9ñùÄÖ«Ÿæ]mûçë­ÿd¹´Áxµúxï8ï]}"ï£éP èʤ9ÈBÌiܼsüÇQÝíp8nݺµgÏžãÇ£l€çùáááW_}µ°°°ªªjÍš5¦®®ÎívQRR277—N§ãñ¸Íf«®®^XX°Ûí&//]vîÜi³Ù†ÑjµÅÅÅ v€Pò[]]]TT„+ækÖ¬ñûýsss555£££™LÆív—••™L&£Ñ˜ŸŸ_PP€ý?¤ÈØ¥‹Åb………H• ···²²2 cŸ/“Éø|¾mÛ¶áÜU0ìééÁa£¹¹¹êêj½^ŸL&Ãáð›o¾‰ä¬´´T£ÑìØ±ÃårMMMmܸ±¿¿ŸaAH’ܸq#®æ«&.—kõêÕëÖ­ËÏÏ/++«¬¬Ü¾}»Ãá@z]\\l2™Ap»ÝÍÍÍ8шÓd2+V õ³ÙŒój‰DÂçó•——ãÊ@:ÆÄ–ÿÝü€È©T,Y¤¨ €˜·Ç?Ö; †ÿZüÊ–²òÊÊJ¿ß¯¶ÆëׯãE­VÛÞÞ¾uëÖ–––k×®566z<I’ôzýÁƒ=,Ë>Ÿ¯¥¥e×®]v»ß{ï=utïèÑ£§OŸÞ½{÷»ï¾{àÀôNW]]]ZZº~ýzô§Ãîuyyyuuu0T ÙììlooïÎ;óóó[[[õz}4Åxj‹Å²{÷îï¿ÿ¾¢¢bÛ¶mX[bQÄ0Ìàà`CCÃøøøë¯¿ÞÖÖæv»u:Ý–-[>üðÃÎÎÎÆÆFœƒ¤iÚçóíÚµ Ë£ÑØÜÜ|òäÉ={öà–hó hÀ·aÆO?ýôÔ©S8Â833SWW÷Ê+¯ÔÖÖƒA—˵uëV¼Õ«ªª, –Ä.—ëÚµk555MMM### ßûãç#‹…Ãa4?á§Ÿ~Ú¾}û¦M›V¯^½lÙ²;wbÕ=×ÿÏv Ï+ÕFÇHêÂ…Kïú÷/:®ü­ñ+Z|ŒFONλœ¾%½„¿Ä_u<¯aàr: YHg¤,£d}É:ul£LydddÓ¦Mj@F<! ¶µµÝ¸qÃd2Ÿ8qwt:»È¡P¨±±1 šÍfFSXXh0R©”Íf+--Å®¹Åb©¬¬´Z­(•îééÙ°aƒÇã!bóæÍ( ÁÕpÕcffæí·ßöz½¨BA…Ñh,,,<þüðððàààíÛ·ÑøÏf³ÕÖÖþøã¸ïôôtaaáââ"I’ê  Åb)--=uêTOOÏÄÄÄàààÐÐ&\¬\¹mŒ@QQQggg*•šŸŸß¼y³ÕjµZ­fíÚµX ¸\®ÊÊJN¸ÃáPEõ |zÍ3€0ÅÄc‘x<äÓ$•œŸº¾ÑO ŸŸ5[­V Ÿ!@2™4¢(båÈÈH___$1ñx\ÅD"133“L&eYÆ)Oœ4E1™LŽó<ŸÉd†™žžöûý½½½8M‹³ËÑh4›ÍŽkµZœ¦E;—X,¦Õj‡‡‡“Éä£9”‚ ˜L¦ÉÉItƒÆFµ$I4Mãò…Íf …BÀqî‹ÅÆÆÆ0ÉÅf³a±×ÝÝ=>>βìÌÌŒ:çG’äèè(Zg°,k0X–Õjµ·oßF×<ÌgYvlll```dd$ Úl6”ù|¾±±±ÉÉIǃƒ¹½½½SSS8ø 8ã+Âääd?Ê9ž-ñƯÌÍÍaúèèèÝ»wûúúúúút:A¢(’$ÉóüÓ=4—°„ŸÇF /^¼Üvø·?# ü‰Ýè%½„'âi4:.¤mþÑn4Š:xžÇG<²Øý¬Ðh%#ƒ ©´˜ eÉOÇ“¹là‰#†"€ dÓ™´¢È Ð<ŸHhû~šî ̤§A±5`þä£]ÿr|ÿ¿v}ýŽ¡#Ü9qËð×U¿o};4{'E;8^¢c©/¥)9p[íÛK+ÌÎl:ƒ¹$f³¹¡¡Áh4â´ÖèèèÖ­[1þM«Õêr¹‰„Ýnß¶mÛ¹sçÜnw.—s:¥¥¥V«Õëõ¾ñÆùùùG5¬€¡¡¡¢¢"“É”L&ñ¡óóó(Øðù|ãããçÏŸ/++ ‡ÃóóóÈ¿çææ0bÃétæçç/_¾©0nƒ\ÓëõnÙ²=48ŽCwa·Ûn¾÷îÝÃ|ššš@ Ë2A¨[ N§]ÃR©T4-))ÁÞ›,ˉDÂb±Äãñ@ PRRÒÝÝÍó¼$IèÞ`6›EI&“8h2™T“5¬@Ünw6›u¹\¯½öÚÝ»wQrš——‡›y<žU«VE£Q‚ òòò–/_Žr^’$ׯ_#›V«µ¬¬ Ë|êçr¹çÀ©#EÓSØLÆðGãtÒ2È€Úèº_ß˲¸‹×ë---ššJ§Óènf6›‘ˆ Ö××F£ÑX^^þÝwßy<‹ÅÒÐÐÐ××çp8nß¾}ìØ±@ àõz±=Œ ñ~¿ïÞ½:Ç?ûì3Š¢Pž;00 ×ëÍfóÄÄÄñãÇïß¿¢Ø_|±µµµ»»[«ÕîÛ·obb"•JÍÌÌTUUŽŽÞ¿¿¦¦ÇÅÜnwWWWkkëøøx*•òz½%%%f³óùr¹Üß!#æGÑhšcC$yãÇ»óv—¬d#ã±èâ¾¾RßúáßOý0áùÝ™KI6 ädPÿèÍŒ®J¥ðš`Ê:òB,¨€a˜l6‹¿ ‚€,éòHäÓH.‘õª±ðØÁ•$ ÞªÏ$ ¡ÆF%Í4M«|ð=ñ{„tù:rn•%s‡¶’ÙlwG¥SgggKK žŒ^¯GAüž={¼^¯j<·wïÞ¹¹¹`0ØÔÔ4;;›L&@CCºï‰¢h4÷ïßo6›Gyyù[o½õñÇ»\®ÉÉÉ iÝáÇ1>& ¡ ؤÔjµMMMEEEíííêØbÿJJJŠ‹‹îÝ»‡®½ï¼óβeË®\¹ÒÜÜÜÔÔ„£ ÓéêêêN§ËåêììÜ»w¯F£Ù¿ÿàà ’0»Ý~äÈìRãSÿÙ^Aä@âù8 ÇbÇŠ9HÒ|Àá…”HLjSÿyiǾkû?¹;<{§[?mfBŒŒG"–e9ŽC9 Ïó$IF"‘h4 …"‘H"‘ " Q…¿‡Ãáh4JQEQ$I’$‰ûÆb±H$‰D’’$ƒÁ ÊEH’ ‡ÃAøýþX,FQAÑh× (ŠŠÅbAÿ‰D’É$Ã0áp_‰Åbê6Èeq_tÂãÒ4MQT ˆÇãE…Ãatèvá<@ ¶¶¶Š³Åb1|+ ¶´´„B¡h4‹Å‚Á ® …"‘H ˆD"áp¸¹¹9‰?~<‹íÛ·/‹…Ãá`0ØØØxàÀh4zôèÑ@ Âáp(jllŒD"MMMèN0lmmmmmÅÕ£U ‡ÃØ»wïÁƒÛÚÚÐýÆÆÆ`0ˆÑÀÁápcr¿öGæ©}þùçápx×®]MMM±X¬¹¹9`”Z[[@sss4=räH4Ř׬f¿¸…B‘p8 4·…#‘Pø£>þÝïÿØ9~?=–Íeµâ:ºcTÔRY.¨ªb8nFW>ù¢­£kö`»/Œ¶ÉÎ)³#Y5]tLÙ´ÊrAVsUVTAêŒFÖÚ\ÑTUê°ˆJŽ5^”JŽ¥wý0šã·Ë¯#bÿ`@ÒD–ãÈŽU`N^+&SÉ®™±.½ÐKNŠÜ4y±y*1“<'Nšj¡'²üC}2,þÞµÊ)˲XÏåœÆa¸¿¿ÿÝwß½yó¦xtÁèò'¿»® *x¥T}ÔAǼ+UÎó<Å€F«úéWªRUÕ¶m\Ëî²,ß•¥ë:¾"”bÁ ÀÕ±æŽ#±Þ-H± @LÇ@-ø—ø±îïgÓ:Žƒî_ºtéÕW_ݸqcoo¯ØkYz*è¹bÞ?Âi!_¡rÿçªq’fftEVLsRÊdU©ÒTÆS’”ýüÏm/¿ytë¿v¿8ºïÐ_®Å‡J…J>+!Ö‹'}zz:N—J¥t: EQfggÎår6‹ø1"Ê8öân%II§ÓSSSét±í|>?===33ƒÈ´0g2™™™™t:ÍfqÒÐd„XL’$œ ke’$!ŠœÉd …‚,ˈ #^žJ¥EA¿ÐH„KquI’p ÄSE\9›ÍŽ!æŠãÑrDè'''ÑT„Øq-ÑÁyiù|>‘H„B¡žžõK¥v¡ûSSSÐþX³šýâ–Ï$i6;=“IM÷Ýêmm íþø?ÜFf²i¹¢8¶fëJ¥X*K•ЬÛN F×ìo·Ÿ€Ñ£9-]t,ÅŒF4Z¼8mÛÖu½T*iš6w`´KŒ,Ëqˆ—mk¼˜+9–Nž‹‡Áh&FÜ%r‰ }'Œö S±l…sH%*‰ÈË“#["{–\…˜IÌaÞmƧm1×!â¤iôP×!ˆ›ØPUU¨ƒ“jY–À7oÞœT".G„TMÓqü ¦©išÐFðÀºa®ë lX–…´-Ñ*@UÓ4¡ Ø-`4ð%IUÇB Te‹ ¯^Jñ'DÜ}prtXàÑEQ4Mx#&P»ˆ(Cõ_+ömŒüßEù 5Diþ®¹ök4N²$y–™.Îu_çDd¸¶¬J™i¡ýŸ=-§Ú3ù 1òœÜ3øbÄÔæ÷£ð°`Cäú×1°Y$’8ÅÞt|eá;á–ÁwRU®¾X±ÁÄó¯o`~Âgûñ1ãœ1&8oœs‘ìH>¶÷½/T1óÞ†‡‡›››±ED¶mƒ íÏ5Ä`Öïjö¿kœˆS6 Ão½óÁï}p4ZqlÝ1j0ºf?Ïî £]r%µ4–׳eï67º¢Kš^Qõ»”:›0Úr‰1˲òÊŽ1YÌ”à ‡çÜ#ƉyÄ8ç#ÎÈ#îóˆ{Ľ*ŒæŒ<dz‰\N¶®äRJ-O»zžì"Y27ÏT=[ãŽéyŽã¹¶ë8žË8©šázœˆLÓf.×UC(§ Hq?Ðæ8FcnÛ¶¢(ÁâÓ4‡o}ªBXª"oºãŠÔ1Àe|ñ® ê½¿þÖsˆ”|D ©'¦*2ÆÕÅE…ȃ@3âêâ Œ1œAÀ5¦Û÷–sø¥RɶmÄ5M!Ñÿ8ø»<„‹Š{!òÌæ–Ù'[U=ËÌ•J×2…`4Ù^>úSã¡ oÛ¹/þ—KcûŸŸ’ʳ*sogʼnGžªŽ<7¸%Š¢ÀñÀ~àœÁ¥ ȃ®0«šHFñ¤À¹t6MS,¤`½çÁýE¡_‚Ã÷ñQu¾!Ó×Â܇{‰“SuîálÀYEgES… p?/ ¢~ÇŸ˜˜àœ‹q̱̿§ª'†IR³šýâvû‡ÎcÄx69|øèG{ö>€Ô1žËg+šê¹†kªZ¹Fê¨ÙϰŸ€Ñã’‘-{ŽêFtCVTUUËå2ÞIÀI|e“ÇlËpÉ“mm²˜);šAΈÙóˆyœ{ˆ:wˆ;÷ÀhΈ릦TЦU!²ˆ "ƒ¸IŒ#âÄ<ÇumdzÎâ.q¨¬j²¢‘VÑ-ÕÄ‘~-ªB[! ^À~•n"ŸAl 5Àª2:ðîÇaHãÅ…œxD Š3¹À¥aˆÞ Ž@Œiš8=✗J%|t]¹ï „ D—9çé!jz¸£ªª ¸`î â‡àiÀ8§j (MŒ­_Ï]…„ IDAT!6Ñ}tG4ïïšh¿R㤋òl¡bY‘KTQÍôèy¤¥ýMGê6F¶ÿ{wìüÀþÃ'JŠã˜¼«óâ7ÄlÁiòùüÕ«W …‚ÿý½â¡‘wûÊÕ#ïxÛU*•ööv¡‘GUOæÞ„N^Ŧ™ñÓ“ÊårOOÏ©S§®\¹âW£Óu=N÷÷÷C‚:™L‚‰AUßOUU¥Ó=¯C?3ÞßÍB¡ÇEâ©¿…þgöÞöûýyfD$IR[[r04Mëîî>}út{{û©S§DÕU¼2¨êGÕ¬f¿¬áYc¶Ãlgr|"hÙ±óÃOÃ?™bhzV5Űbº^ F×ìo·ŸH1Ëë™’kWl¤Jš^,É(¿‚r ¨ >§¢ÑwÀè©ÙŒlk¿FSFsrˆÛÄ.`4'ÆÉ´ÇJ`²2;œèK÷Ï2–¦3Ó%ˆ“Ç™Í<‡<›˜KLµu›yÅr‰sÒ*º­9ÜfÄîˆÒ }b :€<î -#´ôÌ9ÇÿB®‹|Pœ™U ¬Jh¦êó/°ˆàEøW½y5täJ¾0çWÁ*•е="xì'ÀX'qα—U¡  ?»¾…ú®.v‰À¡¸ ("†a€˜á×»«1øºˆÁÃÀAï@™Cêc÷5N†,+ÅÙŠeiœéJŒ^üöÂÌÈxnf곃±~#ö‡ƒ·Žuì™H†Ç^{µaãÆgΜÁB¦iPì~ã7âñ¸çybÑ£-Ü<Ü Lÿ:D?Ä1ýýýÛ¶m+ ˜9~/ 3A×uð…Äí̸/D”H$Nž<¹iӦŋoß¾½»»w:}úô¦M›–-[öä“O.[¶ìèÑ£DdTö‰ÄÖ­[QEhÛ!®,f2fÐqëêêÚ²eK"‘E^à—Þ5ÃÑxeâ1œÇ†bà(asâĉ+V,^¼xÑ¢E?þx]]Ý×_@ÀþÝØšÍO³,Çqn'#å³¹`0üÎû»ÿm˃£Ñ×1\³–bX³Ÿg?'ÅV,ò"‰‡16Oa´ÃÉá·õ£’ô%Y–Óéôèh²ã·-­­Á/¾j?=|óVv|ÂTâsɳÉ5É­¸ÆôlÖ#Ò Ýq<ærbdk–®jT}U xÇ|ëÚ·Î9óÅçÄ^íÂ'D *Ò¥*™î¤³*WA¤ضm?ÇC\Q©ÊIExI”® I19 jÚù]Ã0¦§§ÉG9%UC4Íþ€X F”úÞaAKDèKœU0prÏóĽal ž:¯òèÇ*ƒÌ=㤕JFE)ªê¤”W Û0]i*cËj1ŸùS㡺‘߸yüâèþÃ'ò³úäØÌ‹Ï¯üÍo~SWWwíÚ5AÅùæ›ož{î9”~Ïf³pÒc’$Ë¡ª*²3™Ìèè(Vp»gffp*Ã0ÆÆÆV®\™L&Q`(›Í @r2™L&“étZPl'''³Ù,ôédYîíí}ï½÷¶nÝÚÙÙ™J¥Nž<ùé§Ÿž={6—Ë™¦yìØ±·ß~û³Ï>ëíí …B ,¨««»zõªxÿõõõ=ûì³·nÝB“$IB9ÏT*%– 01‰D&“)—ËçÎ[²dÉÀÀ¹®+I¤`2™Œð"àaæóùÉÉÉÉÉÉ|>?”^h™L&ŽŒŒ mذaÅŠÝÝÝ.\X²dÉC=´fÍ1ìsžU³_«qNœC¼–23éææÖwÞßýÁ»±l®–bX³ÿ¡ýÓ»;ßJ$®÷ÄŒÈÙ9ðÈ*Ê÷ÃéœÌ´¢æVÔbEÏÈJEÕ!‘¦ª*²ãA‹œ+0ÚS5r\Ç6=beK“¦K¶j’Ë…Öc‹æÄ/bÉXR•t¿ (,êbPg÷³ZÊÑÿUï҅‰¯Ú{‡’œˆ<*Îd‹ùÌ'ûÂ(~¢{üóر\Až\ôôâ'žx"‰¬ZµjzzžÉÕ«W_xáÚ¨¯¯ïééÁjhhèëësçûï¿ß¾}ûž={vîÜY__âĉ‰‰‰ŽŽŽM›6½þúëçÏŸ'¢B¡088¸fÍš–––†††_|qëÖ­}}}hl<_¿~ýÚµkŸþù—_~r„ccc Ë—/Åb»wïþî»ï¾ýöÛÙÙYdŒ}õÕW«W¯^¾|ywwwCCÃ#<²yóæD"ÑÞÞ¾zõêP(´nÝ:TgŒMNNFCµóË/¿\»víŠ+¶lÙÒÕÕ…z+ŽãÄãñ×^{mýúõ§NŠF£ëÖ­CØ5ïØ±cåÊ•õõõk×®…˜£eYår¹··wÆ uuu«V­:wylét: ¡ûàà`¡Pèìì\¸páÃ?¼fÍšÁÁAá>ý£[³ym#N…¼ÔÒDù•¡t%9ë\ÉïÜû…¨bXVJã¹|Z®”-Sµ4¥RD4Z·|ÅNfåá´Ý9qýzÿpblÇŽßùA³?×¢fÿoí¾0Ú`FÙRû& ³©³ªU–EŠ!j@1$iË²æ Œ&Ç#$+Ùú˜4=ki9–纮Í—;¶ç¸®k[žk1Ûô NŽa©D®ç9®c ö÷Íׇ£¡ÁÞ×.wuœû2ؼï¿.œ¹ÔuöJ×_Ïÿ⓯~¸uÃáÀ']ßž,dÇm§"JŽ­šVW×¥]»þð//­_üÛE‹-zæ™g^zé¥Õ«W¯[·Ê‚jYV2™\ºtéÍ›7Y5i ]áÒx<þÊ+¯ Ò5U‹3C~ ÈgÈ}Põ±O¥RÀ²[¡jñ6VÍ©¢;ó9犢ÝÏó$IJ¥R7n÷:—Ë ¢¹8§`Z þ1Ú6O˜Ç¿~㤕JܱÖfZÞÌÈ$™Îý`ôâß>ó裞?~óæÍ/^Äü9räH}}ý­[·®\¹²jժ˗/#¡shhhéÒ¥Óž;wî©§žÚ¼yó±cÇ:;;ëëë,Xðæ›ovtttttlÛ¶íÌ™3D433óØcÕ××wvv¦Óé£GnøoöÎõ¹©úÏãÎî?àìƒevgtFg|à J¥hÚšBÒKš¦ ôBZ~À ¥ èRÅÝ­úSQäÖ÷¶€ôBzaƒm“ô–ô–´¹žäœ““´Iš¤¹ßNrNÎw|5Ëè ã :“÷£6Ó9I:§Íëûù¼?ïDb6›Ýn÷®]»´Z-‚ †ÍÌÌp8§Ó‰¢(Ç{î¹ç><::ŠãxuuõÀÀ\NÈd2J¥²¬¬L(Z­Ö444ôöö’$©T*ÁÍ›7[ZZd2$i›ÍÆb±‡Ýnçóùb±X¡Pà8.•J÷íÛ§V«I’$¢©©ixxX©T¶··¿ñÆ7nt8(ŠîÚµkppÐh4šÍf¸ÈívS¥Õj%ÉÈÈAr¹üý÷ß?xð`vï÷Ó§d2év»¿ÿþ{ÇငÙl>|øp]]Ý /¼Àb±¾úê+èÿùEXPN9=Fý´ÅèÃï~pêÜÕ?ŠÑÞhúaŒ†Õh³ ÍatN¿Ö#1:FÅÂé¸Ñ$@ÄIøh"@¦Bá(4rD"h퀣iO F§(À’L§@f-GV—W“± (x)æ§+2à§Üè$“¤1:M%¼gÿÝÛg¾þâ»/ÿî²™–Ñ¥þ;—ÿqñó¾».]ø¸ãÛã=gß¹ô_â#µùÿ} êãc˜~€™ŠÆÓ‰T†Ie¿?8;;û@v¿ãÒ…Í›7Ÿ9sfrrR&“©Tªì+…cp€ÕÕÕ’’’ééiXÍ…6Ó¬)þŒÉdâóùJ¥Òï÷C*lˆg¯ ³ö88Šú?ã/„fØ ƒð³N‡#¨³µg£ÑÈår£Ñ(t'û|¾ì뇽ð[Øa?B2™L¶þ‹Y´œþD1À»²“:H(–t¦ë.ÿF5ºp +??_£ÑÜ»wïàÁƒ.—  V«9ŽÝn'¢®®N¡PÀˆÅbill4 $I TUU ƒAAŠ‹‹7nÜØÝݽ¶¶f0vïÞ ÑEQ¡Pxûöm¿ßŸÉdÔjuEE…Ó霞ž®¯¯·Z­^¯—¢(›ÍÆãñ\.—ÅbyóÍ7óóóïÞ½ëóùÌf³P(œ˜˜øý~x€t:[·n-,,´X,:nçÎpÐÔÔTmm­Z­–J¥üñüü|4Åq¼®®neeE¥R±Ùl‹¥Õj£Ñ(A r¹b´H$²X,ápxaaáÔ©S555(Š.,,âžJ¥ìvû¦M›0 £izhh¨´´/Óé4Š¢SSS¿°`=MÊd2~¿ÿÊ•+N§3k“Ëå§NÊËË{þùç›››á¹%WŠÎéÏÓO}ÍŸ1úòå«GÞ;ñõ…ë£×âôÃ¦ŽŽ«?ÌÌ, –Fçôk=£ãt<”Šé‰5D×¢É@0M¬%’ÁPÎBŒ† ¾étúIÁh:I14ˆÅS À¬‘$ºæñST€$iv÷³'>˜¨H2LeÈx,äZÆ{nÝìºratðzlFqï›ÏßÕ¨¥ýwNÿýš?háÞùR<üÍ®/Þ~óìñª£ …ÈÌ ` c†€¤áð!`šiÂf­ªªT«Õ°ZÌ0ŒB¡€«F@`nnN¥RÕ××ànm¿ß¯P(ôz½Z­öù|ápX.—;wÇqE)ŠZZZ¸qãÆÜÜœÅb‚ âñ¸ÃáÀ0lqq1Ëåò‘‘‘ÅÅÅT*?Þ\.×ÔÔÔÜÜÜüü<ÄzøáË0 ›ŸŸŸžž†•lš¦GFFx<^oo/‚ Ц<<<<44444ý©TÊëõšL&Ç366&—Ëáãpœ¦m<®[%§ßâÁ “Nãñ8`È Ðéͽw<Äò£0ú­bvaaáââ¢Ïçkmm•J¥v»]*•–••af4KKK•J%€a˜ÙÙÙŠŠ Ø‘Ëåµµµðë……6›]XX¨Óé`ÑZ Èår€Õj­¬¬„·S*•¯®®¶X,8Žóx¼®®®‰‰‰©©©ŽŽŽüü|Nçp86nÜÈf³aÍ[¯×WTTÈår¿ßÏrðA‹Åf³ ‚°Z­ååå†e2™±±1g2™‚8zôhWW—F£Q*••••8Ž/,,”––²X,ÇF£±ººZ*•´Z-dzÙlpØwbbB,F»ÝÎãñ>ýôS¹\>55uñâE>Ÿ `ffæ›o¾‘J¥r¹|rrfƒ<ÅÇE†a¼^oGG†ap›:<6¯®®îܹóÙgŸ}ùå—u:,ä°#§?I4͘4•ÅèwþóÃÓoüQŒö'2¨'buG³­V/ôH£súµ‰ÑI&HF4V·'ÌÄü1xçGžlŒ€¡`@œÌè  ø3™8°8Ÿˆ€€€ hÐI𤙙Œ<αû}†Y¥yN>Ð}îôG­o7”j&:'e®žçÎù}fiËÿ~Îú¬áßî}µã‹C¥¸ú‡Lˆ ™&S ±$OÑ12I¦ãáˆwZ%¯«Û)“É C»\®öövFC’¤ßï?sæÌàààöíÛ§¦¦hšv8uuu/^Ü»w¯H$:|ø°Åbq»ÝB¡0//oýúõ'Ož„{­÷ìÙÃçó÷ïßìØ±ááa€V«mhh¸uëÖÐÐP{{{8ž˜˜Ø³g@ hkk›…® “ÉtâÄ 6›]ZZÚÚÚªÓéÜn7„ãO>ùäìÙ³---ccc‡C¥R566®[·®µµAt:=;;ÛÜÜ\YYùÁôööÂUáKKKGýöÛo?üðógÏÂåPpÎïqÝ*9ýŽ@F"Ñ` ‹é0ÔŒ„#‰ˆ' „…ÑE…Å>Ÿ$Éááa‰D¢Ó馧§kkkM&“Åb)--Íz£Qݾ};4 q¹\¸”Þd2mݺ5//Ïf³‚(//W(étAÊÊJxTƒ„ZUUe2™VVV^{í5‡#‰¸\.ŸÏçóù‹E¯×óx»ÝŽ¢¨L&“H$*•Êëõ²ÙìâââãÇŽŽ>xðàÝwß™™Ñëõ===gÏžEÄl6755uwwÃx¡¡¡#GŽ8ÎL&c4_|ñŦ¦&™L&•J+**`+\©TòùüÁÁÁp8<55uàÀ‘‘‘@ pïÞ=×ÐЉDP}å•WZZZº»»U* þźÇu·äô[b@<ô¹]«À¨jµ¯¤Ò  Áo`ôæ‚-………WVV$ɵk×:::8‚ ^¯W$ÃË÷ôôÔÔÔà8žH$îß¿_SSÿ˜Íæ-[¶°ÙlèÐÀ0L$MOOû|>AD"QOO¬¹¹¹²²2‚  ñc``Àf³éõz ÃFGGãñ¸F£ár¹›7o†l»Ý~àÀÎÎÎÕÕU˜ùH’dOOOcccSS‚ ƒÃáÀ dt˜ “H$Äb1—Ë=}ú´@ °X,‚”””<ÊÔ!‚`A¯¿þºººÚf³™L&X2ÇqÃ0§Ó900߈ÇãÁqÜï÷;ÎþþþÚÚÚæææìôäÓ'X¾zõ*A‘HD¯×Ã^†aåååëÖ­Û°aƒÝn‡%ꜯ#§?I9SGN½~§½ˆy¼]‹¦Ba$î‰ÆžìCÒ)@QÀ£C XI¤—\nd-„B¸'H¸ýË®à²Ëïpm«~ÜD½>W$ä'c)@Ñ€J§â«ËØÀí«Lc_ÓOÜ•^ûädÛŽ¶·‹ú®=qhË{õÿzjç3×÷þ“æü–É3œS{Ø€¤Äü€ HD©@$I†¢)ŸÎ¤.ÝV¬×ëÓé4,!‘Hºººnܸ±wïÞååe­V[[[Û××GQÔòò²X,¾ÿ> ‰ŒïÝ»×áp(•J/p:UUU7nÜ@QÔf³]ºt©¡¡aaaÁ`0”––^¾|z¦½^oEEÅääd<ÅbYÜÙºuëõë×=Ëåêííe±X†…B!¹\ÎápFGG¦¨¨¨°°Ðh4â8Îáp`‡]¡P”••™Ífš¦WVVÊËË L&“Õj‰DJ¥2[ƒY×0íqÝ'9ý¾@'L:N&­Î•@4 ÇÝø2Œ< £Ë¹eeeYPV©Tmmm•••AÖÖÖ®®.“ÉtÿþýæææÍ›7CfU(B¡P§ÓÅb1 ø\î¦M›`§Åétòù|…Bp»Ý6lhnnž˜˜p»ÝwîÜÙ½{·Ñh\YYÙ±cǃ0 [ZZšý補N'ô¿þúë8ŽÃÚ³\.ooookkS©T‹åÖ­[_~ùeoo/lzØl¶ÆÆF“É”N§'''ád,‹D"ξúê«‹EÑÆÆÆG677Ëd2µZ}òäÉ-[¶äçç;N›Í¶ÿþ¾¾¾ååå™™™áááÏ>û ò=‚ §OŸ¾yóæüüüÈÈH}}ýŽ;îÃ<}r:—.]2 *•êØ±cB¡ðÞ½{%%%ëÖ­ãóù0n(›Ø“SN]¹Üþz=óÞ‰wþ£œkûîGÃjògotÂè²ÞèXÌŸL<ÙwPiÉ€0É$ðeâóÛ£qg4J1Q’I$˜D‚‰%™0É„RL0M‡R‰• @†E'Ý+¶Þî+«¸>¶j6©¤ÆÉ®;çô]9°¢?¯:ÖýÅ]Gþ¹ë?ž±ÝÚ2w‰ýÕß^Jšú€ÏèX,ÍÐpY"´G§3 bÅ–êÄR©þ¿ß?<<\^^.‹e2œ[ª¬¬ÔjµN·k×.hEô÷÷ ³Ùl4…Báèè(Ã0°ÍýÖ[o ‚òòòššš²²2ˆÔ;vìP*•±XŒ¦i­V #¨pI’8Žçåå•””p¹ÜÊÊÊòòòmÛ¶Á^öÄÄDMM ´ºêõzH‡F%@t«¯¯GQ”aEY,VAAA8ÀE'ÙU…Ùt…Çu·äô[b@Ðã4µ‰ÄC`E éíÞøZðQ-à×466Æb±ì’KA% ¤"Ö555‰äæÍ›"‘Èÿì][s×–ÎÛT¥jçÌÃTª¤R5•Ì™‡IRn ™`lsI02à0B8!'äž™œ cÀØF’eÉØÁ9œ`'‚/XàËÁƶ-K–¬[·Ô­îV·Ôõ}Íà ]*2¤ÎL„‰£ïÁÕjíÞ{õÍþ¼öÚßÇq¦iŽ755Q.—ÓétCCÃŽ;òù¼eYE566Þ¸qCÅD"±}ûv·Û½gÏžM›6½öÚk¡PlÛžššÚ¾}ûÖ­[ëëë_zé%«Y\\ljjBªí<6étº¯¯¯±±qíÚµ¯¿þúÌÌ ËËË{÷î ƒ¶mÏÍÍmß¾³ÑA“É$D£Ñû ÞƒÁÝ»woݺµ¿¿ÿË/¿ÜµkW,Ó4mvvvÿþýµµµµµµÍÍÍÃÃà IÇq£££ܼyóÎ;/]º„ý• ” ôù|ét'jjj°çÑG­««»ví>?ÕµUüä¨ ÞUñ3â¡w>|;žˆÍLÞ9çþf˾–7[®ÌS šum>U,ª 5•òBù¢XE½WஆýK1¿[-if€7Œe–å C°l äÚX6è:€ /ämÐ `e³D{[klqt ÊT!y3t³íZ_sokÿäã×\Üüüo–}W|j¼ýŸZ^ûÍ­þÁÊê g(”MP ÐMÀ0!›ßRûâwß}‡±¡9ÅÎ;~øá§žz*£”Áºuë0u7??___¿´´„tdddëÖ­wî܉D";vì°m;NoܸÑår ÏÌÌ”ËåÙÙÙ7ãŒj(zùå—Q.G7M3‘H¬[·Îãñ|óÍ7W®\™˜˜@©iÌä!I$«V­zæ™g¢ÑèìììæÍ›‘ú\¼xqýúõAX–•H$üñM›6%“É………ÚÚÚH$RÌ}À°J§–QL³  Zgøx(bIež¡>;íkØë{ãØø…ÀÒ©ž?Æ”"ê·&§Âá0Ã0Îs"Ë2ÇqÑh‹Å`0xãÆ;wîÐ4‰DPÝ…eÙL&Ãó<®"››‹D"Ç¡¨b"‘@†Ê0 MÓ©T* NLLàZF°m›ã¸P(@(â8ζí\. ±.rÅ L&ƒ‹nƒÁ ãRŽšŒ¹\å2ŠÅb2™Äq©Š¢Òé4˲øÝýúaY6 ƒAŠ¢8ŽK$XoͲl8 …B•Z4Mߺuëúõë·oß&IEÙÖÛýó"•Jµ¶¶¢<6˲Á`ðÚµkÃÃÓ““Xÿƒ¢™:Ì*V2E5ML 3•Hz<Þ#ï|t¼µs(ÆyëvНÅuìŒ'Ãp–eHŠ˜ ó´¤(`k¶.Éš«¦ÅHÆ"Ygøíç÷ý‹:¢‘eÇ~¥ÒH¨:§ú+ÇCo½{$[š›m?w©vï½ÙòíB^ÎkŠjëE Æy^†² ª¼À¹¼Ì ¥b©T* øÇRÓ4I’~1fàH£dMW]K²ù¢®ép—Cw™´‚w¶¡+è¢"É‚ _.ôýñæÍñT<šK.€ùÖâÄ'úûw_ýÛËmÿ@}û™¾ß„}O\üðѶ#ÿ²0ê›—¥’  X ZøîéºÈŃ/lznllLEQu]Ïår=öتU«¶mÛÖÛÛkF6›]³fÍÈÈH±XDi[4.ÆZϺººÅÅÅŒóŒ IDAT`0¸qãÆ@ €TxÆ ˜úMÓ¦§§9Ž£(ê•W^Äc‰Ä /¼€ôW„‰‰ –e'&&^|ñÅÁÁA$‚ ,//cözxxxýúõ—/_æy>×ÔÔ<ûì³ XQA˜žž®¯¯GU‚ ž~úé'Ÿ|rnn. aFü¯õHTñ„ ¶ª‚m•Tu"8Gq‚ €¢ÑdúÄ©®—ÿí‹£§f¦ˆsú†¤Š`AYV±xݱÔu½Ò­½ ÑIÛ0 ÇŽÙ³s”$IŽÃTØÎëºŽê Ø >lpW]Ó4ôåF]jì çÁµÄJ…rtw?j)*ŠâxUJ’„:•ÿ=:zê?Ò Ž$º9‡àì úŠ;æšè:„Wíè3ú+ßÐÿ7PU•¦éžžžx<Žó(T/Š¢sTÉt?lûû¢Ž,A">ÙáŸÏ±‚9•àÞ9Ùñ‡S®tž5M½$ã9*W’$Ë,›ª(ñ•fà‹da‘þ½‚ßë*þ{öl ˜žžÞ´iÓ74MK$k×®uœ&®_¿þüóÏG"‘åååõë×ûýþd2 …Ð)#‘HÌÎκ\.ǃz^k×®½rå Ž599yðàÁ›7o†ÃáÞÞÞãÇ'“IAêêê.\¸Íf———ý~ÿÙ³g‘‘?òÈ#{ö쨩©©­­e&‹ÕÕÕ+Š2??ßÔÔÔßßϲìW_}µcÇŽmÛ¶Åb±x<¾yóæ………jMä† %–ùÂ2A|1ÐŽ%L À…J¦¥­g{óùCŸŽu-ï<‰‘ªlâ †þíÈeÎЕ½² çžéNçc%IE6éø½ÿÐÃmáì$*¼ß‘…ÿmЮȉóžÃ8ßûõS9¨ð=N"•_ýð¤Ö ÒhŸÏ‡+=îw+ï~Uü]7-M¯,êxÿØéPš²úŸãÌÑÏÛ?i9—γº®¢x¶(uMÖ•’XpÌÀYÙŒdù{htd1Ž4ße¨Òè* ’F·Ÿ»´e_‹“VL••”™(Ë–@.(åïÐhÌFó< ¤B¡ð ªþßÒh°l°-]“5]6-ÕKÕµD*™É¤®~û§K}}ÞC®>÷žÞ¶Í½'×÷Ÿø×ß©«gÙ[—-"Š *(%0Íïi´ –a—mu•Íf¢{›^™œœDM’äG}X–-•Jýýý'Nœ¸zõêáLJ††,ËšššÚ·o_<—$IÓ´‘‘‘W_}5 %“É-[¶<ñÄÇŽK&“ccc»vízî¹ç>øà¬×ŒF£MMMSSSp×cüêÕ« ï½÷Þìì,Ïó¢(NNN¾ÿþû«W¯^³fÍáÇqÙ" 8pÀãñìÞ½»±±í—Ñ$¹¦¦¦¹¹ynn®P(Œ8p`Æ ‡Bº¹¹ÙQªâÁ™ç%%ñ²nK $MQ¡Éôñïöæóo·Ü˜"Ú¾ì'iQ(g‰I’Ùl6—ËQEÓt>ŸÇJ üHÓt.—Ã4M Š¢H’¤( 6ð[’$qÝj2™L¥RØA ÃP…‡;=g2<–$Ét:N§ñp†aR©¶Ìår$Iâ6Ã0Éd›år9'6†a†Éçó8MÓ,ËÒ4ÉdX–ÅN0lçãýúI§Ó ð,K’$I’^2™Ä+ÍfÓét2™$‚eYQñ\ðR©†Í²,³BAQT2™t¹\“““ù|>›ÍⲕOÑƒŽ·Š•‰BA(„BžX.¾ëêòýà“ã­ó!ÊꓱüÛŸµýá”+Ãp†¡ %k£M•4¹$œlt%Þ¹÷p‡çÂää\d1þÖ[oÃh´U]ÛóëÆCoý]d):56{Î=P·ÿÔ›-ßÎÓU–40ø²6+`6Ú¡ÑB©(IjÞa*šçù•›F?CS–J†i©š¡˜`¨zÙ´ *—)‹ùåðÍÀׯný´!ôÅ>%z²KÀå¡l ¶`  è†f«:(š)(6È¢ÈÄbQgܶm\>ˆÉ3EQ–––4M[XX`$IJ$Nb óÓ˜à¡izff†$I‚ ŠÅ"#ÎÍÍ% Çj; â¼¹ªªXÙ‹Å"‘ÈÒÒ6À_ èrœJ¥‚eÙ4Íb±844´zõêP(„´“Ø~vv6‹I’T,mÛžŸŸO¥RwîÜ!IR×u4O§ÓŽb 6†gòIR$Í\Š%G¯ Q‰´ÀÒ-m=ÿÞ÷vËíî¡…ƒkm÷µquº½^¯×çóùý~ŸÏçñxÚÛÛ[[[qgww·ßïïììt»Ý>Ÿ¯««Ëív»ÝnŸÏçóùœíŽŽ¿ßßÝÝ{º»»ÏŸ?ßÙÙÙÓÓÓÝÝÝÕÕåñxÜn·ÇãÁžq¥««Ëï÷ûý~l€;±g—Ëår¹¼^ogggGG‡ÏçÃíÎÎN¯×ÛÕÕå|ݯ׋‚}===^¯×år¹Ýnƒ¿_?¸{ø|>üÊï÷Ÿ?OÄívwtt`¨^¯÷žø»V(ðþ8pàôéÓ>ŸïôéÓøHT¶Á+‰7âAÅYÅÊÆÉ“-mmm§Ï´Ÿim?ÛöñÇÿyôƒON{zÃdi‰3&cù£Ÿ·;K …ŸÌ3Ù¢ˆK E‰/–8YˆÉ)V4',Âg=^ Çî¡ÑÖÝ)µü‹½ŠŠïiôôøœËóµC£sŠh€åÐh‰“ï¡Ñ¨yçÐè•[ ¶ ºlheÀ°A“Ê‚šf«6@¹,ºÂÁ„g®fã#P^%&j ¤h&è¶VÖMSÐUCÐmÁQ3‹”mÐMK,ßDÁôÐÆ„’NUUq-MÓøêb­*ÖR€¦i8·.Š"ÇqxšÎj‡d[–…{°7Ã0pÉ|¹\. hŽÚ…<Ïã$¾¢(ßÏ“„ÃáÝ»wÓ4­( rkÈ9Õ 1<¬‚Å6’¦i?>S_ÅÏLEљœ祲¬èÙxZfyšLzÒ³m÷»gæ®ÌÑ¿^ˆ¤‰•§š¦)Šr’ÊA`¶8›Íf2LÍrGÓ4A˜w¤(ª2aŒË Y–åy÷c²ÖIB3 ƒ9]‚ 0mŒéLüŠ©È(S…Ùn‚ ðXŠ¢p-#Ã0$æ;qôl6›ÏçY–Å£œ43æ¼ ‚pRÔxv÷ëG ƒÁ3eY6›ÍâYàEÀ=©T #t‚dÆIÒ¯H¤R©¥¥%·Û===ÏçÓé4¦œ©Š çbñ ã­beB’Ó´•’Häìí¯×÷þ?vƳ˜‘F£RG¶  RÒèBY)•EQâKbAQ$Õ´FGÈâ=4úÈ‘ßciV%®ÖúÿÊñС#¯/,†oÝœépõ×í?uäÔeÌF«¶ÎJÊí“@d%…+°¥[.^Ežçyžwø4ª­Hmé`–-°lòÕ(Ëœj•³yÚ´dQÛ[[4”ŒmQЦÅècZ¼ %Ãæ¤2Àª&im¯Y¼a—mÓúÞu œjQ–e­»2€¤7œ–øS˜ºFÓL S…‰aQEQ4M+•JVÅê¨l6ë\¤ì¨3€££ï1Ž«iZ&“­,EE· §}*•ÂåVNŸ²,ózŸEÑ53Î@ÿž’Îήª®]µûÛ¿ï÷ajÝÏYÿ±„Ú,¾OvLVnI×3w«ë:&Þe&üÒi€€î“zÿr?fé3ÙŠÌ“ˆ«ÙûþS$I:æ£ÉÉÉÇ›KÌà;ä¾»œEÿs`6•’PŽœ>ÝôÁŽ]ŸüÚâÝ1ùÎxdûç‡÷:Š'TUfØø›Jp3)Mq|’A-©#f}äØù¾>›ÓáÉÒè,~Š?mÙúo#c£}·P]Wßê SÑtJPÄ(—ìwD" ð±¤gâÉ#'ã õÐhÖ|ú¦Ñ÷>7 %°²Â £‚¬º ¥u1¥ª²¡§59™4 Q†fTH(ä 4­Ã”AYóê00£¬¬Š*€¤€”V°t0>Šè(ÇóÑÃŽä"“6 #‰@&Й˜töuóÉ㒠†O§?9x`Üœ‰s Atbrzrâ‹C§QÔq±—:ØtÑ  tõç³âÌ·í2pK„ðE² Y–qâ~Ë}Ã-¶7g%"#„›¬ ¡Ç…"®æ3ÁÎÉçfú‹Rs<ôƒú!TÞ|íæÅ'gn>(¹–GXqéøñã˜÷}í#³ŒÉ"‹ÿv€ªêª”B“SÖ¿·eÛç‡O:¦8þÞ®Û×ðYýÑ©FQÒ3LÌ¥'–NòL2Ár3(êHÉ #ž—¥ÑY< þT·óÃqÊ3xgµÑ„F« Ó¼à(ÓqyEáøx2A§ôLŒeÙx<Ç‘F³,küÿòþ-4Z‘tPe…çâ:¢ÌhN+² i *€A;M]W㩈œ‰(Hi3$h ‚:LL¦u¿ Ȫ˜J뀢êðcwAŒÌLCfnd´dfJ$†a`€2<ÆÈÐÈ„ñð’‘ïÚoá!Ó4mÖCA&¡(J,cÜ‹dû! †Ä›ÑÐ ãpää5MCÚM˜=ža!Ø ;ÁCâŽI–øä„ÖNA79‘× zýÌ7w B< ÏÌD“¼l@‚‚nˆ2žü²á̺ê‹[÷ݹtÇÿåéó ^•5g¼^/JÞ}>ŸÕj¥(ʼ|Ò4 •]x¿ˆÚ8’áAlðc:‹ñËááa\%’ÏQ÷CÇF8ƱlÕȬŸ×ëEwmb~B^,uBÈâ¡¡(€¦ƒnÄ¢ô©Sg¶mÿøË#§]á¤;&[ƒì‡{øúÌD„ÐY>áÒ‘d*©©¢*%…úFKªTs1p¬bètxˆá‚ÌÔĵfñ¤Ñ hQ.IMËáHs²Ìr3<¢æ8:8Ž{„£Ñ’h*º *ZB”$4À0@àužUCS3LŒgY^HJŒKh  jz*ÁU=@ë Ój8)EMÒ$Ò² è&†DP’$d'8‹÷(œKo6›yá®1 z™¯7•JI’„­y¤­„ðŽ/[Zž““³lٲ‚‚‚¢¢¢µk× $=æÌQ²ÂÅdôެÇŽ;ðmcµZW¯^ûæ§€eY2&'&&ÊÊÊŠŠŠÊÊÊÊËËËÊÊ,XðÞ{ïa•{’€K@–e‘;Îwß}?¿víZ}}=MÓñxܼK&“ZË=‡Ã_}õÕÄÄ„(ŠW®\Y¾|yYYYiiiNNÎêÕ«[ZZ°Ã0§¶¶vÍš5EEE………K—.ÍËË›?~ee%¦5cKòMþÔˆ0‹,~ªªÜ£ÑÑéÈñã'k>øhÿW'D£ÑðŽW4¼3ûFgit‰ÒhÔË{ÂéPìåW"É8–_‰ÇãÄð.§R©G•F+2ˆ) :Š–ÐAÐ @÷û¡):è÷ÞìÛ÷Ùþ¯t´uÞêð{fâ T*¤“².§ CH‰ΖKM `Uð”$‚‰+ã„=¤¶„e"uÀ9FUUB‰I§Ó¢(¢®ƒ„Í¥w‰¢2rì«7㟑èž6CÕàHoxzÈÎIKš¦q‹7Úˆë0ñ÷À2˜Âh>(ÉMÄÍ_ü…"X9œt‹]áw‚œŒÄ¼¤=È  §¦RËIÒ±IŽé@XKŠ3‘ЯšÖU_üð‹Zú'÷Ÿ8;19ã¼öçüÜÜÜææf¯×ëóù<ÝnoiiY·n] @zêt:=Ïàà ×ë%Ù§ÃáèëëÃÜA €®ë###N§½çJKK-K~~>ÚÔ¤Ói—ËUYY¹wïÞêêê~ø—^—.]ª®®à ‚¢(ºÝnY–›››1÷êÕ«ÅÅÅ·o߯p:ÆH'©ÂÍqÖòlii±Ùl½½½.—ËãñtuuUUUÉA__EQ6›¤»»»çÏŸáÂAb±Xkk+!y6›mdddtt ø¡àíûÎþÑÀLå‹/ƒÁ›7o¿ôÒK---7nÜxùå—Ÿ|òÉ7Þxcpp,ŸB¡Ëår8E544<÷Üs³fÍ*//Ÿ˜˜ mðù5o¬e‘Å/ã^®‘¦ƒ¦GÂÓGß²uûgõGD£©ðtˆå9EäÇljot–FgñðøMMˡؽbà1.ŽÑhŽã0éËt1 ƒSË#I£ $Ó‰TYe‘¦ƒÓá©«-ß?z¢­µ½»½ãï–s_îÿ¿l­Ù¿sßùÃû;Fg(QŒH2¨)ÐUM”8QAÖ8YKŽ9Fíž##êRm6ž'‘yà‚œ›ù|>Œ@C&4‹ÿB¶³É™…ÑxŒá]ÃâÁö’$aoD8Gñz½‡ƒ¨ÁÌ8ó 'I’Y0M@äܾ7¿}HÄøa2™ ±XŒ(VEQ4oûš ÷Uâ aé‡ `iZKK¡X¬ãö÷ÏQ•Ù$žDßèmûû®†œ¹ 1Þñ@Þ«ÎËËÃBÆ$–´DSð®®®={ö¬Y³fñâÅ•••ýýý{áÖ­[555¥¥¥›7onoo»l±X¶lÙ²jÕªuëÖ=ztΜ9½½½7n´X,SSS‰DJJJZ[[ ¿ýö[ðù|k×®íëëóûýË—/¯¨¨hllÜ·o_[[Û®]»nݺuûöíÍ›7çääTVV:š¦ÏŸ?_UUµxñ⪪ªææf̘˜˜X±bE Ó(/,,ôx<8fúúú6mÚTQQ±aÆ‹/¢ä`ãÆ999UUUwïÞõûý›6mr:š¦ ÕÕÕ½ýöÛëׯ·X,Ä-籂¦i(ê (ª³³377÷…^˜œœ ‡Ã%%%Ï<óLAAšob¾ùòËÊÊæÎÛÔÔ$‚ f%[VΑÅo‚™FG§#ÇŽ¨Þ¶:~!åW²4:‹ß‡ß¬æ…$Š:0ªiVÁ}di4€ª®ëŠ*ªj@ä¸hWûµ£ ®±Qkÿ–sMŸìÚa¼ôøwºÏžÚ³qó¾-uß[ÚÓ“¢Jk P“:€¬¤‰5@ñøœÿþñމ ˜Xïäääž={|>¡§‰D%È(8Öu= }úé§øõ’ZÇ(Û ² †{ ÃH§ÓÇMMMíÙ³˯ÊÁ`°¶¶vÇŽ^¯#ÄÈ×±Ò;ÖCžžž®­­}ÿý÷£Ñh"‘ÀHÎv(óÀø7þ%¾"`Ž‹C&ÆŒ¡eŒãàA†„CΨQUÕëõ~ôÑG]]]H‘a£ˆaò Ñu¦éŸšR›sA²¸'êÐÒR„aöpŒA ²6 íÿÛÉ©:S»÷öÕÁÐßÎZÂQ>à ½<77??y§‘Éäëìì\²dÉÀÀ@8®««kll¼páB[[[kkkMMM(’$ixxx÷îÝçÎëêêºpáš5k¶nÝÊ0ŒÏç«©©¹råJGGÇ©S§JJJæÌ™355uîܹõë××ÔÔ8N«ÕúÖ[o555-Y²äîÝ»@QTiiéððp8.((xâ‰'^ýõK—.uvv–––vvvz<žÆÆÆ 6œ>}š¢¨íÛ·¯\¹òÚµk½½½—/_Þ¹sgoo/Çq£££åååN§71pk |¢{zOOÏÇ|ìØ±7n\¾|ùwÞÙ¾}ûØØØÉ“' -‹$Iííí .¤(*WWWïÞ½»»»ûúõëÍÍÍ»víŠD"÷-#0 søða—ËuçÎ… >ÿü󇢨§žzjöìÙ.— RÃ0A@§ —ËõÊ+¯<ûì³W®\!]!‡&+íì#œÅCâ>môÉ“§·þå¯_4žzöE¢Ó¼@´ÑYQG¿¿B£}Qu:JRE¾ÑÈ–iaDðQM1Tô{ëÛ4€¤ëIÇèÀ6ÖklˆGBÊÕr©éË϶ \÷º;¦Üßw;R[±½â†·tž>;esC @¹W%% ¼0@µÚú—,)r:í`òx¶Ûíååå6›<–$0ƒ”‘çy‡Ãñæ›oö÷÷ÃOjCFÅáms VUUŸÏ·téÒëׯƒ‰¸S•››»`Á‚¾¾>"@$A lÖßß?þüW_}Õn·#Í5šÈQ]&t–DÙ!#þS|)hä|H·¸B---mnnÆó!설Q’2óUµÉ/ ˆÇ $ãqv&ƧÓ"€ À'¥ Ððnõæoj÷ÞnéŸÖP?t»Ça½ÛÓÙÜÓqî̉]_ìÝpæð†ž“«Ú¼ùÅÆú|óký×7C#= ‰`¨†®ªº&«Š$‹èvûè’¢b{…Âøº\®¢¢"L¢’eÙf³1  ‡‡‡1Ü«ëz (..v»ÝÑT¸Ýn$4è&†Ó˲V«ÕårŒŒè_碢"$Hpý~¿Çã)((ÈÏÏÇm»ÝFÝn·ÃáÇÙËb±¼þú믽ö& išö_ì]iP”wžžJmíLÕNí‡ä[åÎZ«…1¸Š¹º¹¡ihmï¨k³ÙÌŒcâDÍŠ ŠWâ$&(„Vš£hnú ûí÷í“îèû|Ïß~ø«kM’ÙJöSžz?÷ñÝÏÿù?ÏóÃ0lllL¥Rá8þ”Új4‚ B¡^¯GÒ¸ÙlÅqüiƒA­V;:::99ùloŽãn·[­V«T*»Ý޾…L&+++»xñ¢ÕjEUf³Y§Ó!W%<)è@Y»Ýn0]äñµ^¤Ñ">Ÿß½ˆÅB¦X=f|ô ÇA˜½ó³>ú¬úàÇÿ¨l¶Ÿ»þ¹cÆg3;E²%K–lÛ¶---­°°0>>¾¨¨èèÑ£h‚½ÝnÏÍͽtéAA466fff êÄ&¢­­-55599Y¯×[,–;wjµZ†a<O___YYŽã‡#==ýÕW_moo—H$<`¦§§§¤¤ª9Þ IDATD"‘´µµñx<4&%%%>>^.—€R©ÌÏÏïééA6ë‚‚—Ë¥V«ÒÒÒ\.×½{÷’’’d2ØíöÕ«Woß¾ÏçWVVòùü¥K—J¥ÒÑÑQèÁAmÝÝÝ)))yyyhNgAAÁìì,EQ‰y¾…B!òIG"½^_ZZŠ”ûŸhšv¹\W¯^µZ­½½½èÛíöÒÒÒçŸ>>>Þ`0<]á£øÄôôô¶mÛV­Z¥P(†Aþ³Máèþf!÷"ñ­x6b87ëºzõúk'ÿëý‹×þº ©]Œ.â‡á‡D Ãá0ú¿y:œeÙ fY€ S ¶-Ìú©(ËÇ=®®ãØÇ:c€å F‘,G2LÈ„«ú2͈bÖ¢kÿêúkwˆªæQŵ;Ÿ½}í}á£O¶öÿió¹KZÎäþawrØú‚v t$eb ŽF´ZuIq¡jb ž± †üü|›Í‹ÅA³¡¡A*•nß¾½³³ul©Tª‚‚ä2œŸŸïïï—J¥………µµµ(†tk…B!•J…BáÉ“'1 óù|A”••õ÷÷£­Ò‘‘‘³gÏÞ¾}›Ïçóx¼ÉÉIN·{÷¦“'O–••ÕÕÕ=zôÈh48p`åÊ•qqq555&“©««k×®]•••ÅÅÅ»víBƒÊ ‚¨­­‹Å|ðÁ… ,ËÐÐЛo¾)öíÛ700±XÌh4Þ¾}{ß¾}yyyR©T©Tz<žh4j±XöïßߨØxøðá‚‚‚cÇŽ `¶gÏž¸¸¸äää÷Þ{Çñ¡¡¡Ã‡geeUVVÖ××ÛívŸÏçp8:ôÑG8qâÔ©S¹S`‘F„<žHÀï­s.„ŒDé9Û4é ºœSüÓµêƒ_œx°uØ~þƈF'mÚ²iÓ¦öövµZ=::Z[[{âÄ ¥R‰zÁqOJJJNN.((@mh;ÇÐÐD")//çñx/¾øbqqñÔÔ†a"‘Qíh4j6›F£±Ûí)))6lèèè(,,D E›Í¶qãÆ-[¶Èd²¬¬,«Õ:66†ZpÅbjµº¦¦¦££T*•D"Á0Ìjµæçç/]º433“ÏçïØ±#33S­VG"‚ º»»qÇqüÖ­[lhh@s  ‹õõõÕÕÕ‰D¢œœœ^x¡¸¸Øh4 †’’½^ …¢¨¨H£Ñ †²²²žžà8Î`0TUUÿl"q,Ë=9\.ו+WÌfóýÙ–ÍI[’6M;í8Ž—” —,ùž€ït¸XIǼ¾PWWç¬{%77'L^¯ûñï/°À1,GÒT„¤Â, àÉ8¬Çö¸E,âÛð榫‡Ž½ñÞ‡MŽ1\Äÿ¿8þÆ1 7ŒôO<¥ÑO§ºÃÑQlnÖ!w˜ÝAï´> úý~¿ß Ñ$p4ÅúGB£Y† Sœ ‘㷻ϴ7 ’Q?Eú)ÒOÒ>ŠñQŒ—â|$!Fÿ‰\Ôﶵ߹â²Í}Ú¾æÛM¿{û ð“ßïUu^’Ýøí­wÅÊs¼O¶ÿêö±U²ÓÛNW¯ èeÀ,@ÌOFc€/eB‘ J=*™L“õ¿=q†UWW«T*P*• µµµ …¢¯¯/77·¢¢Âl6k4šÜÜ\—Ëåóù=z„Â[‹åþýûµµµåååƒÁårÕÕÕÝ¿ßn·Ëd²S§N™Íf³Ùœ™™‰49¹\þúë¯i4šää䤤$ƒÁ0;;ûòË/‹D¢{÷îéõúÞÞÞòòr‚  źuëÒÒÒär¹V«­®®îîî¶Z­ƒ¡¯¯¯¾¾~jjÊ`0ˆD¢eË–íܹ³««Ëd2I¥Òææf ù\. QoCeeeEEESSÓÞ½{N'Ò³ÅbqqqqCCC]]A8Žgff¦§§£V‹Å’™™©T* Æ6› Çq@°nݺÖÖV£Ñ855uóæMT/CD^^âè$IºÝîááá¼¼<‹Å‚b¸Ès266¦V«ÏŸ?Ÿ‘‘¡Ó馧§³³³ÑWCj´V«EïØÝÝ Ñh½8†a?á•G*eYšåb D(ˆP£rÍÏ~üñÇf£i°¯åòKJL° †—WVýÝßÿrÅË+§§}I£¥ Âq èËØšºöÕ—”C3óhh88¸(@ ðøA€(p°;¸Ÿìi^ÄM³À°@36‹µ±ñò±×ß~·á²ÞÀ(%áBÃÀn/nžu9}o,Œ…ü7R£Ã$å hŠ!¢ÑM×›GF´ÌtâÄÉgIó³ñ³ÅwÒèóFIÙ·€àB(æõ=¢à¨1™¤c±Ø„FÓD°¤!ì Ï›§ b ·ÍÛms3Ö9—e~Þ2·`™ó˜æ=¦9ŸÕå›Å¢,DÃ2ä ºmm×fL¦1ùDïµÖ+§Þ;^|á¤èú»u¿Û›ùß•«nìþçËâ¿9Ï›¼¾ã£C)~í]M\SÃ’ „)ŽXð¸uzUUUÉÐP‹Tš¦N§P(DE&“•––Êd2¯×;>>. u:Íf«®®€ÑÑѼ¼<‡ÃA’¤ÓéLMMåñxv»]­Vggg#sÅÂÂÂÌÌ à8.‰ššš:::***€GDBBB~~¾F£Ñjµ½½½~¿Ÿ¢(‡ÃQUU¥×ëûûûy<ÞŠ+¬VëÜÜœX,nmmµZ­>ìèèàóù‹— 6tvv¢ú‘H„ $è¥Âáðäädjjêš5kZZZt:Ý—_~™——×××OdmmmÈF"—Ë¥R©Éd©®®–ËåápØétŠÅâ¡¡!ć:;;¥R)âUEEEn·ûiUÖ"¾„<Ž"‘¦ØhŒqVˆR~÷ÜÙ®VíÿóÉóÃ_>2ž½ò™k!ŒMyiü¤¤$TWGQÔàààþýûsss‡‡‡)ŠR«Õ¸{÷®ÕjÕjµ]]]o½õªÛÚÚöíÛ×ÞÞ.“É8°bÅŠ­[·Z­V ÃŽ9ÒÓÓ388ØÒÒRVV–˜˜¨ÑhP{Ã7jkke2ºÊ¨IæÎ;GŽA•Z­vóæÍ‰‰‰ˆˆ›L¦ììì¾¾>H … …b||\"‘ˆÅb¹\®×ë[[[/^¼811l6[ii©ÓéD· Ã0Z­öèÑ£èq (êáÇõõõmmmííí»wï~î¹ç²²²ÐÊ-++«µµÕívwwwK$N§Õjkjj>ýôS³Ù¬×ëÛÛÛ_{í5´ þÉ‚ cX–#Y.Â@ˆ‚ 1X—{áÆõ?kTºEYQIÚÆD¹¬CÞÙËËÌú§ùÇÒí¢Ö6E/¿ 4Û8…-x讞ÑÁæøõ+&´ú9w ÊD"¤8X¸(pˆF#íy¤£À‘‹4zß–î c˜›u]»vã?Oýöý‹×¾/ž RÏÒh¤Fczã"^Ä7ñ4:DEü$­³=¡ÇÃÀ=!Ÿ‡ ú~däÈÚRb?Ír@’L˜b#,$qæ} ÞpÐåõÎxý3^ÿ´/0í ¡cÆòzÉh„cH`i†Ž¼³æÎ¶ëÃÃðܘ]ÛÚþé©?¼–ýuÓ¡9Ý Õƒ37Ogü¾ð—ªÿfü“ŒÁÆÒ³{¦Ç?vX?EÅh€ Q¢d Ãtâò³ ³XL¨a×ëõ*•Êœœœ©©©h4ÚÝÝ]^^ŽN£ÅbILLLNN&bdd$-- …u:::Pò†‡‡SRRÒÓÓÍfóìì¬H$B~b$Œ…B!£Ñ(^z饸¸¸Õ«W›L&–e'&&x<ÞÈȈN§+))ÑéthÜ Úm·Z­‹eëÖ­ˆj«Õêµk×&&&æççóùüµk×®Y³Ã0½^¿~ýz”€ÁÁÁôôôÞÞ^äöA¿5333¹¹¹Ë—/ÏÈÈHOO_·nÝúõë[ZZH’«¬¬œ˜˜@ŸöÎ;999jµzvv6##)èƒÉð€ÎOvv62d×ÔÔ †Eýƒƒ9‡5uÄhµFýã&·cÆ·àz÷|“äÀç'Ï· Ú.|zkf.h4X7&nÚ¼y3’oÑ N÷á‡:t]‹æææšššÄÄÄÔÔÔòòò¶¶¶……ðx<_}õUiiiFFÆéÓ§ããã“’’L& ¢I$’ÌÌÌÚÚÚ†††mÛ¶n’æææââb«ÕŠ<îÈ¡ÔÓÓ“ššŠÌôF ¤§§#MDyy9ŠNLL,[¶L$k4šÆÆF‘H´uëÖœœœ––¤¦[,–üüü‘‘Ô‹Å<Ï×_}üøq»ÝNQ ½•—— …Â3gά^½://Ïd2±eË¡P¨Õj•J%Zî’$yÿþýúúzÔïVYYy÷î]tZØŸ(8Šæ(’¡£4¡™ņbl$Æ’ž@0Ûo\¿e3ÏøæBŸß¸µò7ÿ¾iýÆWS~ù«_'nLh—uŒ™ó¯ñËW,3'cQè’®Y»:nõr³yš{œäfŠæ$GE9:Ä0!† °leÃ,e™ËP,Ͱ4÷]“êñ³PÃÄHà`Úá¼xñÔý}iôB˜yÖÔqéêÿ°w­ßMÔëúãùü'¶_@Žˆ\Ë)E®…Z”‹Xº)°±ËíÙÂV9¢´@/¹_Ú„ „Ší©P…–¶ÒKÚ4M2IšÛ$™I&“{æþž/DÖÑ}<û¬íR8yÖ¬~H2óûe’LŸyÏû+**°“’çùï¾ûnãÆEÙl¶ÊÊJlNzÒFºŒ_€y–UžÍçó p2ÌÚ׺¿¢ü¡DŒüìKm݉«§ÎÿxóÇÐËÕp4 ѧN~pêÔ)‚ AÀ>Wmjjòûý’$y½Þ÷Þ{oÙ²e;wît8ØQZ,ívûPÙߨØxâÄ ô8›ŸŸß·oßòåËO:õàÁƒwÞy‡¦éB¡€]}gÏžE 2Ú …ÑÑÑ>ø ™L¢¸ùý÷ß?yò¤ÇãÁ–Ó>úhxx8“ÉøýþÚÚZ”G+Š2>>^[[»bÅŠššš’‰¤ÛíþøãC¡<öMW…¦éÓ§O£ v.—[XXxûí·wïÞ=00Pšv0¬««Û±cÇÄÄ„ÍfkjjÂ&×ÙÙÙ¦¦¦µk×®ZµêðáÃSSSOš<>sK=#œ‚¢2^üÎÁïÇâAI€~xXóÚö×^ß Dxòù<ǵz—6YEEk£ÅŸ$Ôet?ƒ$)b‘¨hL£Ñ55ÿõ¢ÎúÒh¦ {¨ Í–hôؘmÎî*Óè2~Ž¿K£‹2Ÿ,pSîÅBŽÉ£á]4“xÚi4( òŠ$‚¢€(Ê_>ŸGÕ,ËxÃàr¹ÐŽ ¿Ò8æ³`Ê#ž4}!s²œ••”,§%9/*EQxx 829¸xÁè÷%Šyx˜·/ئìsöio6LÎ)‘Ä"áqØß°2Ë2Nטgá!€Pä²²œD^9øù& ²$ˆ’ÈKbQ 6Iø½OJ € HH£ã­×›šÿz^e.Óè2~;üc-†dŠ~ÊE ("H›Ë¥Ò ·}äÎÍ¡o»¾¿­vM_¦z™ÕØQmÕVÝØûàʶ‘îW [¯©ªo[˜ð„9!@Æ©¢@$Eyøû¾<÷Å÷w‡2™ŒÇãlkkëïïO¥Rét[¯påÚét.Z´hùòåh+¶ÿ~ôçºÿ~}}ýÀÀEQ}}}µµµõõõ¨rnll¼uëÖää¤Ùlnii‰F£v»}Û¶m‚ D"‘æææ©©©•+W¾òÊ+^¯íº&''eYVelllË–-A8Ž+V`´¯Óé}Úáp8Žºººššš[·n¹ÝîáááÖÖÖ¡¡¡T*F׬YsäÈ‘ÞÞÞéééÛ·o×ÖÖÚl6T»¶µµMOOŒŒ466Þ¾}ÛívÏÏÏ_¹råÀ¨I­©©±ÙlRÙ”ãW¡@žeãÑH,™¼3úÀó‚“é4CŸ=¯;Ôxí3C7ÆWoÆâ9¡+ò<ßI¼ßC¾‹fçxÇÓét)µ>Nçóy$ÁHpKɔǕV žÌ(Q…atCK¥R¥#ã¯óuüˆ/Ý> ¥ç8®dFÎ0LITãJíÒÚbB</þdjŸzrž¥·†S}2Gg‚A8yxv[]E€, àÕVL$æ2ÃA~|É–!›ËJJ€(äx‘,p!YÎçóø +Ëù\6)ËYQÌ‚¬(2ü÷šôOÅi¬O  ðE€b¹ ]Æ/@’Qm0˜NœüðãsmeQG¿~¥mó$éd9>•f²,•K>å-† ‹Í¡K)….úh´„ ¥-—gDžËI"Ïåò —ÉÄüÞù©ûÃßõ Þ¼t»÷oï·^ú|»Y]Û­Þa½°aȺß5|&ºÅqM'8¹O³l¶ $@îÞ<|ðPMMMUUUCCÃàà`)¸ëÁƒ 333Š¢x<žÝ»wð¯­ûfF[#cÆß+2C ÎùœO’YQÉg¹œŽQ¹|QÅtšåŠYqvÖFD,›ÅâæZ ‚€þ»˜¨‚Œ«e˜ §( üÐ{În·#%,Âa³ÙH’,ÉH’D¾lvvVQ‚ PFÁ`P–åt:9ŸÏ‡ƒ±XŒ —Ë……½©©)TS w/îëv»ñC§( ÷""LMM•ÊL&].—ÏçClhlllÓ¦Mn·'ƒ«ÿÙlV–e,“£Úìv{8ž››Ã¦7e9H’„LºŒÿ °’˜Èdò ˆ„dzçZ>Á"~ëä×ïžî› ÿÒÚq©Ã¨UõZƒV«5jµº½½ÝjµšL&•Je6›;;;-‹J¥R©T===z½Þb±F­Vk2™._¾¬V«/]º¤Óé4Éd2 çÏŸ×jµjµú›o¾éìì´Z­ƒÁd2iµÚÎÎN£Ñ¨Ñhzzz ÃgŸ}ÖÛÛ‹»_¾|¹³³S«ÕZ,–ŽŽ¨»»»££Ãd2F½^‡½råŠV«Õh4/^4™Lf³Y«Õ †®®.‹Å¢V«µZ­V«U©T‹Å`0FN×ÞÞn6›õz}ww÷… :::®^½ªR©ÔjµÕjmiiQ«Õ&“I§ÓiµZ«ÕªÑh, î…ïÏ’Édêèè°Z­úg•ÁØb0~©7^ÔÔz½Q¯ëÒé,:}—ÁØýéçÿÑÔü^›ú¢®Ke´j;uí]VCW—©½íb—ÙØÝeøªÇªQ·wwé zU{ÛÅËÖn£A×~©Íd0w›/_lm7›Í†®6C×CW›Á¬2š´F£Ñhè6,zM·^cÖkÌz­A¯Õéu½N¥×©ôzíï}RÊøÁh4wuY ­A£Õª5§Oÿ­ùß?i7~U6¼+ã·Ã£ø•ÉÑ­¾¯¶þR‰F ±EÞæI2YÈ1ù“¤S *—dØ$h”sà_xZh´ÄA–@¡"Š ˆB€È‹bšçYIÊd%#ˆŒ)^d!  ®Z**Àƒ˜“€“Aä•|"Ÿô°”-MO“S ùA €H‚Hƒ”)+Éœ(KÂAQ@P€dÞôÿlQ²tÝ)Ém …B0•N§ñÙòåéŸ ˜hT(äS…A†©dZ”€Ä L*#?o5ìoèinøz|Ý9%Œ±13;=Ô‰ŒŽŒª¬ÌʬìÌ“çw¾ó•ÍFÍð<¯T*Ù¶íºn¹\6M³P(†Q(*•J¡Ph4ããã¦iæóyz— Çdúr]—¤èjµZ«Õ\×­×ë¦iÒ:ëõz­V¯T*¾ï;ŽS,ã8®T*µZÔ …BE¥R‰iR¾F­V«Ïè;ï†aYÕ¡NMM¹®;>>n6 Z9 ¥RijjжBB8íf¹LRh}bb¢X,š¦Iæo’À''''''ëõz“““–eÑ#.ÉÛ”“C­©J¥}Ü0 š/‹†aPIÝÕj•¤wsVÀªVÁ° †Y2 Ãh¸FÃ7nð\ߺØvï×ÛÎ]ú©æ^*œwÓzÕ,§jF5]ÕÈ6ÂzÅ*O®[ ¯V6W8«}³–” 禖7•/Ÿ+fX(VG‹•qÓ®7LÃ0,£á˜Ó“e6 »a˜ ãÿûˆ´ð÷Ër òê ßv®ŒŽ}òɧë^~ýým{†Ë~Þ–=æ«›·g4Ú ÜIìu1Œb/$‰˜ÒF(ƪî5íWF†ó­ö+-\9k^|ahdøÜ™‹Ÿ|úí’§·®Ûòý@-¨ÆAÈãº\i©#¶lóŒØµ›î޶mû¾oÛ6™g &Û×"æ‰/Ò@ðˆs?N©"!C.=¡|ˆ˜Œ€BB8äÓ*è/#sˆRi(Ì‚t!|ˆ2†dBK¥µ&¥ s­ 5˜õ¸é¼}j¦¿`ÖïWJiÛvww7ÿ‘«•–̤în 4D%_j4>ýbßàØxsH Ë5ë›>Úûøê/o½x¸§øÎ޽u3JB¡åôˆA–>‘ zd}×9çŒ1š§; çœ~V×uil$ê.IOP4EýèÈø{q†a’M¹ù/*ɬÇ1 ‘eïRƒÌéýn24§iÊ9onP3m‘ášÓ62Ït6âAC%Ùb´×ÙwBd%ŒA4W¾R!có}—¾˜ ——1ç’3pÎ4ç©ãWí °ëËM¹KG,3-Eð„Ö)Ò,™¾ü)üã*Š’,oCrÄ>Â@XáGE ž¢.ái„©ð]ßâ<åœ &³I¥Z0)gÉ‘káo­!¥–)ƒ¥©â®]{^|õ÷·í,º£Ë]©oxïã·?ø¬hXœ§×¨Ñ~`»ž†~Ì…è #Ì×Â÷>ùbÙSk¶ïü*—ë¸<Ò¢Ñ-\9«×=?04˜;u~ÛöoÚVlY¿õh–Ýðà # ۦѦo±k»‰ÐŽãûpV5W×"‚ Ã¥c¥S&t"tÂt‘2°i¬âD€§á(å‰VBr% ¨:ØL«Úë2ó´€’PršF+ ­%IщF¤oV¦Eó¢I™næ"Ù-Üh$žçñbqû¾½CW&‚0…s»Q}ÿÃÏ©ø¡î»;÷¹„B³$I¨Ö–1Æ£î*Y±`3Uõ §Åè˘¦I¥ØiZ¡aTB»†¡iš×¤sÐ[Zk:>ô)Û¶³Sš¶Î9oþGø»‡ÒH5R ¹ª€Pð«æàŽ?¼¯ôÔ£‚§œ¨a*Á’XK?¬‡iPR3ÀKDƒñHOE@(.p˜FXh '®˜NI#8 ²ÊÅéIÿW_ 74 Q-WvíÚÓ¹aãÛ|ök4z¼Z+9žÄ~4›:ìD7‚+Õ E£[ø?1gíKk†F†{~îÝþÙaòF5¢zqH7å—Æ;DdljížÕˆ/ð£(¢Û͸®K7‰YA£9 ó”"—H…J%‡fPˆšà)BhÄ@DZ¥JrÉ•¢ë9‡`à1XÉ!8Cš4ePËé>[ZAkh­µ–¿¤Ñ‰ÆMjêÀLjoóë¤@´CÑ’¢o<4"lj<×Ãzà‡LúARŸÒQjTKï}°gÙ³_®}·ûàÙ‰i-x!€ÑÑÑ©©) ž#—Ø-åÒ¸”’fÆXÇZëJ¥â8Uµú¾OéËBZÞ4M4=VщE-àìÙ³tyq]—h«‚Xoš¦Í'qwÌhÃd>111666:::<2:~ºëdéÊ„U¯lùø‹'^8øÒæó‡{Š›÷~e{< E¥T=zôh{{ûwÜq÷Ýw?õÔSß|ó ©ËSSS>ø`­VËú­ÈLD …ÂC=Dißýýý[¶lùù石œ¸ ´ÖqSd‡RêÌ™3<ð@½^¿xñâ[o½•™(H Î$^Ôâž^©×ë´XµZ]ºté½÷Þ;oÞ¼|°­­íá‡^¹reww7×&I¢”ÊDb˲„¾ïSŸ>NZᜟ;wnåÊ•õz=£Å®ë&IÒßßßÑÑÑ××G:B³âN6Ià˜ÉÔSJår¹Õ«W d'v³3d6@)$ Lk­³L: –Óçå]û?êîÏù}cÁ’GßGÈá'Ü x,¥L XNhAň.3LI„>·$,O9|7#Ÿ!|†©ªa‡~ª’RãªB¤É§9”ž÷û-5æ-ü#CkhM©ˆhÔê{öì]ÿʽóáŽß@£G+N3¾pa°E£[ø³˜³îåNJêØþÙá%OoíÜ|äRÅ+‡^$#ˆ.Ž¦ÈŽSǵÇL<Ûu<ϳ,˶m¢ÑäAœ-4šB)‘¦iœ0¦¨Ö1P ‘7EÞÁ¸ƒ!ƒÆmíGR -ç©”\k© 5”Ô‚KÁ”Ìh®!µ’Z(%”fJ§J§ZÇZÇÓ)§šiL¯˜ö:J³Ù="ÐBˆLÒÓZgÃîRÊÌ0ДJÀýWƘ†aº¡ár/4kåMí}ìù¯_ÜÔóÝùÒ¦Ï÷Wê~¥Øxä¡ÿX²dÉ©S§òùüÈÈȉ'ÚÚÚ–,Y’Ë庺ºÚÚÚŽ=š$‰mÛZëb±X©TΟ?OÙϾïŸ:ujáÂ…?ýôE5Ÿ8q‚\Ŧi^½z•)yÀ… öïß¿xñâ“'OR3KZ-½KЏ”’¤bb×.]Êçó´*ZrhhèÎ;ïܹsgOOÏÐÐÐ… ._¾<<<œ­gpp0ŽãÁÁÁ3gÎ;óë^½zµ§§§¿¿éå]]] ,èíí)•JY«—óçÏ/X°€¢'餽xñâåË—s¹\bM[ÌçóýýýÅb1ŸÏÏ›7¯§§‡l-ZkZÛìé¾y©ƒ4ð0®OÕG÷ìß}y¼+œìŽï¼ûÝÞA¸!&ЬR…é¡l ªoà*줒À‰…>}®/’!OŸDw7tE„±DÐ¥•kÄ!)´˜N«V-ÝŸƒÒÔÅp÷îÏ;7l|kë§©©ÃŒd¾î_ŸÝ¢Ñ-\?ÑèëïŒ ê3›i´•úŽçRe!“ž=j4O¡´*N„Ѐ›bdÒý⻟7í<òêæý6xeë7ë7ý±sÓ_ÛrÀ°O44´ç©ÒL!‘™ôbéÆÒO¦H88‡d`±„¯à+xž†íA{@@5…\kMViuóý÷‘y£™IÓ+D,(Þ!£Ñ³Ç3:K a”˵R±X¯uä»B©ê¸!ÉÜÀ¬•ßû`Ï®üÃÚw»÷7}¾²d OüË?ÿëm·ÝFñ‚ÔŒðñÇâ‰'N:µnݺßýîw<òH†¶mŸ>}º½½}Ñ¢EíííGŽ1 c```Æ ·ÞzkGGGooïÉ“'Ÿ|òÉB¡àäÉ“7n\°`Á}÷Ý·|ùrb´kÖ¬™;wî-·Ü²lÙ²cÇŽuvv’Œ†áéÓ§;::ÚÛÛ‰ÝøñÇŸyæ™{î¹§­­mÆ BôõõÍ;—Â"Ã0l.i-—Ë}}}«V­Ú±cÇòåË/^üúë¯÷ööÆq¬”êééY»víÒ¥K.\¸jÕª~ø¶òý÷ßÏŸ?Û¶m+V¬8pàµ4¿téÒ£>JϾïwww¯^½švÿÛo¿¥¶šººº{ì±»îºkåÊ•»víZ´h½Eg~Öæ¯?Ø}#.J„J %¡´€–PšñØÕ¡Ïöì)xgrѪµ?ÞòO¿_ñtîøq¼ðÂáçŸ=¼isÏׇ.ýÏÙJgçþ‘+ˆì#cØøÆ¡‘|0äzì³¹tùòÏW>wðÕß=ÚU1Š+UL"–ðµöµ¡RH¡(ü¨…2ÐzÚÔQ)•3SÇ_ZbøkíWZ4º…ë1gý+£WF.æ.íÜ}ì‘ç>~åãc–°„g— Ž#qÓÔqMß5bߢÎt‹âœ“dø×¡Ñ*³k •ŠkÀ‰ådë¸,%ÝܼûOå'¿øl6‘ãV*Ä\ €ýWª;¿>¾iç‘uoî~òÅW¼ºcÕ›_®~ç`çûß½öÁájƒ%¡€„–‚::à9BŽ˜#åH9GÊxR»R»*›Ô žV£o^­µ&ùùšŽƒt%Ê6clöHt³¡mC«ó‘©É†ãkðmcËÇ_,{öËÎÿÎQû•RÕžø·»þýöÛo§n8qÇq|úôéÞÞ^Ã0öíÛ·dÉ’®®®ÁÁÁÞÞÞ7ÞxãÀÇŽ;~üx{{ûsÏ=W©TvïÞ}ÿý÷:tÈqœ\.7þüÿeïÚŸš:û|ÿ†Ùýug:³³3û¶;u¶ŠÕÖñ•XÈÅ´ F  ´@­¨E jÖ–î«õR/U X(x‘›$„„KCn¹œÜ@br¿œs’“óݾšÒÝ÷Ùygœ÷m—ïä'˜óðœçœC>çû|.Z«ÕÚØØxñâÅ‘‘¥RÙÓÓÓØØ¨P(Ìfó·ß~ûþûï h4š]»v-//Çãq™Löå—_Þ¾}["‘=z” ˆH$¢ÓéÎ;wÿþý¾¾¾±±±K—.9sÆëõZ,–¢¢¢ÁÁÁ´k„ð¾r:[¶lÉÊÊêêêÒh4_|ñE}}½Çã1§OŸ¾víšX,–Ëåuuu‡D"Ñ–-[víÚÕÙÙÙÓÓSYYYUU5??ïr¹²³³ív{4Õétõõõ]]]£££?®®®>~ü¸Ýn×étMMMmmmÓÓÓ7oÞär¹ï½÷žV«Ev8já×ö#ß…¤’eáW¤– Fž;W¯üðƒm™|îƒÿº8·{ßà•kQù3X¿þÔ?þCE!¯µëñ¢Ö‚âV¹ èøÂ0=ÕTÏÄ–¡±Qv ¼ÿúËŠI¸ruê`Íí1ùR’…(‰»$ 1XÍëHýÓ•¬Õÿ¹˜v£ÛÚ~þü̹¿Âðîyˆ"–ýÄRðO7ÚË«·´ui4z“Ѷ£×ê×k'N×Ízͳé–;O‹ß:u]bò1¾d2 O$h÷ÒÏ@‡i:òE"+ñX8{¥0ú<Œ)i! FÎÆ¨p ¬~jÒ¶²”OIH’¤˜MA"ÁÑàŠØ$CÅÍL'ñM$ÆT“}âáAñÐD$”ŽÇ&DʾEóý§ß^¿{ê|ûWW».>8úuûñ >ÿ¾÷ó+ý_^í¶-†e|>ßÈÈðÄÄØè˜X::<"‰%B‘XølR1¿h§$¤¢ñP4¤1œy,J±4ˆ%è@’e†ùÅî*#±µFÓ4 q`!åý¸‘MQ®¶Ãᘜœ”Édf³w»Ý‡C*•vww#_¹Åhz€²-ôc,Ë"_i©°Êx_3Ò¾f³yyyÙáp`c8ùÒÎG…V:ÏВɤ^¯ÇÉ#vÁ€7ø5ÿºÃK…Ê¿pZ­vxxX.—Ó4ír¹pJ±X GÆ„¼ñÒLSDNˆöà%U¯×£‹Ò®Õ^ȯÅiÐ4ý?,Ø~WÅBÄç ùVüѨ7¥Y†bóKÔë÷8¾ÿiÿÑ —Õ½ª¥?µü¼¸ì_^t>tä“O>yôè‘R©ìëëS©T¨…›Í–››ët:†9qâ¦HÎÌÌdffnÞ¼™ ½^/fggiš–Éd¹¹¹³³³‡#//O$á¤Μ9c·ÛS©”\.çñx&“I¯×WWWÏÎÎÆb1™L–ŸŸ¯×ë].WCCAè››k·ÛñêOOOŸ={Öd2ÍÏÏs¹ÜóçÏŽŽŽ‹Åb¥R9::Š#KKK›6m6›$ɲ²2­Vëp8Î;—Îà\ZZÚ¹s'™OLLlÚ´©¼¼Üb±èõú-[¶p¹\³ÙüìٳݻwÛívŠ¢d2‡Ã!gÂáp8ŽV«5 ¹¹¹6›-ŠÅâ½{÷fdd`7š¢(”!þvˆÑBN›ö¥+›dÒˆ;oµ]#ጩÈÃõƒ½¢ÔØ$³=çì?ýóÇ}ôØãù³à¡ªj%ÄhHhf½ŸÕßU.ÈTìæ Ö¯{48îHž…Ô_•«­ @4HKKCŠyášÇ¢´ño½kõwVÉd ØDXXñx[ZîÔž8}éVûŒ^«WW¯ÕŸ:J˜t*¹ævsÿžO~8yeX÷œ~Ó@{"~›'î 0Tˆ¢Á•PÔ‹‚áWGê@Íü Fc(w’¢(’h ¢„?Ù«±Ê—âD¼6‘`!¡’4,$h’M%’$ ý @ØááºSy»?ÌÎÏ.*),­(û¿'»àƒ‚½eÕµ§›~¸úSÿµŸ…—‰›»•—;$U7²wºä~afF¥Ri]]ÝîÝ»322x<^Î˪¯¯ÇÝgD¨/Î…eW;×ú$™v¹Âߺ\.Çþ4Ü€´Ì?§–ÑhMÇ0åA TTTx<ŸÏÇ0ÌÔÔTUUU~~~qqqyy9nI£gœR©¬¬¬LC„H$‚F¿(ÌBЉ¹‰mÓìU F)--œœ$I2‹E"ÜÑF)N, ÍÓÑ}luס*ˆÕøyöhÈ€ºC‰DrìØ±¼¼¼òòò{÷î¥ Á4"]\O\¥´íCzLœ˜L&«­­ÕétiØ“ Ñha7˲8ºÁËlÅßa±@G"ç²;ˆ²)š£É¦”Ê¢^¿ßãºx­½ìð½ãç]rûùÖ_&£IÇÂRWWWEE—ËÍÏϯ©©X\\ĸ"½^ŸJ¥0|[£Ñ100°qãÆwÞyÇápÌÍÍñx<áÉåò’’’™™»Ýþé§ŸÞ»woaaÁjµŽ»\.¼vOž<áóùf³Ùf³Y,Š¢&&&öîÝk4†Á”|U;yòdww·V«Õét …á©V«ÍÈÈàp8ÅÅÅ999ùùù………GŽQ(EY,–‚‚‰D‚ë!‰òóóÕj5I’+++ …s7;::JKK- ôôôðx<±XL’¤Ýnß¶mÛºuëL&“ÓéÄc@(r¹ÜÞÞÞ`0øðáÃõë×oܸÑjµjµÚÂÂÂÉÉI…B½½½™™™V«5mæˆ÷ÞoGJûa4ÅøV¢öæÎks6ÂÑ„óãCíc*PLÃæ?6¼Ÿu«÷ „b0&s«y"“0ÉÄ“ ‘Úly6½¢PÃúÿèݰNú°Æg¡[´¸³è¨znÁŠ&’ñ¯R40LÚ)ɾø¬á—µZ]ØðBíu{ZZî|VêâͶ5RÇZ½ºúFßú±owõã‡f—ãË‘ ´;쳺cN’ ’¤?à F<ј?zuÃ?£_|6•§ÀL´ )5Ý<ü]ÛÉ«÷;†&gl(€Ÿ„ph€@4áÇè“L1,@ X½ÉX}èÓö»³Úû<¡ÕO*''ÔSJƒ‘PMÏuÞïùâ«‹›ÿ˜Ÿ•WšóAE&·43¯,ûƒ¶rKþó½š=zÊEQ˜‰-‰6lØpéÒ%ŸÏg·ÛÇÇÇ~ar5j›à%#Óív;ÎH$2??al@Ó´Ãá Âï÷ã—(I’,Ëêtº™™Ì’ˆk•JåÔÔ”Õju»Ý(Åbño¼ñÖ[o ‡B¡ÑÑÑË—/?zôó´ÛÚÚššš†††&êõz„;`2™P>˜ý/y™077777§R©°­<ïÞ½{i˜L&C|€â*H¥RÖ‡ ‚ I2L<I’2™L©TâKBú¼‚0˜?gµZq4½^ÿèÑ£–––žž‚ 4ÍÇ\SS#—˱[‰DpÙq© ¦Á0ŒÛí¶X,øf Ÿ>}ºk×.Œ7 ápxuB‡Óé\XX0 jµšeÙtK~5ÿ] @ÓñpÈ -®xQÒ»\2ÏCœNÃh4¼;ßÚá%$»ìpÚl6£Ñh4———»ººŠŠŠ>üðC‹Åb4ù|>^8Ç3<<\WWWPPÀãñ^ýu‡ã÷û±­«R©@*•òx<¥R 2™ììÙ³\.W <ýX,¦×ëQ•HQ>qèQóÛñvL¦ –’eqê@ÍBtÑm¼ùÓÕ³‰PLÇö nN(atþý œ­?=“C( wMåà 1¸]‚vJJšÕ3ì ˆywCÿ¿þKGÆ{×ví»¾gÿ÷{ËÏ©f\ÉP$††}Á!Yey·†_Öju½h•$„ÑÍÍ­G7^¸qgMb¸V¯®^;qú3£YŸîFŸ¸,Òº(W,Fõ<´’†ÑqŸß»#Ñ`(òJ ïV1:~Eê “ àRÝ£š3×;O|ßyº¹÷Èw_Üìµêý` ƒÉ FgÜêŠP4 $iÊb1íÙS433¤ã:îKÁƒ±ZðG̦yŸ/¶ä š¬N­~‘°¸fõö›ÍE{Ë—pbh’…ý`£Ñ¸oß¾¾¾>X¥„{òäÉ™3gx<^AAÁW_}5>>ŽŠD¢šššææf>Ÿ_QQñôéS›Ív÷îݪªªÊÊÊÙÙÙ´dJ"‘9r¤¤¤¤««+ dU*Uqq1—Ëåóùûöí …‹‹‹ÕÕÕo¿ýöúõëKKKûûûkjjzzzâñ8MÓˆ³ Å¥K— f#óx~ü˜ÏçWUU‰Åbü¡P(ܱcG{{{EEÅ:::µH$@•••——W__?<<Œ¯«Ûä¿·b!àv3ép»¤’9ÂÌÍ&BQô.;|:ηv,¹‚®%ïîÂ=eeeAà‹ŠÝn÷Ýwsrr¬V«ÑhDV:fàˆÉdºpáÂæÍ›·oß®V« Cyy¹Íf‹D"R©”Ïç£ÄÐëõêt:«ÕŠÒÃãÇÁ0Ìàà`vv¶Á`°Ûí%%%Z­FGG·nÝŠO„Ùlöx<øÄ©ÕjN§R©Ôju{{û¡C‡\.×ÌÌLVVVúéÃ-ˆd2‰DP"Éçó±ùH$h1;;ët:Á;w‚0™LMMM\.í"‘ˆËå>|øL&ÓÖ­[·oß®ÕjµZ-dzX,4MŒŒtuua³Ù¤RéÐÐMÓAp¹\£Ñˆ_´R©âAÕ| IDAT4//»Ñi³m” ü-¢’)§ ÌͲ,¤`Ù0ÚêÐ5ÿüã¬ÑN±0­ƒ=Å7§æ@"õo_^÷Æ¥Á>ˆ0=ëý¤¢Í0~7„W`¨òsZFÇèÑ1ÈÉìûÿµvõÁ˜ $rV2XzL ¼žp>¾¸¸ØÙÙyúôi‡ÃAQÔ•+W†‡‡I’Ôjµ …¢­­ÍjµŽ¿ñÆëÖ­{úô)I’555yxŠìd¤J˲N§³¾¾ÞápX,–¢¢¢’’­V»¼¼ì÷ûÛÚÚäry6›Õh4gΜyôèMÓJ¥²···³³ÓétÖÖÖÞ»wÏn·+Š#GŽÌÍ͹Ýn‚ ~úé§ÖÖV—ËEÄ›o¾Éçó;;;§§§—––ššš\.W0ß±cÇž={~üñÇÅÅÅúúú¶¶¶……“ÉtôèÑÙÙY’$>|ØÒÒ²}ûv­V‹ÜS>Ÿ$¯×‹#‹™Íæ\.g2™.^¼xç΋ŢR©¤RépØ|ðàÁÖÖV½^/•J/_¾¬V«C¡Ðððð† šššîß¿íDCCC(òù|­­­OŸ>¥izvvvxxøÝwß5ù0ê?fq ²©d Qèu”ÕLeÞå¨o9èsÿýú·-Gîž¹"RzЩÃjYÚ»g_CCÃôô´ÑhÔëõ£££•••8„¦(ª¶¶vpp0‹i4l„¤RéáÇ_}õU>ŸïóùÌfseeå“'OR©šßY­V­V{éÒ¥7nØl6»Ý>::Š–ñx\.— ‚¹¹9$u‘Ëåðášššúè£H’ôx<A\¿~]¡PÐ4m³ÙúúúЄ¢¨²²²ÞÞ^|qǃ$IÜg°Ùl‰„ äíÈd2‘H¤T*­Vë&&&(Š?~ü8dzÛí ÃLNN–——wttÌÍÍ=zôH$íÝ»×b±!‰Ôj5vJ¥F£qttôƒ>¸xñ¢Éd²ÛíÓÓÓjµz`` ®®®°°P¯×ÇqùDñ|Ë}er°’ƒeb0?ëü8–&ËÅìêûþ %0ÈžF*—GFaf J·Ý..¼9; €Íì<ßÿd„SLÃä?ø¬šßýp(èò‰c³ûöNLÀÐ8Èá›Ûjƒ!½² ƒt‚ýWÍeÍB& nUf¸Z/ÔK„Ñ«ñ+«õë•ãgŽè ê¹)ùͯúÅ¿8óÉhžýïHÿÑ0ð_Áè9 –ΦT´çý®§¯üC½n€asòÃî¹úº+~.:uSròzíŸÿçÏg/?˜Plþ`œeR)Æ ÓWðK=KàréT"™ˆÅ¢ád<î’ÀÇäò¨l:c¡å‘¡»$õZ ¹OqŒ¤T*…BáÄÄDžO¬ÕjwîÜ922‚Œ[«Õ*‹‘¡P(¶mÛ&•J}>ŸÁ`¨ªªÚ´iÓàà Ïç#Irÿþý###,Ëz<žºº:ƒÁà÷û'''1-" ÍÎÎâʃAN'‰fff4Mii)ŸÏ7 ÍÍÍ.—+3 “—!²,‹sn£ÑˆGF·eËë*•ÊòòrdQÓ4]]ýsÏÀ²ìÊÊŠÑhôù|N§³®®nxxihh IO1;;+‹ççç­VëîÝ»?ýôÓÅÅE¤[”––"gtlllãÆ<Èd2f³¹¸¸˜ÇãAQTIIÉââ"Æ^ –——»Ýî@ ÀãñÖ®];77/(3™ JZq ˜GºEI$š¦U*Õúõëׯ_???Ïqœ\.Ç]ø‰‰‰ªªª¡¡! IrÆ ÕÕÕJ¥R©Tâ(Òé´Ýn¯ªªB|‹ÅþÀÓh6™ üñL& Xx¦‘>ôÛÑ•À•Ϻw¾sòã™ç'=½•TÐy¦Xìêê’H$•••Hxèîîž››[^^&¢  `÷îÝ*•Š¢¨~ø¡ªªªµµõÂ… úÓŸ6mÚäñxŒFãš5kZ[[5M__ßÎ;M&S2™:räÈÖ­[AUUUoo/Çq‰DB&“½þúë;wœäóù.—+‘Høýþ¾¾¾¦¦&¡PØßßÁ+‘H¤§§G ðx¼‚‚‚ŽŽŽ»wï†Ãaš¦×®]ûÚk¯UUUmÞ¼¹¬¬¬¨¨¨¥¥%¯ÿ‹Å:.s700€-_(’J¥{öìY¿~½D"9sæŒH$òxL&“ÝÝÝb±¸¢¢âСC×®]+--Õjµˆžóüß?££¤Ðë³¥Ò}ûN·tE“03/(<±oß?eã°áÍ‹;¶ÿ0B’MÇ2ÑRõ©£?ð‹ÿz¢ýQ×yûŽ¢k³óÑ`ä øê–mÿAiiÅåã'ûzû,vDBNÂ/FÑÇå “& Ì*Œ^­ëe‘:VÃÀWë·×+'ÿÒa0jåÓ _ÞºqúÚc$uü;‰ap%FÃáðÊÊ j Q7öò ï~áX—ã²+I&”Ò»üíýsŸßÑ{§í©Î÷>:t¹ÿý›c»-»ôÝxç éÅ/zû+ô”×îZIe€Içl´]\ß`6‘.—'‘Î1¤XFÓÄŒGâÙT ’)6°±+ÈdŸNWUòÍf &/ï#a±X†††p§ÌfsMM Ruqh*‰´Zm(zðàD"A#[Š¢x<ÞÆÈ’$¹uëÖÉÉI–e ECCƒÕjÍ«óî …b```xxø»ï¾«®®Æ¹rIIIqq±N§ÃLüƒðzÂóT9@F¦Éd2 ÅÅÅÛ¶mC2è³gÏÄb±L&Ëf³KKK~øáÀÀÀÈÈÈØØz¸\.Fsøða™LÇNgMMÍ­[·?~<==}óæÍ¢¢¢`0HN%qqH’lllÄ=úÙÙÙ¦¦&§Ó :®´´´¬¬ G†B¡Ðf³¡vÓf³ÕÖÖ"í¤   ¼¼n>µŽ{žäÌ0L4U(2™lbbâÖ­[|>ßd2étº;vÔÔÔ þöûýMæçç«««‘Ä¢V«7oÞ\[[KQ.ZOOÏäääàààíÛ· úÃøµÈA"Z²Ò¾P( Ìr“S³÷ïöº)[$è¿úù·;îüxæþœíj÷á‹Žé …B$ìÚµËh4âm·Û÷íÛ×ÒÒòäÉt˜Ù»woccã£G:;;;æt:qêߨØ(—ËU*Õ•+WÌfs"‘ðx<]]]EEEÅÅÅ x‡¤Ói­V{ìØ±ööö'Ožœ;w.&“I–e-Ëššš¼^/Æ rg·ÛwíÚU^^þÖ[o:uÊjµ²,ëv»÷ìÙSXXX^^ÎçóKJJvìØ j»ºº’ÍfÇÆÆÎ;‡¶z>Ÿ¯½½Çã=zôñãÇ]]]&“ árWW×ÔÔT]]]SSÂqŽã–––.\¸àñxð³$I¶µµ½ýöÛ …"ãëÂív·´´´··ÏÌÌ\ºtÉçóaï·}ü]q££9sà€ùWqºí7n}e´¸\>8|äëö#= 8|¨ûÄÉž%W&’]J‚Ëá Ÿ>õoãþsg8y¬×·œõEb 3òPDZïKJÿzôxw( é p9p»‚[…Ñ«õ›êeI WRˆ[¼ñU½Zÿg½röü ÒB,Ϋ¾þÇPSû—y§Ž dü±k å ç~6¼‹&–“©àJ8‹!—ÇÒ¨ÙúÁhî9Œf¢9ð§áÞãÙ3W¾þð‹{'®|×ññ·¹ùðΔU}ÐA ý™XbÅ3i†# æ†ú&šrÄSå‰8Â9_  ,§!˜o–i»/î ¥âYXŽÄ^o |,©oŒzxÔ{µZ-‘HpFË0L<'IR(NMM!öÍÓ‘ ¿¿×®]È¡Ôét%%%%%%:.N›L¦††äXkµZ‰D‚ª&xž †HtïÞ½ÍÍÍÕÕÕeeeJ¥ñ÷Ö­[)Š2b±˜¢(Ô-!  ‡Ãn· 44X,6›ÍATTT#Æ¥iº¶¶©)nÜÜÜ\WW' q ÿ‘ÚÚZ¤Ó4½iÓ¦ÊÊÊ––‘HTYYYUUe³Ù´ZmccãØØ^¹J¥B ˲SSS555$I"o¤°°°°°Ðn·Ó4]__Ÿ'ÌX,‰DB’¤ÉdÚ¼ysuu5MÓyÚ(˲‡Kr¹œN§;}ú´X,nll,+++++³Ûíƒaûöí………¸_ÜYŸÏ‡áÒØ6hµÚŠŠ ‡g[¶l©¨¨@Þ¹H$‰D.—+NãêýïÞÿîâ §ã±`,fóûBñT<Á.‘ö»—®}ѳÿdßÙ«Š¾YëÇßÜY‰dXR‰t:&‚¦i”c¢Ð“eYœâÛl6ĵÙléΨÄu8ØÑQE’$n^aw„¥ÑhÐY¹ÎØõ% Fc6›€ËåÂÞp7Ãjµâó•Ífñ~@OÅÅE¿ß >€-Ah4¥Réõz"‘žo~4=dÆn·çO ‡R©d&¯hôûýEÅb1‡Ã¡×ëQ ‹1›Í¸x¤ÙlÖëõ.— Þ|£J¥Br9ö¥ðÜ(÷²ò]âûFC†C /iŽM3ñ•¿çû;„™†á`Aöú!ǘ͉,@ìþ„–P©ÝjUÔ¨ŸÌ– é,D–áXì.x8b£íK@$Ñh²LæçÌBt».l29ȬzG¯Ö‹õ² ïñ,é ç%†_û“B¡5–U½Z¿®WNœ}¹Ñ·¾yØÔþå™OFµî:ux#Ë*‹×¹ÌdãÙÄr0Oy"Ñp$–Wâ4½&^*ŒÎþFDz\  s…îÊ/Þúñìµo?¸Þ3¼@½é(@PCL’ $’ÙH4Åd€PëÌ‚ºÆ©Y•Õ5Xµ‡}¶”^t2ZoNçãÔKÌ3:ªuÆU¶y9i]I=ˤßohHÐ(Ñ xa\”·˜ÓÓÓùäê………ÒÒR¹\Ž?ó …B ØívŒ« …E±,KŸÏçóùA {WMML&C‘H„¿²yhât:<833³°° R©®^½ZXXèñxpªbŠ¢öíÛ7>>Ž^Èx…ÃÃ×.]BWi½^ãjŠ¢6nÜÈçóõz}.—“Ëå;wîDí÷û1¬X¡P¸\®‡¶µµÑ4íp8***X¢Ñh„B!ú$)“Éž>}Šn ¸ ñxÜív‹Åb4ôœœDc†a4MIIɺuë,‹F£H}N$“““•••4M“$ùÎ;ï…B©TŠ žù_ö®¬¹‰kÝÞŸ”·Ô!÷!Nå’!˜`HB 7  Á uB„L’‡*„ÑØ’°À` ±ƒc0'lÍRËÖèÖ,õ¤nu÷îý݇Ïî8cåTÝ ‡Òª.W[jµº·lÕÚß^ßZšÇoݺuäÈ‘x<žÉdÞzë­o¾ù& cÇŽ­[·.N‡Ãá^x¡®® ϦSÊÀÀ@cc#¦‡¸ÝîgŸ}¶®®ÎårÅãñU«Võôôø|>4÷¸víšI‰~$…r@¡˜NK\9™Ív÷Ýô3sÄСR(—ré¯ÏÙÿwO×Á¯&»G#§/_OÌ—ÀC§K™"!ÄäаHÑB~è ^à ƒ.(íœ:–,ÆâLëùs~& T¨’º¢ ¢h¨TÓ!]¡qI­¨:”JÀó ˆ T4’R(’BU*ÕÍ„ª­´ ‹¬}Á4¨Ä€ZÊi ?¥ ª:©ªXnm=¿ç‡>?ÑòKÕèh6—,qyI,K/9¾ÀóeNª 6Úuœ·tOMyjÚè~ÿuàýiôR§ ´B… $ËYžj¢&ds%QÎI•2'ˆ¢ˆìÅ»¥R©Z­þn4È"® “ŽršgžŸI þ4Ï–eY  -º¯" RUdQÖ{Bk^Ý<9Ãxå>gòº3u}2q}2qcr¾Ç9ÿ“ýΙºõ(qýaàæThÀ=;1Ë‹Â廃«6nôD@)Å‚V8nhhÒ4 Õá©TêðáÃmmmÉdÒçó]ºtiÿþý¡PHUÕ‡nذE~¿Ù²eË–-Cž …^~ùe,Ê:ν{÷Þ½{š9âóùÜn÷Ûo¿ÝÕÕ•H$FGGÑM,‹ù|¾çŸþ™gž¹sç6x}þùç7nÜH&“ÉdòêÕ«ÇG¡v¡Pðù|»víòz½X'~ýõ×>þÊ+¯<÷Üs×®]CÏæ£Göõõ¥Óéééi»Ý~àÀlÝklllooÇ~Ç}ûöÙíöT*Åóü·ß~{ðàA,û­Y³íÆ!ápí`tt7ðf±}Ðï÷OOOoß¾½··×çóݾ}ûµ×^ÃC`¦»»ûäÉ“]]]N§óþýûŸ}öÙÑ£GQg2<<¼k×®`0888xàÀ§Ÿ~zzzÓ:V¯^=66vãÆ'N¸\.´|óÍ7Ñàlvvvùòå+V¬p¹\±XìàÁƒ6›-•Jy½ÞÞÞÞ¶¶¶ùùy3>úÉ…ª (¢-—ç2é\Yà9ÅÄ”?Ÿûò뎷ö^9Úâ½=iîîñø£ÉXº˜/e2v 2™ ö§f³ÙD"Áq6 …l6›©áÉE:“cÙR*ͱ,Dz\f¾”™/dØ|†MÇ¢ ö]ìlŸö8cóñPt–-™L4>W(yAÎäÒqÖÌÙ\~>-¥3$“ÕsE)[N&s㳩áÄü\.'¤Ò…T&ŸÊγÙ›‹°9&¤³s™t2æÓl>ÍØtžMgÙLšÍÌÿÙCRÃ_ ¥W*q96Í&’Ó]mm>9öåYË_’ ´‰HîÈW-H£u]-ó¥T©œ«("ÑR•¡"óŠR©£F£køíø™øtêÐAçTi6[)V@5.)‰rAV8^üQü îü¢ÓFš”D¹¬h¼"i!Yª ¨ª)QD0T0TM+îAX ñ·¦S·Ïõ»çœ‰"#h–Û÷ëÖ{‚ ‚ÀbÞŠ2›šš†††–^÷½{÷Ž9²fÍš†††÷ß``å¹CCC›6mÂ5âX,¶uëÖ­[·¢±ÆÜÜÜž={P2A)íëëÛ¾}{SSSoo/VU`dddÿþý {öì±Ûí7nt:>ŸoëÖ­O=õTSSºÒº\®>ø`íÚµëׯߵkÖ†±dèr¹öìÙƒâìL&ÓÚÚzàÀ;vX,–¦¦&¬Ôªªêt:wîÜY_____ìØ1¤Å¡PíÐ9dbbâwÞY±bÅòåË?ùä—Ë%B$9|ø°),ŽD"HÁÀápìÛ·Ÿ ‡ÃÛ¶m[½z5j]&&&¶lÙ²jÕª?þøÒ¥KëÖ­CCkT? :thÆ ;wîìîî¸DŽý‡Z·nÝŽ;Ž;¶{÷n”§ûýþ[·nmÙ²å½÷ÞÁ‹ܶmNTâñx}}ýæÍ›Y–UUµ¿¿ÿÓO?­««{ñÅwïÞÝÓÓƒ5WMÓ–zK?Q  s\9Ÿ›Ï竼¬Fc©‘þÁ2›å Y¬FxòQ×PèÐñS6ûNkWÇ‹Åb±Z­V«Õb±´·····777Ûív»Ýn³Ùš››Ïž=ÛÙÙi«áÉ…ÕvÉb¹Ön¹né¸n±\³u\µu\êì°wZ¬mçÎÚ¬öï{÷ô™ãíÖæsNÙ»­.¶4·œ²Ù,»[Z¬§Îœnm»Ðn¹ÞÞq³¥¥§¹¥»Ãb³_i¶]þ¢µý‹ŽöK׮ܵÚ:­mVû9›ý´Í~Âf?a³Ÿ¶už³ÚÎ[­V«Ånµ\²ZìVëE«ÕjµvüÙCRÃ_ --m—/_¹|±Óz¡½µ¹åŸÿüüÐÇǾj±ùS`¿=ÚÕ?jéé·öö_ìëÿôÄ©ú×_g¢1Sm¶a™¢hŽfF¨˜uz3÷w°ùoé}šìPƒJS?”¿©ç„ëðæ˜ÉÊ_UUSÖd0î[m\NI¨ŒS ØðÆñ‡€p8 Ÿ ::à8|UðãÀ¥hsܪmj+ÁK˜¾_?ü;c²>Ujk¤* á3¼+åE1õÂRu0Þ¼ ÀÛ9õMÓ„®ø^àK’e9µm4¯ƒqÖ‘Ÿ´ˆ¼8X@EŽŽ†c1VR#Ø«×y2 î9xjÍû_Õí½w±ÓwèÜ7øh,i€Ðh¸¹¹¹¦¦¦¨¨¨¨¨¨¦¦¦¹¹9 Æãñ¡¡¡ 6¸\®Ô'ø"÷¨#°€™ñ0L`X@³ Œ¶âÀ´@PAº©á¬}ƒG_’$ôÞ»[°0ï_þùÕ]Wáñ  ·\9S¶æ­Ì¼Œ•+׿ä,ZS¾¢µíLÒŠ Å¥Ð~¨Y¿àQ­Y@²€„d4b<–LóA‹a8:~üÄuŸüÇþ£(ðñô˜T÷q­Ûãêïjh¼Vöþ¡ºƒ×í•T”HqÃæç8h¼®s|LäiU$‘çyx7UÂòDM?ÇC톉±ªð¬¼S—ó×݃ËGîB ^ , ³,£I† ²Œ¨ÅEI {–¢³QòóCMÏmÿ¬ëj_èÀWȨð‡—-«®®niiõ+°1Ýwuuܹs'U·I’d(²Ùlðx^†Ãa†a`s$À4M¯×ëóùl6<[ÆlEÑhÔï÷;N§Ó Ï̤Úàá$†ù¢šm&ÖXçÊþYX€¡Ù£GŽÙlvì{ÏËËù§ü‡¿ÿ»ÉK‹ßv¹F|~wþ¼´7fµµ_ÇýÁË—¯¦¥ÍÉ›—‰.fÂzT=ÿ ?Úü¿¼oÄÄà×Ö¯ø©hXxC— E”XAddYÔIT¿‚øåLÚöá#.罎£ —W¾w°î`«’#š,ÇÕ¨(÷»hZ2£h,Ù˜&<;@ ~Py^øQŠ:ÒtÊæÄT-’@¤bùüPÓš÷¿Ú¶ûîÕ¾Ðþ3Í£asú‹Þ*ÎÍÍÅ0 ºÏNgQQѼyóZ[[«««§L™²fÍX`‰DJKK322`&#À4MÇ÷ìÙs÷îÝ?þ¸¶¶Öï÷˲\\\œ™™Y\\\QQËu]ÕÕÕYYYóæÍ«ªª‚§/  jü|ê³Ý~ˆŸCÓ4š¦Oœ8“=@iiéþð‡iÓ¦M:µ¬¬Ìçó9ά¬¬ÜÜ\·Û­(ÊÍ›7ÓÓÓ333Ýn7@Zñ$øµeà^2äFUUä…ËEE‘—u•#~9“¶îø“}ÄÑ}»ïËc-¥ÿv î`«#ªPº" %H}Îh”RLV–Z呌F &*YVæ¹@$røÔI›3âHƒ—’ÑûNM÷ˆ/knöŒ3`™Ž,Ë4M×ÕÕmÙ²¥¯¯¯¤¤ä•W^Y·n]GG˲/^,))ÉÏÏ/))©¯¯‡Ýœ‡###ã“O>Y¶lÙ§Ÿ~j³Ù.]ºTPPðÚk¯åååmܸñÊ•+0G¥½½}ãÆEEE .,,,ää䤥¥Ùl¶p8¼téÒK—.Áhó`0˜ŸŸßÒÒû\êëë `fKEEEww7A¡Phhh®PÂñY@rŒg»ý!ž:uÊçóÁ¬‡ÃáõzÓÒÒ&Ož\PP€ãx ˜;w.’ш§Ç”Ñ®07^F÷õÙ‘ŒFü$“>ü¤ózú»†¯–½ÊhR•Ô¤“Õw,&…UuŽg$ŽÑŸáˆ!ø}X@á8‘e”D¢c Ÿ•ÔÅðdŒ ST(°ï‹3ë¶^LÉhA6ãš’ÃÃÃ.— .!_¿~}ÕªUï½÷^0Äq|É’%Ð {ùòåeË–ÙíöD"áóùfΜ9oÞ<·Ûít:W¬Xã8Œµ ™™™999«V­ªªªZ¼xñêÕ«].àæÍ›uuuÐÑQYYyëÖ-˜f“Ê7|‘Û%'>Éd’¢¨sçÎÁs0ÈãñÌ™3gÊ”)K—.Åqð7Þx#++ ™:O‰'eêˆ)I%¦F hîérØÝHF#eRíÎíN·«·sðØñ+ãW£•„FKÊøCFâЈ!1±€‹‰,Ã)Ê·7ÿ‡bèNJ*C…÷}q¦jó×Û?ëjé ìm:OÅ:­­ücuuµË傱AdeeÍž=Çñááᢢ"‚ ×®]«¨¨°Ûíñx|dd$//¯¸¸Øãñx½ÞÅ‹§êîûúú–/_~á Ãm6Û7b±X<w8AÑÙÙ¹qãÆÔ‚4Ž!>Óm‡x0óôìÙ³†1m³ÙfÍš5eÊ”ââbŸÏ‡aØÜ¹s3331 C#†ˆ§Á“1Œ)I," ø%<1ì¹ÓÿЈ!ôFß÷°ŒdFQ6ÊÇ(™E2˜¨X@EM)ŽóEȘ K²NúF ^˜è¾/Δלݶûî•ÞàÞ¦ópݬtUqqñ­[·Ün·Ífkoo_¹reee¥Ãá IrÉ’%·oßfÆn·—••utt8ÎŽŽŽÒÒÒåË—s·hÑ"Çá){ÇW­ZÕÝÝír¹ÛÚÚvíÚ…ã8EQ»víúöÛo½^/I’­­­UUUðÔ*¦v<Ûí‡x Ç{öìììì×_}úôéW¯^¥( xÀ˜CømH’’LZÀ´@"IøñÆÆ“µ;ÿ²kïGPpFõg¸nwýlÆØx\±4¬_‰J"+q¼DFQ$ň³š…EWHøüèÙª Û¿<~¾«kpÄÕÕ}dYl4—!þ6yÔñhn4”Ñ® a!Æ¥(Í)BDbXž“$ ªgY–áÊ4’ÑÄÀM+Ïʲd&e#9xß~µù¿£D&ƒ{ëOWmþúÑ2ðÎÎÎòòòœœœœœœòòòÎÎN@"‘ðz½•••k×®ŒŒŒ¬_¿>;;{Ó¦M===º®Çãñ@ °uëV ÆȲ<<<\^^žŸŸ¿}ûvÇwE===µµµ‹-Z¸paii©ÇãI­Cà z0Ö‚‰x1M“a˜ÆÆF蕇}–‘H¤¬¬,;;{óæÍ°fðåË—/X° ++ëí·ß^½z5|<îMžå{@¼0X¶=Ú°uÇÇ=t|$$ºhã®;òÑžÃ=t<Äp‰„Áò ¥ƒœSd^D‰%VUe-‘d5 &u ø?y8©cÇkãG £ É]0D* e4/ )õœj‚eàâia1³â†’H¸F¢—ƒ RÀHò1êà—çÖniÞñyÏ™ŽÝg\ž¡šVèºîr¹ü~¿ßïw¹\º®§¼Aô÷÷‡B!صŽaØ;w0 “$)Õo«X`ä3ÜëôööööööõõƒAØSÐuÇñÁÁÁ»Ý>>”#µ—BIÏ3‘H¤¡¡Áçó™¦ ‹-“É$üL ‚0M~Ö0†Åáp`É’Éd*ø=FBeU”RIµ;ÿRâ¼3,aL¢ÛÝùù‘Ýõa–O&ãœÀâQ:,H¬¦Šš$É”ÑzÒdTs¼Œ>Úøuw÷}çˆÉhÄ£<Ñ©C(£#šü¨ŒfežcPFs7^F#o41° ‰,ƒ‡Ã®}‹á£IÝÔXŽVl<÷á¾ÞK]øÑo®R1åA93º®Ã¼¹ñs~š¦¥n„]ƒ©»dYÖuÊh–eáÀ™ªª°ý;¥† ð,K’$žçÇÿ8$‘HÀgŽÇãðÒ4Í$⹇Oœ8 /áQ“eYš¦Áë‰Df¦îc“ˆ†a¨ª oÖï1±0ó.i DÂä±cÇ·lßùïû¾ ‰PFÿyï—)o4/rPFsº&é²$sÐÔ¡%’PF;ƒüC2úÃw‚qáŒPF›&j¤ÿ›fÒö¶¸œ©Ã®9¢JÔP¡Œ†¦Žñ«ÑœÀCo4ÇqªªÂë° Éhâ¹ÆÀ4š*Æ}ÌÍ+º¢Æ¥('DhŽŽì­?]^sö£ý}ßÜõïm:ß;àôa¿§iš¦i†a†×a6°Çã ‡ÃðˆZ’¤H$B’$A‘H„¢(MÓH’ŒF£E‘$ɲ,¼"Šb  I2 žçišŽÅb<ÏSåóùp§išeÙh4Fáï¥(ЦiQÄs‰ßï·Ûí ÝÝÝ@  Åb1‚ Âá0AEý/{çþÔÄÝïñþýÑ™N;U§ÓN°*Ðo\ôà¥#•G[mÕzçiŸãØ£ÇÇ*Eb€pÕ¢b¡¢UD’{v“Í=!÷ì-Éîf“ïùáS÷pžžãÓΙ©—g_?0Ùew“ì†ìûûáýy=MÓ‰D" Úl6£ÑèõzN'EQ@ ‰D£Ñçý&d^z\.O  úfü¯^«S©:þúíßê.vÜ$  I©OÒ1›?à%i©Å0F††J©p\Ä|1³‡<ÓÜ-ÉhÌLüƒŒá.Ëèqþ[F_P\¯ØY2:$$“i>šà~Ýb£HŠ¢"‘I’Pp¢(J®FËȼd‰xv*™L"”‘NoûûfebáúæKŸ|yùÈÙ‰Î!Óߵ¶÷v´õtwötww·µµ555555µµµuwwwuu555µ¶¶^¿~½««ëûï¿ommmoookkS(ÍÍÍ …¢»»»±±±¾¾¾³³³··W¡P(•ÊÎÎÎË—/+Š–––îîîK—.Á. …¢£££««K¥Rµ··÷ôôÀA”J%|¥455]¸pA©T*d^H”Je[[ÛÁƒZ[[U*UOOOWW×Õ«WU*ÕÅ‹›››ÛÚÚT*UWW×¥K—T*UGGGsssccc]]B¡hii‘¯¯Ìÿ“ ÝÝí]ªöVeËñã'=vòüE½+†‡…ÇDèë³Í'Ï_tÂÀ‘tŒ˜ñû(†ø¸ ™(ÞÅya¶ŒÞöÙ!¼“¦_ù-Êf³m^;ú—#¸Õ¢n¾ø£$£ƒ|‚G©X’·ø¸ ‰R¬G¨$ŒÇHš‚ 4MÓ¢(ò<ϲ¬ì–‘y È x, ¢È!”Q(Lº0[&ÎE¾³Ÿ|yù›†éëãÎ3ªžÑq­^c61 ÃLÙíp80 3™LV«ÕãñØl6 Âðz½^¯&®3™LAø|>¯×k·Û!)Ïårá8IÒ8ŽõK›Í¦×맦¦ ƒÃáp»Ý‹E§ÓÙl6§Óér¹زaG»Ì ‰Á`S(÷îÝÓh4:Î`0à8n0à¢ûý~‚ ôz½Á`°Ûí†Y­V·Û ÝëõÂ'çy¿™—›Íátº ·˜Ì+Êc'NŸWvܤ5*ªí‘oÎ)NÕ+]Á°( 4K3þšeÓbRä6&U£Cl ïfËèÙw²Œ–‘xí›ã_vÛ“qÝÅ–0ul€‹ó(Er‚u†QHŒ§‘(ͱ08x¡Ç¦Ëd2²Œ–‘yÑÉ žaRÉD ³x=&«=ƒ AàÝ¡3ãWFˆÆË}NO$“B(ƒ$àì~p²²,+™\¥{ ¸œyžÃ4xš3™ |Qd2)ÝŒã8hX„ÂxjsÌÌŽBs­Ì‹‰Åbioo‡´o4K[d2™h4Šžþï[ò°Â¢t­á£ò¼^¼Ì+C&ƒÒ¼€2(0ãooïzôÇñééi³Ùl³Ùìv;Bˆ¢(8±‘HD§Óét:Ø7‘HHÕkF£V«¥‹4¸’‘ù§ü"hS¢$£÷¨ýÛ÷m¿WFa¶ŒVª®LLèÌ&«,£e~ͳd4ŧŒN†L &Ì2ÁP G8š¤)¸Ñ4MÓ44éó¾?vû^Wƒâòöý׿9í?£ê¹qkø›Ú//]·fÍšU«VÍŸ?Ó¦MÅÅÅ«W¯>pà€Z­®¯¯ŸžžF±, BôMÓ°ÈqMÓ ­¥à3„(бXLªMÆãñ[·níÙ³§¤¤déÒ¥åååUUU?N$8Ž=zôرc8ŽÏ¾W0¥i¬ P´†#Cý;NCA*ij[é1 Û¶m[aaaIIÉÚµk×­[W]]½wïÞ;wîø|>Ø^J¹†7’N§AÕI¯AÒî³£ú¤‚+œQÃápMMÝnUýj{(-Kgg'†a©T*lذ¡¸¸æÓ)..Þ±c‡Óé„1ƃ*++ßÿýµkךL¦h4 H„«W¯VVVVTTÀq`{9RZæ7òËúSÝÒÒ¶÷àŸ¿»Ðþ{et8.Züî£AFƒ©ÃhÀe-ókž%£cI^g#cqÄFâñp$DEÂI*F‘v2š¢(–eAe´ŒÌ‹N¥“É$C‡iš˜ñ…)6&=O2¯«©åÊŸ^ßwòaß#GÃ¥kn_ljB«×ôzý7JJJîÞ½k0 , ƒ:¤(jffÆçó9§Ó™J¥@GB"zªb‰D*•‚Ú¤V«…ÖdQFãáÇøáè Äq|xxxç΋!tçλwï"„R©ÌcµZ¡h Z–ã¸T*F ‚P«Õ jãñ8øq ‚°Z­ÓÓÓÑhÔ˜Ñh\ºté½{÷,‹ÉdÂ0ìÉ“'W®\9vìØÈÈÈh–eµZ-†aZ­ÄŸ”…l·Û ‚À0L§ÓÑ4-ÍOÎqœÙl&Âãñ€-$ŒŒäææ>|øÐëõ‰zWý@·ÛÝ×ׇaX8X¸paNNÎèè¨Ýnw8RáÇñÂÂÂwÞygîܹK—.5™L0VÑëõ½½½¥¥¥o¾ùfVV–ÝnO¥R0R’e´ÌoD3¡Œ’dô¾C_Ÿmîø½2:’H[´e†‘dôø¸Æ Çd-ókžå1ñIs0D¡x4Á“T„‰ã1YFËȼ¬dÏ0,³{½ªÞŒx£Š Íxꚺ?ýêêÓcWGmu]½\ ¡4Bi„2EEE&“ #‚Íf;qâI’.—k÷îÝÇ¿yóæ–-[***ÀJ144´aÆO>ùÄd2A—!BÈãñ”––æååUWWkµZš¦†Ñjµ«V­’dV,Óh4åååccc8Žïß¿ÿðáÃV«Õl6Ÿ;wnddäàÁƒK–,*/zz›ššZ¿~;-} IDATý¦M›À6bÚf³mÙ²¥  `Ó¦MOž?õÓ¤÷?*§'¢EÇñââb³Ù,´îÙl¶‚‚ ÂÁ`NNÎ{ï½wöìÙõë×———_¾|ùþýû§NZ½zõºuëúúúBïsûöí5kÖäåå­\¹²¾¾~||œeY—ËUVV¦ÕjBàРiúÛo¿õz½fÙ²eü±Á`˜˜˜(,,øàƒ ÔÕÕMMM NLLÀÀ& µ··WTTÌ;÷í·ß^´h‘Ãá@MNNæççÏ™3'77÷­·ÞÊÊÊšœœ"·ÊüvdS‡ÌϳLa6ñ‡i&¸)·ÊȼÜdP:™LÐÍqF‡=)"6ÎSþH2J%òL}û†?©ö|x}Üy®ó‡P‰Bšã8»Ý^QQa±XDQ„ .Žã;vìP«Õfûöí¯¿þzUUÕÐÐÐÐÐÐÚµk׬YS[[{ÿþýýû÷C%xxxøÐ¡C7oÞôù|CCC7nܲe ´ú}ñÅ===.l±XräHssóêÕ«,X°hÑ"˜sdddûöíeee---999Ë–-ÓëõR–âì)èedžÜb(óÇó,Mr‚ÁA“ ÄFâl(¢"Ž–ïdd^V2ˆg˜ÐŒ/s¥ÒéÍÿéçˆg&àu}×б}ÿµ¯ëŸ ¨=§[»ü!–ŠÆ3"â8Îjµ–”” „9›L¦’’°½eee #„‚(++«¯¯¬ÓéV¬XQ?ÿüsQQÑO?ýd4oß¾½pá¼¼<8æøøøÞ½{KKK +**víÚ¥ÑhAÀ0lÅŠ}ô‘Ñh´Ùl«V­F£ÉdÒb±”••ù|>›Í¶dÉ’œœ»ÝÎó¼ÑhD%“ÉÛ·o———_»vÍf³ ¾û/†i_òóósrrªªªÊÊÊ V®\¹k×®¡¡!Q9޳X,ÅÅÅ7oþ{gE½ïq_}ñͲÔŸ´Ê*õÙ4&dBB9 àf¸ÀÜ€W®hÈ:‰j"H¼CB$ KÖÉl=“Ùz™îžé™^§gíÿ}øéT.¸è=%ËéÏÃT§»ùõJõ·ýýÿ~ìv»Ùl>~üxAAÁøø8Ïó¥¥¥gΜµX,ÇY,ðõ^»v-??¿»»ÛáptttL›6íÍ7ßœ˜˜p¹\K–,Dɲü{|ÁñÒÚÚêñxnܸ‘——W\\|ñâÅÞÞÞ·Þzë™gžÉÊÊòûý.\ظqcoooQQÑÓO?=mÚ4 à B  Âb±LŸ>}Á‚n·;m:×M:¿½àΟÈc•{waNÇÐÀXZF[X…‰* ” Ê‹W¢C(!'%6Vèb¨( Ø9dYƒG*•Òe´ŽÎƒŽ†Ä`PxN’<,–UY‰1êFW7´þeóéÊC7;®M6œþÞƒ¡ýŠ$I‡£¤¤ìËЇÅjµ.\¸ ™AúÐëõ"„Gaaa__Ôwóù| ¿UU^¸paAAAaaaNNNFFFyy9dŽÇÆÆ(вÙlv»Ýï÷÷ôô¬]»ÖívÛl¶7Þx#33ÓårY­Ö¼¼¼[·nÁCËf³-]ºÔétšÍæùóç¿þúë>ŸOUÕtæ²££#77777·¸¸¸  `ÆŒ6lðz½ç»ï¾3›Í££££££o¿ýö±cÇ8Žƒèv»óòò–/_^ZZZTT”——·råÊ›7o"„úûû+**JJJ œˆÙl¯Â¹sç²³³—/_^VV¶xñâ™3gVTTÀ8ÈÕ«W›L&I’Ð#ݦFy¶µµa&AषX,eeeÏ=÷ܬY³Ö¬YÓÐÐpñâż¼¼gŸ}vÞ¼yhJùp’$333§OŸÎXôh{Êuþ‰hÒ4„RÒK3'N´íü>¯?b£${ 6âãwh¨9Üæ¡Y„RÐ œäE!“cŠ réfà91ÕÔÙh«Å‘n¿òëæ4¤Ëèyî(£c(ÁвÙ#ú9ãÍpR˜–8.F*Š"”|Òe´ŽÎƒŽ†â²œP#NŸ¯ëRïÈ„5×Rr ©qÑk6µï®¹õý€ÛøíYÂÏ£J%´d2ɲlyy94¨-h2™JKK­V«Éd*((ÈÎΆô3Žãeeeà…M$4M¯Zµ êu\ºt)??ÿÂ… 8ŽÛíöÓ§O Ȳl³Ù>øà ÃÒ%>l6[QQÔôÈÉÉÉÊÊ2›Í&“©¤¤ÄårÅb1AÌfsQQ‘Óé[°`Avv¶ÝnG¹\.¨¸×××—••õã?Úl6’${{{»»»UUµZ­ÅÅÅPª4÷¹sç6nÜÈqTÒðz½¹¹¹'Nœ˜˜˜p»Ýׯ_?þ¼ ±XìæÍ›ÐÝétvvv®_¿^.]º´téÒöööññq³Ù|ùòåîîn„øÂáp Ð]X–5ãã㊢$ f÷ÉÉÉ+V<õÔSsçÎííí÷üüùóçÌ™ó /<ÿüó•••>ŸeY({7222gΜ™3gbï¡û}d:(•BZþøã™™™CCCÝÝÝýýý§NÊÈÈxñÅ_{íµ“'O‹üR••••‘‘ár¹ F‡ªªÐæFGç^д_d4EÇŽØUµÿ`cËd´›aiI‘’ 5•ä04WI]FëÜ;w3u„"Q®”ŠhJT)¨ ¢,ɲ Žôk‡0=­£ó !%Cœ ‰xLCl ì¶8P$bý5Æ“k6µïüòú·}ŽºS<ˆ’iHUÕ±±±eË–A¥MӒɤÕj]±b†a$IΞ={Ñ¢EP,Ùëõ÷ôôÀÇÇÇ‹‹‹=ªª&“é£>úþûïøá‡£G †M›6QŲ짟~ÚÙÙyùòå+W®\½zµ§§gß¾}<Ï;ιsçfee²²È!‡Ã‘ŸŸo2™¼^ï®]» ÃùóçÛÚÚ6oÞÌqœ$I}}}ƒ¡³³óêÕ«mmmëÖ­Û»w/I’###ÙÙÙ>Ÿ/Ã[†a;wî<|ø°¢( Ã`¶}ûöÎÎÎK—.uvvÖÕÕUUU9{ûöíÕÕÕ·nÝêêêÚºuk @  †®®®ÁÁÁööö-[¶ ÇcµZ—,YÒÚÚê÷û¡vÛýºò>Ÿ¯¹¹Ùï÷›ÍæòòòW_}µ¥¥¥¶¶vÚ´iO>ùä{ï½G¤ÿÍfóâÅ‹Ÿxâ‰yóæA¥h«Î0ŒÓé|å•W^~ùeø¶7›.Stî‘T %“¼£)ÿñã­w—Ñ–’‰HB•ä°žÖùünÍ‹‚ Á`úA›Y–u­£ó £¡”ªòÁ@XQ¤ESÈfŸìï½Êþt6zWõ ÈFûÈÔŽD"v»}çÎA@s¾h4:>>^QQ1::êv»ËÊÊÖ¯_ïñxX–…JÏ?ýô“$IÇmÛ¶ R‘H¤««Ë`0¬X±¢¢¢âìÙ³,˨»«W¯nݺµ¬¬¬¨¨¨°°°´´ôÂ… °Ý5kÖlذÁáp ¯_¿ºrD"‘ÉÉÉ÷ß"[,–¶¶¶ 6TVVöôôD£Qk2™V¯^½jÕª­[·¶¶¶‚Ãl6WUUÑ4îªÈq\SSTÁ‹D"çÏŸ7 ¹¹¹û÷ïïë냼õ•+WöìÙS\\¼hÑ¢ŠŠŠPÆš¦ŒŒlß¾}öìÙëÖ­ëéé¡i!d³Ù 7mÚtãÆ „(ÂG„ÏóGŽ1™LE¤KBEQ…r»Ý‹ð`0(Š"œ‡Ãár¹àp ìÝ# Çq,˶··Ã» BÈáp`æõzM&AñxÒÿš¦É²LÓ´Çã†+µž µ?44¡D"¡ªj2™|äKnëü“Ñd£[ZŽß]F;)¿>ÄPçÿÉïbÈ‹˜:A ݹt­£ó £!%VóxþþsŸÕéNiHSâ(¡Ñ„7-£Ï z¿9ãÁƒ©8R•hº <- × âq#„4Mãy惊M&“0b/ƒEQUU–eѯžX,–H$`$Yº\$Q>sÅb1UUÁ6–⪪¦Û¦€ÒE1½4]›|iq#:n{àÅb1Ø=øI `?Ó¡B0–ýÚÓa:~*• ‡ÃSm¾éGl4ó,z$A¹\®ššðާOf2™„‰ôi˜©ª*\}xù¹­°]bʹ½ßǧópJ¡D"•P£ZÄPçñXåÞ˜Ó>40öõÑÿ^ù^}eýß-ŒÊ¨j%‚Šlñ‰t8]ðNb xžç8Žã8Q¡à¦1ÔÑyðÑP€$S±¨[½*Š'ïŠL¥p(x·»æÖ·}Ž/·›m^ž“¹@Çq’$EQ ²,³,ë÷ûY–¥(* ñ<Ï0Œ Çùý~˜ _êišöûý ð, ¿’$ùý~’$)ŠòûýÁ`a˜@ €ã¸ "Ó4­( ˲Pˆã¸P(DÇq>ŸjÕONN놱Ûí<ÏH‡B!EA ÃÀæ(Š"I’a‹Å‚ã8´þEÑãñÀR–eƒÁ l×ëõÂ4ŽãPžHE’$%I"‚¦iš¦q'ÂçóÅb1ÇÝn7Ã0$I’$q‡V¯V«öí‘„$I ÃNž<911a|®„ã%" QÅqœaÇ#‘MÓÁ`Δå†ÓWÓãñÐ4Í0 Ü0÷ûøtB,˲”?Älksóá]Uûk·ý/ýõ‰ß1o´>ÄPçðØ¿ïx|bd°ïÆW_w•+ëz,L”QUUS¹ˆ„Q2㸗؉0’È…xQC¡P(‡õl´ŽÎC€†"û¬¦¦úÐÁuÕµinnþäà¡Oj›¬$o¨jOuãÆ¯É—Hª!>àaDX*2¯¢¥P$"«‰dHÕ ‹¡.£uþOÛµw›Ýa9Òr~¥¡yWm™Žù%’ŠÞFðþp2*DE†Í RZ=Cûpê2ZGçAGCb0¨Åÿ‡½3ûŽã¸Î8þLËʃ“8/Ñb[±e‰¤(Æy°¢cqßI¦¨…”lQ"KÇNŽ%qDš$ÖÁ`!€Yz­½ºz­<\N±9@O€œC ïïi–êo溾¹uëÞTåùìÊ2O2©ÒhÕ³iyÝÇ>ø§_|üÆÁ.~Ó~ûÜã·gî/tz>ì­{žÂ0„=úu‘~ x¥ÛínJ§ H' Ô!oÞêõz  Y0užŒNîÏ ¹.½^¯×ë5éÄq†¡› ð ;rþ¬ êl­Îúý~°qŒw3®êOÝÌ©?ݬ!„sôº^gY æûþÿ|wóÝ‹ŸÏtÙ\”Þ˜ïïÝìícgº$.JCX¸ä]&âDóDIÀF›¢ŒtY·Ñ§Î}rãÆ™é6Úhd-lôøµ[§Ï^q6º¯õZ Ýcl4¤D;mŒA ÛÊ£¥n÷ÒÕ+sK+Ei­)“˜yåýGÏÿôß.¾yøö®/¸t¹ç [ZÁ¤BJ)åƒpÂ6 „PJ%I’$‰R ®BpÎ7¥Ó„ÖÎ*¥ÄÆcÌ=UJÁ9H­5ê<÷W†¾¶”RBHÇM:Y–%Iâ&<€I¯Ôg‘¢é{¢ÎÖê4çœC3Ñ4M¡0.êî RJ­µÖºþ p—l\G)•¦‰bTÐÚ²ªªîN}ïÃéýæão;Ó‰£¢4”G`£‰I„‘BHêHòlôÌ*²Ñ{öìµµjE`£‡ŽÆ"Ïc¯í}ezvâæ·ã'OñÜ ÇwüzÊË|cÀFCRG=M(‡ÜhWð.Žc(W„6A¶5•µe™'Z¤é¹UFéLø„õƒØï<~áŸ_ù’:]¸´¸”™åôy¢”Âg!ü㯠ƒR°ÊÂUlÀuFè+¥´Öpú1æÖrðpð",¨ðePçIê°GiÒç=bzðÚ,ñ=QgkušÆóAù‡ Ý‹¤†rÎÁ—óÁQ«ú€ÍêB„`’AB[ÖÚéöÒñß<¹·BÑè·ŽœZ ƒ¼H(ú^‡rš™ªz¥ŽP³]2³Jwò‚³Ñ³3óC6ª¡~Æyh£Oœºü£=6:HÓ¤Jâ„·ºÜcÕƒ#†ÊxBÊcQQJ!ÆÀÃh4‚ì*+¢hui‘%IbmRØ{3×þò·åÙ¶KêØs`üÓï]¸4Ûîš0òÀhA².”R°ÈY–srÈMé4Á9[&¥„È·âRBˆ¨¹[_“c€:IÇ90¸Ü…*›t¬µyžC´ÚäƒÔ¥T–e`݆"ÜuPgkušÆsÎaª@àþèõùæ.WJcÜÎ6Y“4²)k-c„Ç‘‘,ÏLUU“­…wN¾;µJæI~k9úÍáSo;³e•JÍ=ß“ZÛ*­2©(Ô6E 6zz…lÄFçƒ>AÈ³ÉØ/w½<9}÷æ·ã§Î|ùÜ Ç_?ðÕÝŽ^å<³™Ç£;ó^'Êr™Ë Œ¸Z eÂMwwcͲ m4‚lw*+ãXs¶Üïú§«óËÊZ›Vq§O‚þcüägîzçæÅoÚ‡.\š_òŒ* ©ƒÕ’ ›N·ìÁÍÖ6w‰3ÐÔ¡ïÎuEÜ ƒ,^ÔÙ¶:õÉ04CØ£³¨Iu¶\g] ËÂý¸‚Ÿ^àèà"ä„8S ã7¥Ã9'$bQÈã 5ºªª©¹Åcï4¹ÏÉõvïwŽBnt–kÂÂEÏïPG  8§Ò¤Ø~Ù8c¿~ãÓ³ã×n9wõ^>u£{Je6ƒJ>·…* n4$uÀÆ Tê`Œ•e‰6A¶;•MK‡VJD&aÄ–¦Û9W‘×ÝôüO~öážãŸ][<~ñsHê€h4¬ õÍܦåS Ê5¸K`©sA¦ ê4á¶qøÐ2_w„>êl­Ž›îE%éÌS“>êl¡NÓx>`èiýw—³¿0ÆYaVûõµY)%¥1#AÂ,Mìàˆát‡¶ãìûEßÁ®Rñý ì ¹ÑõJh£‘3öÚÞWfZ“P7úù—Nµ_™ë‰PÚLd´×…ŽS¯ !F@eã~ŸE¡O©±Ö'|aqåú_¿Q!i²Ñ.]_ÃxsÔªéÝzìj#:#X»¬Ž~Œ:ÛPgíhŠnŽþÔÙn:€XSs.„Xk‡‚ÊC6z³:BˆºvÑh´ÑÈããAÃ]®R IDATzRÇDÏt¥Lª$dz…x¬JyJ{ýˆ+OH.Ö‘RBEt¥ÚhÙîT6aÌæY$Ä¢×ï…¤(m´ê¥T4%u&ݪY?‰ß´|ÖßÚlÝ”NP ‚Õv¢‡6|Ýë„0 Qg{êÔç@ý÷U}Î8‘Ñó u¶P§i<«ýÅÙ£³Å]èŒ8[d&œ¸Á›Òფù&QeYNÌÎy÷&u ï 81œè™¾Ö¹Í}O,…=RªA Ý¥ ŽÂÿ­¥”bÁ;ÙTÖ_]…ö+ŸýÕt{1ͪŒé¹ÑP7º¾¼I)]6×Zè  œâ Ym\gÄÚìê;[o9{ÇjõŒQg{ê¸ÂÖÄ5]Ðf,1Mú¨³å:ëB)…´fèíÂseò†¾Ü.T­ùËЀÍêpN‰]4z²µpô½ÑF#±Ýÿþ«V{Ú%uì9ü—i?Ò4·y¤ÙäýÈc•56¥Œé´Þ~…s^–%”˜±Ö¢FíNemšVYš[;µ´hJ[”Ö¦•-ìè¤:hÓÅ쪪jZ>¡FÕPË1—¶q&´ÖЉWÕº~@ŒÊ=VJAÃ^po¨³ uªª‚"kjЦƒjjиª›5é£ÎÖê4wå¼\¡:pºî:H­–µ.†ìÑJî’ë¤iª”HO-‹ÌZ;3ÿÄùO0©y|<´Ñ§Ï^=ÎFϬÒ@Ø*©tCÝèº.ŠÊZE6A¶;•M…$iÊò,·¶Û Ú÷fF1äôaˆH гEÑ´|2Æ´ÖY–AEyw!{tïu#:MË3øoÈ(ãƒDI2H£„N °ê°¨³µ:°vÀ~&{tnÀS)eš¦Y–ðèŒ1ÔÙZ&¤”ð«)Ïó<Ïá·Sý(jÝþB¤y­þèc”š3ÍI‘§"Ÿ±]û^iM~ÿÝ'Oñ£Žì:øÕ”g|c’* oueÀG ¥ µâB !`cÅ#¥„®Bh£d»SYE ünkUV.¯ôþöç¿ö—›’:”Ð|IéŠL°MPä5IX•¡Ç¬|›ÒiÂó<)¥1†„Ó\\ nD„¸5yž‡:ÛSNj㘠²çÁ3ÁœaŒi­Á·Á¶þº ÎÖê4cÇ1Dš¡ÝàºöwtÝèÍêÔ“:R£!7úð¹0©y|ŒíÞ÷z«={ëúÝ3ç®>ÿò‘݇ÿ<Ù×}­5,Kï-’PX©”²Xr_1&nò²Á¶ æF#È ²"Šbß‹¥\£~D…4Ѫwúa¿³ÿèùüùG{Œ_úvþÈGŸ-­„ef!7AdûÃ9gŒ(F¡‹¡ËnŠF/‡‘'5Ï3'RQöxÚêÑÙ}öýOÇÇ'\3ð‡7T´Ñˆµc¯ï}mv®5þÝßOŸýâÇ/Þ}øO“žì'R&ÖæN;ö©åHb°¨ÇC´Ñ²S©¬ŒcG½(ºxåË©¹¥3[Ø$fh£Ùé4Ùè©U2¥7¼7ÿÏ£§W£°( åÑ\§»‘@ "iLü ì1¥}‘Õm4D£g¦Ûh£‘µŒýzϫӳ3ß{ûÔ™Ë?~éð®CWÑF#ÈÓIe£^ÏHÁ™X˜—i‘fU´ê±~€6AN“ž\‰[¡¹1ßß»ÿ$uäEBX¹ÑVš I’De• UQOê€fàS“-´ÑÈZÆ~µë—“ÓS7¿¹uòôçϽxl´g”©2–æ÷hÈ]RõA ;•ÊæJ)ÚËËÆÚÉ;w§>:w^Gm4‚ ;6zÝÜèÙ•Õ¥ ‚h4¡A{é²íñ¹žp6úÆ;“³h£‘µ Fg´Ñ‚<½ Fž<˜Ô Ï ˜Ô ÈÓ &u Oûþ§×¯ÿ½5»€6YË(©db‘õck5„²Dx2Ž)aŒÁ ÑýkYkÑF#Èv§²šR[ä¹µwÛs<É*kmam^5ÙhF&qA¼\QÓò)„ÐZ§i𦩋_‚ŸÞ”NZë²,«ªRJÁú +ký±Rªªª²,G8ÔÙZð߃îÍÜMص€I³Hk=¦£ÎÖê4BÀ§”eY–%ÜàEP·¿i^k£Ý%×1Æ(%4gš“"O­µ3ó÷ÿþã&½îô¬úÌÌûbf•Ömô\km4²–ƤŽÜ–P÷h7´¹,dÆ’öx6Úí³@X"I´Ñ²Ý©¬·²"H¼Øé|üÅåÛÓiV•2¥=D4šÕ‚Oà‰aûu]`‘SJ¹,ŽúZ»q&À½oƒåÓ­ýð^ÔZƒoCí©ãf[Út?·`†Ô=ÓZPgËuÖ…R )"ifŒA*×Ú/· ÷ÖØ”Ž”’1"H,i”¥ÉÿYðn¡ïu('&FrSJÉu–÷h2d£oܸƒ6Y—Q¹ÑPÓ˲[ÃRÚëG‚øŠp)`ñ£”ÂÍ1ŽcÈž'îìžo¨³…:MãYí/Î-îB熅C¹"0`hælD‡1FHÄãH05º,ˉÙù¹Ñó½þ*aq¢y" 8§*ÍÖFcR².I¦Ê"•´:IÀ1$Šý/{gþÛÆ‘åqÿ•‹Á.°ƒd`0›Á ²ÉØq²ÁìÎb'—oÇÖe[’%ùPì8“ØN°3Àıå3¶ê¤n’}wÝ}w×þPf‡Ù4é#Êy±YüÒ†J¬/ß{åœjTßïB’$ ½N&íZMÛÞÚ6Œ@JⅪęNû¤Ž|×Ì)Ú;‹m u¢Ó†Æ'vò3èô Nó(Šq¶ Ðé5E^’˜Cëiñ¸¡ÍW~—Ö}n£ÕÝÒqrqURÇüÊz›Në†Y# ‡ÁŽh4”Sh£#™ØÜ[Øâ’1O¨a"A,[Ž1VI¹ŸNÓl4ô:™Q×1ªa„¸oÙxun)@´ÈFc—äcŒ×i¿}æ[oÂlÜ ;Ñ)‚sÞݱñ7¾¢zÐéM|ä•jžIÛvtÝÕ)šOëì¸ÛlwDšwØènu(¥»#¢4‰¤” åç–â0à¡`å ï,o8Þr~tjôŸÿíÀÐðä;ß/̯€ši—m6]v«–ŒXÌ-Ûa¨ŠMD°zÓÄçïžÐ©ö™ ( _ÙÜ{4SR%†mr£óhôŽý¬ÍöI¾{ÍŸÒøum':môóý»¥HþhÌ$ÞÔi\ ;VyvéƒÎ®ë´D%4ïHr]7ovIŸM,ÁÏ&€Ñzs½nu!9*­l´ŠF%uE£m‘lºþŠÆ>>}^ÙèÛ·Á)†@Köýû7gKsS{pºÿêßýêèþc_€W“Lr×/¬®öŽ<œžS6ÚÚª‚Ÿ^gOØDÐi¯Ó’½n£-7®N1„h4Ð’}ûßûýÂÒâƒÛU4ú7 ©^M2 !61®a„E`;d}~%ÄÌ5µc§ÇÕ)†W§ÖO_¾¶YqÒ’:@’:@çç˜Ô6觇?šš:ûåß¿qòàÉ›Pb¯&™Ùº¦J ÷—–× Ät~zæ5PÝlÿ Ók:н^b6èœ}ï¼`ieùá'Ðð^q2™AUËZ®lW Ûc£Sai•Ã'Gÿññ‡ïM~W>yáÊÚ¦ù4¼—¡Ó¸?_íø‚^Ñ~½Î.êÍ' ¿qòìjÉŸ˜qÆ뱆w`£ÎyÎmô;'nÌéL÷9¿¯™´ªUAðF­6yý«™…å4“ÒO°fÚzõ話_þfâ#÷áøÐyÙ:{èxÐi¯Ó¼Ç_ tξwÿppeµüýÝ™¼á݂噡‡À«F&¹ëfQ¨Qúô0ð0A–~¼oâõß]zïø£ÏïnôM~±]CY ‡ƒ:?»ÃÀÁFóCRÇÐÙ/•^´}+òc™º^°°ÅM,S/ó—øÌò0åL-eι”2Iß÷¡Sì2ImY&ñ}7ðýDnWô¥'%ÏÁÍ%†[U7$Å?„ˆc¾ïGQ”$IÑöIñ5ÅqDZúìÔXŠÚhU¤¹ÙF¿€NœSA° nRÊù•õç–’(‘ǦÌõ<$)Øh sö½}ð­ÒÂü½[ú¯ýâ×ÇóNAÙÜ[ªx&~Zbèrlû„0J)u!äû>¥Ô²,BØhèu2)£Yæ–®R² V§Ý*ÊÌ£õLʼÉTÛ¤øø¾¯veÇqÔ{EžãÑ¡N†ap΃ @Y–¥Âiy\M½!„‚ àœ†:½©“—޹®‹êÙóÊ3©5CñO‡ÅÏæ2>W§ô,¸‰@§7ulÛVŸ¯v,ŒÆúEÜЊ¡HtvW§h~¾HTçüWߨbOA)U%†-¯w«ƒ1V ï²_dY¶¸º90>¹¬³¹ÕèþŽ®8vœø”£uÃÔ™`IìÅ>ãHåFûqbÐpYÃ+;<0ñÚ¯Þž|ð`ZòLRGÞðÎ ½PÆ&å¥ ª92¤Ñ ‡!9˜’ü»ZUbH áì 2™ú¾GÉÂêê×·¾_Y‹b™°@&YúÑSc¯ýöâÛß™ü®üÁàˆf2†}ûªˆ?‹[©—æûœÚÞDuŠ(ÒÙA> tö–ÎŽeÃëemEú ³»:Îo„Õ[¾(X½‹|Ñõ®t\×ÅØU¹Ñ/¢(šž_>~æ|‘^Õôa4ŽDäQæ"l1F¼( tξÿ±qy©ñø•’ÁÕñ+*±˜¦Ë±Á]L ­'u(í8Žl4ô:™Œ…àm;¶g‹*+Ìt°m|r|X%uüùÁöÇgÇ [ø<&ˆæ¡ ×uQ½Ï+ÀqœßY»ÕiIÚÌ·öÆG£kj&èô¦Žïû¢Þã,÷IùRᜫ„]•³[$:»®Óæ)jBü&õŠÆÜ—çŸ¢óèróõnu<ÏÃØ%ŽÍ­J —Ö¶Î\¼Ú>­J ÁF/F‹ÜèËË;u4ÛèæNžçÅq 6zLËâYæIfRÓí•™…˜ dé‡O޾öÛ‹>¸voóƒÁÓñ’Pú"Èw,ιïûaÆqÜfûli£îP§%aA ÄóTÆÍX¡œAa‚NoêÄq†¡êÏÐ躵w ³»:EóYýs²*Ì]x·6º[8ŽU‰¡GQ–Æiš¶OêXÓ ò<7’:€à©nì­J Û$u0ÆB”Ò$IT@J `IbY2Žh”Ö×*2)e$ecÛP ïÞúèö•;kGF/2OÊTf‰L’$Žã$ITàö¯EQš¦O_-Ë’$QOLÓ´+çü?²Lu,V²9êŠztzY'¿¾caärå4M£(z®>èìŠNÑÌæù‹ÖRÑõnu¤”Y–È4‘Y,e*¥œ],Ÿ½øÜC?  Äx1ž–>ššÎK Õñ+E%†r !*cIÔO‡h4ì2™Œ£òÖÖÕ¯oÞ<Å2Q&[¯;=þúï.½õÑí/îo¼pŰÅö†¦UõZ­V«Õ4MÓ4M×u]× Ã¨ µ,Ë4Í|²išµ:ê¡u èô¦Žaj ¨»ùò0M3È4M˲ÔÝ"}ÐÙ]¢ùE¿wÓ4ÕKç˜ 4_ïV§R©lmmhÛ[ze³ZÙÚÞÞ¾ûhºMR‡ŠF«ãW Äx1 K ‹ÞaJDý#ιJ“JÓl4ô:™$–ã3N9Û64êùa”Ô65[sjšq¼ò_Þº°ÿ“o¯=¨|pöÒ­GKc¿ì?;Ö7|~pxb`t|häÂÀèøàðDÿȘú¹ù6Ÿß7|¾ïÌùSgGÎ7^ïP§èvàܸÒék«f‚N/ëä3•Îés£C#WNãõ–ú ³ë:E­ùÚÈ×À˾>42><~éÜØ…‘ñ‹C#cýgGG/}>úÙõÃCã †˜Õè¬F|xìÈÀÙšýCn4”ÿOöíï÷ªÄ0F— ®û<È"D¥ Š=ÉÁ-Û"ŽPÊY^š­Jw !¾ïƒ€^'“!fNEK™J™ÎÍ—¾þæ¯UƒnÞǧ?ÿ‡7Ž¿{ìëÛëÑC-Y$rÑ‘sš7§‰Rmçí¼æ—t¯ùv¶Âgª¬ù¶Y¡½Žzt®*fk\Ý)tr :{E§hý´_' ³[:=u[ÒDIc%ÌÕèœ&ftoÆðgÍhÆŽW¨œÑÅ·sk?<úÉ©>ÓuÂHPNÖt£Š) |pBad²hYÃKUÒxüÊâBùÀƒ¦Y¥'þ™ÓÎFÛÜ{¼lÛT × ¶©«W5yJi`£ ×ɤ Ò˜ŠXˆòRûLКåê$Ú°ÒCƒ×_óèûn>Ñå¬#g\ù½)gmY²³ÎÇœ•¶]‰´T{1ÐÙ[:?ÖúŸF§ÇF2oGóv4gÅ3v:ãÈ$g±œCrÈ%”@çç£Æ o^çó:W¡èi=xbÆÓV2gų†¿‚¢Çúáþs§Î릅>¥¸ÈF»~¶fñ²ÎURÇà¹Ëwï>™›] 4ÓÎF[LsHmckòÂDye! ýDJâ%U':>påG~8|cjíúÝòµïfÿ÷ñêõ;³-Ç©¹–ãæÝRËÑ­ŽÍ3‹tÚëƒÎ^Ñ)Z?¬Ðùéuzjܼ3ýÍÔ“o¦žÜ¼óøúÔôWS¥/§æ¿º»pýÞâWS¥¿|¿øåßîÿ÷¡“§‡Îi†.eEÑšnE£WM¦¢ÑÊFß»7]š[ 4Ó.©ùáìFBrGÛ±ˆcûl4ìU2‰ØCsPmk“Çqµu*¢ :tâüûÿÓÿÇãç.ÿåÄðµCƒ}ŸŸ:?Ù7ÚÅè?ÿYËÑ•H®Óþ 輪:?Öú—­ÓS£trpôâ™Ñ‹#—N\þtäʧç¯~zþÚ©±/NŒ|6xñ‹S×þð§CgFÆÖÖÖ(AµZ­ÈF[<^ÑÉr6æF/̯€šig£IÏo2ìIfsfZ&¶€BRìU2É\Nl Ofqšø›å[·nUªfÉÄåS÷WV·YÕ WkhµælÛdCsº›ºÛr¼€Žúa½f¯×ìAçÖù±Öèü:=5¶4«¦YšfTkæVÕ\¯¹k^«‘U,Wœu”·­Å•-Û!2“2“Bˆ"Ý\bøðá”-ig£]/˜.»“ÜžãB‰!ìu<y,Š‚ÐÐ+QÈ1276ÖlÛ][¯œ8qfq¡šd2ˆeËDÊDʸ`DYëQ4¿[üÕsÙöÿž¢:{K§h¼ìõ:?®Î®Œ4“2“2Keše‰Œ&2Le˜Ê ‘A*¹ŸE±š#“ 4M»ÈFÛ"iŒF OÞ¿?%†@KÚåF›”?˜×U‰aD¨*1„†w°waØ'®° óê•K[›å(äRÆBä²¾Óç¶7,™IǦ–åÁ4­J ]Ò­N~ÌoãÓó‹:{Kçe¯+Ðùquz F¨OQHP€]A#ް@X0êÛÂLf2ô£,Š£()²Ñ®Ÿ©Üh(1žË¾÷þóÝ•ÕòÃ;OÏüù¿>þΉó¦P§â *k¡Ed"RÏqiÀ-Fcêì)eš¦AÀaà°' ®‰4uÃÐ+«å…0$RFR¦¦n õ×6u™ÊÐ<Áu029C” ‚]‚ÝÿcïÊš+7®óüÎä1yËòdÇÚì$¿ y‰j43¼—MÛ±“ªT¥’%˜º”U! Ñ —œp \s¦´41t©K)´B¨“¢*”1©ó]£4ÇÉÀ] áw'äÙ9ý»ûÿô§þ.uœæéWP:£Œ!|‡·ôß9î|ð·ï??ßJËjûþxz:K19¥`FF ^Œ¬¨•ÜkѹiøÌmœ‚þˆ»¶Í<›Š Ucdë´ ¤D*Üe%Ï•­Ó‘8Ö<Š e5ŸXÉSë‹éùäô¨šO®(Åôœ3ZÎËÙÅbrVÎ.ÊÙE1=/¦ç¸Ž•´œ“bVLÏoÊCË9îEÂj>ÁúÜ šOêÅ´^L±#¶\-¯‹§šOXµ(¦ç)¸®±(æð:çg?§Àk?Ǧëß¹©ÍmåY[²Ò V«TO£ˆ×@­°ÒkáµÀ½K nÊÓhð@5©$­ P+˜.(Qœ)À¸–*†6u)†„:š3ÐxÝ!iž œÚî`ûSøû?û“?{çþøŸþ»Ýg(£»®dô€Œ;ïýõ»ûÏ—dtá´i]­ÌþÄ,hràEQ2 IpôF3ÆŒ1¸>ÄFðF ¶©ñ]ãüÑážR5cSRÏ×¼&;¶''Ó’R5²6²”¼^+Τ x^*ÎÕ¥•÷b/¬^KFoiji ¤VÆ!Z IDAT’9%×@j)¨€{•d8£¥dÔh0-äiŒÆ½ÇGû¶Ç÷ï}¸³õ—ãѽÕåÃÑÖÎöƒÇ£íñý¼¾³ýàáhëáÎÖÇãÑh룇£­Žm·ܽ)Öìl=Øß´=FÎñèÞ£í1e©ý&þ×Å3º÷Ñhë£Ñ½Ž÷ŒÎª`M½d4_[\×Àµâ›®ß¶5pì…ö+y6µÇk(ÕŠ[)€×‚%™ mRI¦8Ã6ȉm°/ÚŸÎ&Ó³³“›òLÎ/rý|:Cž‹‹3Ü›{aÍZf\¾.žÅlþøñ×ÿú«`]#ŒPB*¼†‚甊«”Øtý7ÙùmåÙÔ¾o}ëÅ{‚RJÀ£#3²­î½)ç”1Â9…o‚øñÃDÀåwmÛ¤Û¶aRœ–õ $~b(A´–6´kƒ:žï/ÉhЃŒ~ËqçGüÕ³½]”Ñôÿ€2zaÕªŒFo4ÊhÌy—eô=`Àg‚QÞu|´ç=Ä('DZu¤ªGvÎŽ§(£˜DHÂ9åœ÷¿Ùçœc~žM_ôã=Ÿ´sÄõy6¿ŒWhðÖò„¬µ§§§£ÑèñãÇÖZιÙçœRJJ‰ëÆ­µ1ÆZ뜳Öâ¦÷[Þ”W°#rc”Rù(¸‰ ðˆkñºx´ÖOž<¹{÷îÙÙ¾bÅî}F«†oŒ|-úf¿ô‹ÜJžMíaűñR/ÜÌ·‹Õ]7⡜QÎp‚ à¿ü ò*M­VJÅ„¤Æ(ׯJ…½)]•Ñï¿ÿã®ëbŒ}=dêxËqç‡ïýàÙÞîoÿû+”Ñý„wD[ êð¢‘e…2šr&¥D»ÏzºmÛAFðý‡ÕMUÔ˜5à±ð»ŸÿâÑtvVUäÁýíÓ“i’À \.€ƒd\0.8Èo-KÍV7¯É³©€PK´È¹ÄŒëØxàa\Ê9HBùt¶øøÑOOÎ0Ç®ÒvmiBTÚ ©õ¾iµqJ[mœuoZ烱޺&´]¢±þx°—6©2‰±Þù`]Óo¼‰ÿuñt)-Šj¼ý°(k¥-e ´•Ê\Ǫ¯ø_øÖ‡ÛÁsýƸIP¹¾ß¦¿wµã·òÔB)‰ÐDÍåŒ圂 ‡ZiÞF—RñRFOÔZq BR”Ñ6´ÄÄ¥ Žßüæë½ÝÃAFXž÷ƒ§»Ï6ÉèÕO £ø°ŽÉ:PO2zÀ€7Zšb^JÁS Î˯ÿåÖè.ç´,k”ѱMBj.€ Æ îËè¬á„Ô›FM”kBjl³$û®Ï³© ²éÓ.É;ŽÌ2hàÑÆQEYׄýãOölwßX¯s>¬-¡í¤2 T–¿Bj”­YF;Ú˜BÛY×Ü”PÂjã° àÆ³Bù+•AAl]³¶xïÑ—œÜèŸFÿ7:˳ÚZ»‰‡ƒ<8<¾ûÑýª¦Ú8Æ…T†2è_ölÕhäk‹zéçÃúÛʳ©0.@( çu’2 ”Ê—n™÷R¯ÀC…¤JQ¡ ˆŠQÂ(å„ À@ΩÒ½Ñ(£OŠê‚òR *‡ƒ:´o¸O‡…x>(£ï~þÙg_=}²?È諸ÌÔñ¿ÿóõhûßÿø/?üàÃÿÚ«mÕØb­Ì³3¹ )¨½Ñ¥fe]qÎ !„”Ñœó!oô€oªªÂWۓɹ”R,ŠyUU”Òñxçìì"Æ$¥”R*¥”ý7¿øUS/mzù+„À÷ø°.ºãš<›€OïøléMtŽ'áœ+¥ðiàÁî˜ìß9WÅÎÎÎññ1FwjkäºâÚàÚ`CcCco¯½³ÞI­œseYRJSJMÓÔucÄ_“Ÿ¦””RRB Û4”2¥D)1ZkcŒ1FŒ3Á  k-¦OEÉëœ !4Mƒ êº.Ë2¥T×uJÉ{/¥Ôú2ûxhpê¬îbˆmƒIˆ×–fRJgggãñ¸( ­u¶á~Ìþ8êmºþ8ã ¼lêðrìÄmâÙ„¬ ðf‚½ò!°2O›‚?ú‹YT¤Ìw›òH­@  ¤êàÈé5˜T'EµZ¶Á+üÑ6´…ðÏçc¨&³ÇL"„ëØxàÁ]Œ1kí|>ßÞÞ>::º]lÖ•œQ7}l}lC|‘ث뺂sÎ9—çrCIšUE×uRJôØ !BƘ”Jê+€¿ìóC ÆcbŒmÛ†Æ]æ‡ÎY¢[½y‘=ºWºât]‡Qã(£áå§—¥ÀÜ+bˆW {é_à–ñljŸ#?q×QCï.§‘o5ýgllðºxò9+¥ú2Þ‰Ð`»œ©cÑ®;ïþä½çû«™:šÔVR£7º‘3uŠâô+„œðŽ¢µdô€ß`šº®‹¢@WßÉÉ c¬(Šû÷ïgÝœò€Ô´6 ŸèUêd«Cõux6ßøVÑ/•E?®dßUn<ðà/‚ŸÍÍf³ñx|xxB@‡ô5KL]LR¡-YkQ‘H)QUç ¹Pò:çRJ—ÑÖöí0„€MÓ ŠÅ¾ÙKÑè¢nÛ¶m[çîÅC§”¼÷©k󌘱mÚàS×¶Áã¤wëÊz´m{||œe4ÚjßþûZ }íkÑp«”·gSûlýîý·OŒ1|§Ý—¿XÓ¿-¼.ĪŒ>œ-¦\@ã•× HÎ=Èè×Ç¥ŒÎy£Ñ]8R¤Æí]è R4ÎbX[üúêºfŒyïµÖ”R¥Ô £ øþߣ éºnww÷“O>™Ïç(£ÏÎÎRJøR^¼ ½j2 BÝ4|â ‡±ªÙ™½äåºÏÀŽH.z‘!Y/ !0@vàé󠌞Ïç(£Û¶E§oÛÅÕe×u1uKËŠþ`Î9¾Ê°Ö6M“§DFqŒëø¨–RByÂ+G”®êTÕy3Ç¡âAsGH) !RŠÞ™Ð¸”"†m \Æù¥×” !Ç㪪¬µùŠõm­:»B×"Gf¯Úü­äÙä$ÿýÒ'†Wu`¸’_Ãi­‡O  x#@A9r||ŒOˆªªF£ÑÅÅEúFF£’X’¿ÿÏÞ™ÅQï{ü<øä›UZ–eiS–K6²‰!!°$äDsFš¦ÑÍa:ÞXn"Çv¶’«²uKÚɵ~cù«-CX4ù›íêþ52ú×ÛÑöo“ÑZŠ¡˜Jê)†:¿ŸR n™µC'.lbHÒXÍwѲèÞhûi oܸa2™Pª¢(AôôôƒÁ R ÑÕÕËuùDÊ[K½×&7kgû¨/†æd½Ík‹vPß @?hv˜E1£ß$R럽·)M ªjZ5›Í………---G=zôhGGGmmíàà ä¸ÍåŒ;¨ª:44tìØ±ÚÚÚ .D£ÑT*•¢ª*ªà¡ª**Б-MîÑÜÞétÚãñœ:uª©©Él6£Ê3¨rˆªªÈ¿“+•0VS댪ú+Ÿ|z)žÀ9^ÔJ¼eŸÃè¬Þ8°ÕÚ}#X”[ÕN®õ`5ú¸ÃÕØ zµz \ °†«¥9´5ÚoÑâ;eäöFCEQO1ÔùmäL1ÌUðŽ@‹zÔÚ¦Ói]FëèÜûà8Ž®.Ñh¹„Ãá0ŽãAtwwûý~EQ´ë+„Ŭ¢T ÃlPpM’$!AA0 £¥ÇmÖN.PÍ_“Š!Ü8ï²rÄF‹|rÝ!‰²$ÊrR‘“Š"¥)•’Ó)9=66¶sçκºº±±1Çc³Ù®^½ZYYyâÄ £Ñ¨(Š×ëõx<.—kqq1 J’F¿ùæ›#GŽ^¼xqzz:ƒA¯×»´´‰DÂH$ât:ív»Ëå ƒ4M#=M’d p»Ý‹‹‹' ¼ØÚÚÚÒÒÒ±±1–eq÷x³Ï^MïjP³¶sÛ‚;eýUkeôr,¡§êü›üá­ÿú J1ԼѨýJ2#3IÙ€Œ r$ÏáH2 !÷/e˜Ð}ž(ŠºŒÖѹ÷aY6‹AÑv»}`` ‘H ot H¥R0+ƒ'×eòN £ „H$²¬$Iÿïû ¡i¦X,vùòeÇ£ªª$IH(¯Š”B2Z‘RIA8QN*jF<|øðž={pO§ÓÉdÒd2ÏÎÎZ,–3gÎäçç·µµ555utt ž?þСCÛ·o/--}ýõ×øá‡³gÏ–––VVV644\¸pÁåruuu•——×ÕÕ;v¬­­­¿¿Uµììììè訩©ÉÏÏomm½~ýºßïohhØ·oßÍ›7?ÿüó×^{­½½ý믿F.öD"!å5…Én¼‚fRi5¸þôâe$£äOún7;ƒæ™Öd´¢(d}q,Â@:)²I@y£yIÆXu1|ï{ã/Ÿ\þfnÎêrzß~û¯kŸ“è2úg#Mp¢‡  ÊSB’fHá96û©ºML&“ºŒÖѹ÷Añ…¢(.//+ŠÂq†a4Mß-F‰D  IRUU$¦qÿ½ß÷Ac]-Š"„„ ÖÙÀ²€ãy‘ƒ<„œ$ʪªŽŒŒTUUŽŽŽÚív›ÍvåÊ•ÊÊÊÎÎÎÁÁA$—ëëëoÞ¼Ùßß_RRrüøñÞÞÞ¡¡¡ªªªýû÷÷õõ…B¡üüümÛ¶½òÊ+7oÞ\ZZr:íííO=õÔfggGFFJJJNŸ>=33366ÖÜܼ{÷îwß}·¿¿ÿôéÓ o¾ùæÀÀ@QQÑÞ½{›››ßyç÷Þ{¯´´ôìÙ³@UʃëX¨uãÓꦩ¤2`H—Ñ[•ÍÊhRH{p)Æ"Ýs音³f‡Ý£Ëhµä”ÑbZJÖ`%hU E²$K£Cú_K#’.£utî}b…B/^DŃUU½‹ÞèX,¦ª*EQ¨|u(J$Ô¯ÕùÍ ÃÇ/_¾ìv»WK©ŠfiŠ]»%)ÈЀ`xšáxNÊdÔéééÚÚÚgŸ}¶¦¦¦ªªª½½½©©©¦¦Ùܵk×ã?ÞÖÖ Y–-(((++ëëëëëë+..~饗|>EQ>úhGG‡ÙlÎd2>Ÿ¯¸¸8//ïäÉ“Á`Ðb±ìÚµ«¢¢b```bb¢±±ñÉ'Ÿ 8AN{¡žKW" ‚áD ò€Oê2z˰YM‰Îyã êè¾xufÆd³ºt­³–d4Îò . *O H–Æxš?GVÁ¬üq]FëèÜû 2;+++_}õÕÒÒJÛBIúwEF3 ‰D¼^/ʯX^^VUÅËêÜY²e´Óéü)x 4+¬ÝBVDû 'Ò ‡Ó²¢$§úúnTVVîÝ»·¡¡¡°°ð‘Gyøá‡óóó#‘ˆÇã)//ß¶mÛÉ“'{zz>ûì³¢¢¢¢¢"£Ñ¸°°pàÀ‚‚—ËåñxJKKŸxâ‰S§N¡b‹f³yçÎ{öì9qâć~855UUUµ{÷îÑÑÑÙÙÙ¶¶¶çž{îùçŸÿ裦§§N§,Ë@ààÁƒÕÕÕ FÇ‹$Å”–•—–•/û8Aq¼ ·î`8‘f òÚ YáD(H_°çÒ•(Fj2š‚,Еô–à7x£—1y£‘Œž5Ûmn]Fë¬e£ Z”¬>†æUŽäy‚ÄIˆ@—Ñ::÷/èy7òûH’ôûýwQF?ôÐCè»Âëõ¢¶Âx<þ{¿ïƒ³Z:Õ¶Ûí‚ à$AAž„üÚ-Í hŸb‚a YA•±ñÉêêêêêêþþþñññ®®®ÂÂÂššš±±1»Ý^YY¹cÇŽ;v466677>|¸µµuffæûï¿////**r8&“©¹¹ù™gž9yò¤ËåRÅåríß¿ÿ…^(++kjjêììDÑV«•çùü±¥¥¥±±1??¿¾¾þ7Þ¸víšË媮®®¨¨†,ïó«ªk«kêÌ€Eƒì°ì44 8m %MAÞåõk2šɺŒÞ2lVF㜲ž(ÌŽv:–t­³–d4Ge•%8Ã1† “PêÐѹaV›B«ªšJ¥ÀÄÄD$¹[2õ$‚@QT,Óº‘ëÜA´š_¨n´ÍfcY6Ç¢8&¨u·!œŒtŒq’‰4 y^NOßš=tèо}û¬V«ªªƒáƒ>¨­­mhh¸uëVQQQ~~þfff¼^o__ßÐÐÏó£££lll SSSÛ·ooll\\\äyÞn·ýôÓ/¾øb{{»Á`(..®©©ùq,£ªv‡«´¬¼¢²zÁh’•´ÏL`ÄÚÉ8AÇ :†SÚ@3žåžKWb8•%£y=®ckðï§ÎÏÛôCuÙHFSBÒì¥HVåH^ )=ÅPGç~õË•$)K’DÓt @¯îŠŒæ8Çqš¦“É$ú>A3¿÷û>hÜ&£- ‰…D'W0ò¶m0A0b#C$© NÆŠò7½ßÕÕÕ•••™Íf€$IƒáøñãÝÝÝÕÕÕ=öXUUÕÜÜܵk×Z[[Ï;g±XJJJöîÝ …œNgIII^^^WW†aÇMMMuvvæåå;vl```ff¦¾¾¾³³Ób±LNNž;wîÕW_5 ï¿ÿ~UUÕÎ;_~ùå¾¾¾üüüšššGÇ9=mXÈ/*)¯®3ÙœrF F⑱îˆ&¨p‚ Å m„d£bcs/w_úGŒ`h.‰3Åê2zë°YMð©lotÏ¥ =ÅPg]6ŠÆ 7爣C@”b¨¼ÓѹAðÞÞ^TÖW½«±Ñ`µcê ¦( “ÕuLgó°«@@@@C@S` ˆ'ðO.f´:Àù¢‰Éú)~íЂ¶ã#¹% h!Î+ÓFsu}cýá£Hܱä“L‚‚×G޶´¿òç×/þã«Öή‡šŽo­¨=Ø|²cÊ`\Áˆ[ó¦CÇ[=aryæL¶#­mϾ¸»µëf‡Ç³Žâtÿèø¡£'öWT7·t4·¶¿|´åÖœÑéñËõ»únj8|ìXK[Eu}skûw?ô±–¶Ó5Ý¿E&3Ka¼©ãGNvM™+8\!ÙÁ®» RB€ä$ï'8?Á­ü Ɇ(n‡f—ïï—¾ˆ Éâ‚wûhêÜ~CŠ!ŠÖS u~‘œ2ZVSäüX*N©”QŠ!!’¦€Öª ³,«ªª.£utî} „<ÏSµ¼¼L’¤ ‘H ëîîî@ S¡=tBÒñ‹ò:»Ù úŠ@=A6kgcÐËé5lÖìÖ²Ã2@`€Ð¥H‘Â!… <€¡ g`÷W',n?“tâœ5!XâÚaÅ’vBvŠm¸dÅ’6\r’²%L͸ƒÓΠ%„»1ΣmÚ•³ž0šŸ´/[—GÍî‘E÷°Ña'Í!rÒåŸ]Š80`)w`Ê4ø"¶(ã¡ø%R´Æ¨¹åØ´Ó?:ïºe^2y¢ž0À_Úý¸Ñµ2iôL]3ŸÉv­P®Âä‰NY}nB¶²1 '<ñ wln…±aI+–t²í¸dÃ%´E3f\¶+¥ZÈŒ…P¬XÒŽ'ÝdÒƒó§ÿÓ/¿]‰á$Åð„ÎÒEâÌj×fµqɯ?|Ìj;=’$·¤\ëo;cµ—ÿ\|0k>4¯-»Sv´e·µ_ âdœåQC^,G‹"/¥3ŸrGiW˜yÿã/þtþ­î‹W ‹Þ~Eg]þðŸý‡Ëi˜4~Üó}Çù¿½ý·ëÖŒò5÷%”©&¤Õ&iJûǃ¢>Fª.£utîEÁ0LëØl2™®_¿‹ÅrÉè\W¦\—Oôå ¥OhϬ6kç׳Öòk‡, $ÃC†åÀ3$Gã,ƒ‹,M²l °ÿûEïˆeÉäÍxÒ„§‰ÌÚ1ŸPŒxz‘Èñô–šO( XÊ„§l¸d c:ÉI*ægŠrNRvÓi7rQ)');ÙŽ'mXÒŠ‹VR¶3Š gÃô|”µÑ²•” Æ qÖBHr0i ™\ˆqóaÆlQÎáqÁƒÉn\rÆDk„µ…9KZC¬3!z‰”—J¹âIs˜]LÈfJ5S*ú;M¤j"Õ¹¸lÄÓhFF<=§g™YB5ê®Îa™ù„bLÈV,éÄ…i§¿çËÞPœ išç Ka34 Ðä¦vbç:¹´©[ÏN®õÛÙ¬ü½Sv˜õškÞhÈR 8 ²B‰™¥8pGÀÿtù§óo}üé×33&§cI—Ñ:kù?ö®ì9n#½ï¿™Jó¸•` ƒˆ!DB ù ùy ù A„OøÐé…;ÏÊ›nذ‚šéÀí¸i­Åh&£ëNTðš!ZŽj9ªfˆº)ÛnT7eà VдæêO[ŽªZ²lðšQÖó, IDATtà°åÇeƒWLÑIÃUu'¬ÙAÕ’UKÖì î„ WUlYu‚ªÔܰ§jn¨Oêê”L¾n°²%jNT¶C,/YJGÐu@ZÛ1]÷Ó’—Vü´ ÒÊQݱÄÿ5·¿X¼Ûw|„9c ûLj ×w>ëÒøÔd†ìוn™Ü<¯<ãègIù­y²6“ѯl×À… yÎñËp\FÿÇô×=Wœ"£ÁŒ1ýPi !Ì×Α㽀^’yooï»ï¾ÛØØ‡RJÎùY’:ô`ð}ÿ”*óÌF¾_À3 ð(NdŇÆ ¯}}\Š\] = B`a13wûÙzoà šfPµÂÊI¶>%CV¬°lë¡ËfÐpãÒ@û´lȺÕlU6di êN4j5[i+õÙÚ.©ˆŽ7lÚÑÚ.Yߣm7©丕 YðUƒLQ´dÑ’kv°î„%W­|Ý +~\ö¢5;(˜bÍÊ^TqTÉk¼¸ÏÖxÉU[5¼¤le3(r´ñ%K U4£u;Y³âµZ3‚’!k†h›â»ÚöÌâݾ‹ ‘”r໌žœ Êáû¾Î3žÔíÏÏ$ÿñN{ºÿoÍ“±QF»,Ú€Þ>üŸ¿ÜúçߤWêØÚ|™ËèãøÝþôo½ÍÒ‹ÚÕëþá÷—þx鱞bxJRGöPi ÈWêÈ‘ã½@¶®œçyŒ1Û¶·¶¶N‰F‹ýì0–M.}óû xÎ2"¢1qù hÏ"ˆ1ÄÈ£È%СÀ!À%bL L ¦g¿zVìnÚAÛuSVlYµÂceÙ[ÖlU±eÅ J&¯˜AÕ {8ÕJºbu'ÒõÕ]\µBm3(RkëʨˆRŸÕAÛMZVTê³ê¾h»Iu_T²a„ K5MU7ÃÚ, D¦¬;áª)^ XÁ’?nôù-ÚAÅ+ ){Ñš®;aÙ‹Êf°n°ŠÔܰîDeKè–ë«Ð-/²l ýúꣿsçÚµkûûûišfò}‹J)•Rþ¬üÕY)•M¢ÈÖöy#žÁSJÅq,„È‚U£2!$„ˆãX7àãŒ#ñ$–R`Œ\‚}J¥Ô(T)dêêÂÝ›wŸ|Wh?-tÚ+ÅìÑ‹ÆãµÎ×¥Þr¡µ´Ú\)¶¯uVŠíåboemc¹¸ùhµ³TØxRÚZYÛzø¢½\Ü\.öŽ•+…ÞÒÝ'ÅoÖ¶WV»ËÏ{ ½'…­Ç…ÞòóÞÊj÷Iaë›õ­oÖ¶Ÿ7–Ÿ÷ýÐZ)l>.n¬6—^tþÐYzÑy²öãÓòöJas¥Ð/—V›Ë…–nê×¥ÞãµÎÒjóáózÖ`}¸´Ú|²Þ}ZÙ\^k/¯µ—‹Ýåbw¹Ø[.vWŠÝÇ…ö“ÕÖ_=»uÿë/NSB%‚ œ‰6ýeRr´Ü”RŽ÷ùsÉ3ÉŸîu†a†ºÇŽÊëMUÃ0̦ZÒ¾-žÌ9—Ñ9Þ:&&uðHº”×¶<¤ˆ"ŸB? “’:0ÆSSS7nܘ½pá‚–Ñ9räx§ðòåË4M Ãxúôi«ÕŠã8ŽcŒ±ëº×®]Ó/‘F¢Ùð£AS & ŸÂì ,Æ8›–ô¦<“àû¾æÇ‡yغ©äp«—,«„RzÊÆsÊ}`SG{ØyŒBJRŠRމØ7Üëó·ÿ2gþËGWæîL/Ü™:É>»~kjáÎÌÍ{—æo_œûjzñîÌÍ{Ó‹›^|0uséÒâ£?ÏÝÿ|þÁÔÍ¥‹ ÿûúÝ‹ Çmjþ៯ݞxyqijîÁ¥Ùû3 ./.Í,<šš{ mzþá¨]ž{8}ãþÔõ¿M߸eþÑ KWæÍÌ>¸<÷pܦçïOÍݽ4ûÒüíñK¸8÷Õç³_Ž4þ®v»¸xçâ⽋‹÷..Ü¿¸pjáÞÔ½/þúpfîöü—zÛ}$ñ@„*x½§fvóõý‡“§xKËAG“zÏÏ)ÈÔm¦kõßÍ0l>fÎ|Þ"Γ:rü6˜8ÅPËèê¦;*£=‰'M1„~þùçW®\ÑI;;;I’ü_]Ž9Ž€16!„ûûûz -£³¤Žñ×£x,añDŒŽ¯hoÊ3 ™‚Ôñ0Ji–‚¢ëÙçQç†ß&Ø'ØGÐEÐeëlR„"†h°¹½·;pû¦¿Ó·ú.9Ñvmtà³àºÞwɾGwºë°¾'ö\þʦ{.?Á¾/uýDÛµè'~Ðwø®EûßwÅžÍ f'µõ~àÃ}ƒì ðÀæ.Œ<ŽØ=@›Ÿh ²FjÓMÝ÷è+ ¾4Aß%lî{tÇðû.ÙóÈžGö\öÊãÚö\¶çÐ= ½ì»T$*L}!€çöáì.Ÿ""ÇÓvñᔾsÉ3ÉíÉYïÍAÞºdÙWÞ Χæøm0qÁ»(MP š;°T@™­Ô1iÁ;Û¶/_¾¬s£?ýôÓÝÝÝ(ŠtÍ‘#Ç»Çq,ËÒõ^¯W(lÛv]÷êÕ«¯^½Š¢fEëáææjÓÖÇ0²Ê¾õ xNá×1M¨ë“­ÃÛ:4¥?úÐxÕó 1F€`È¡kÎ%¡œñ0TÃ8Mã$UÃÔ'âDÃBq5äjˆxè¨Tz˜;Xz4òhäxBUJU ÅÐ%êDÃ|ÈÔ‡)b‰  ‘‡B¥‡µ¦¡¥°`Z㬠È"ŠÄžõF¾ø#ßû?Ƹ[:wê/îñu„ÅÀƒ}}ûª4:òÆ Ïùn]RN~Ãÿ=Ó4}ë­·fgg/\¸páÂBˆÖ:¤ÑÁ‚+ãœû[ÑÆðÉtžçáÃûC‡Ìm”.ý ;)5€%™àÖê½ùöÏÿåÿòô‰ÿº—šÔX‡³õÒ:% Õ‚ÓTðLBÁ¨ü|%ï¯RJD¤”ž={öüùóW®\¹uë~émw“ÀÆé…Óö/xö˯Üù…w„1Æ9•RZ«›¦éºÖ_(ecTÓ4uí”RœûŒðÿ¢s(+¥êÚµmkŒ_aŽ!üÝnÿÏ\jŒiÛº®k¥àÿ™ŽÐR5U=iZ«¿üâ<%ó2sÎ0^´g Ö‚¨ªÅ~nî&6ΘÖ-$-Œ µl¶ÕÄéiå>ªÃY]ƒ]ÝX¥$“¬¼Tœ(É”æ ©TÌUjÒU“®2(ËAÒCx¸Ž•\5À*§°­ºIUi$7‚A´ð§9½- Ø!v(8%5ce×À+%”Þ‡•‚º®›¦2Æ|…v]×4•µö Ïß—:wêïýV)UU¶ª*ï±|Ú‡sÊ‚i­}­ýÒ*„s~×u<ûómÛqr§Þ§]וº]Y?â§gßÿÖcOýìç\»¶¸:¸Òè`mæÄÓϬ÷W¯^}çü‡=þÊ©×//ÕXT1Í(ms†\» ؈›L¶Dçô ¶·7ùË~ó›ÿRt]Û4âäs4><ñwøb4Ú¹|ùÒÍ›sˆ“ºvÜŽÁ1þ_ŒïW¢ 8 _ç¯tä#ønr;qÖjPr3Î"*¹vÒÎEɵ€¦Îu³–ŠÕ~ôÖ{ßzôoÏÿ`îú ·zâĉ½ùSH£ƒ!âÌɧ_ج~ví“Ù ï=üøË/¹4ˆ\ EÌX3Šj*Ð"*l‹ ‹ÜNÀÖQ#®o¢œ4ˆn‚¶Å‘S!S °õÒ`Ýǯi:]ODYUG~Àw¦m…µTšQ)mmœàL”\IÙ6é4ž½èÓè×çCìP›9ùÝS뫟]ûÔ§Ñ/ž¹Ü›D£E¦jePlîðB V›Dµ;¬këÉAä e“7X´¸Éll±œàz©"ÓEfp0Rí¡H+Ê&¶[Üâ.2]VãÑ!~îŒu;Ö­/T³ÅííR®³˜ å*WWœñBh¥»‰O£û)üpö½‡}òó†»ÑÁîd3§žûþúÚ֧ׯýÃù÷ÿô±—_öÌì…KWçVzýF;Äf¾ûÌË+«ÃßÍÝüéÅxâ§Î|Ü‹ÚHb¡&B£‘hjÃ¥!TšRXcœ1ʵA0­¥À+gœ3FKÏÚ£EàÀK­åAvVï‹h£¥Râ7pàÀ÷KÅüÆ‚ÑBJ!À€´J) Ô¿0Qju{ ¾|õz¿× ‹;Äfžú»—–£ßÎÝ|ó/=ðÄk'_ÿxyTD'jƒ‚"É5¡)ƒ„++ÀJV–K‡è ³B‘Äò¼Qԉ‰Âò4 àØ ?Ã'†[žË2ž޵R$9ê£ øCaX¢i¬H¤i<­”47Æ)]€àh‚H­¸kr5¤úó4úÙÙ —¯^„4:Ø¡6óÂÉÍ/oýÛ'Ÿžûè×ûÌs¯]ZË0³È2Ù²9qJÐ9“†qm@8N,+'5°˜ãÄТÔqâ›|«ã¤‚Š‘åi£ÊZšÆ²–Ô²¨ w"³õ“Ù~ÿûÅõµaH£ƒ´™žmy°óïŸ|zúí÷þèÏNžxõÒ évX+[”%G):¥™²”kGeÅ™bŒq*8çÀ¥_iˆR:-sÎ=û%…”cŒ0ZRZ ¿Ê+§~5WÎ)ø5 8¥´œ–9§ž}ÍÝÒa´ôeÎ)g„Ò’3"›*3F¦Ê¾uZ3ÝÊ÷ :G¨ãWóžàküŠþÇq?è3ût¦žvêì®ÿµ?Ná{#ú‹?¢ó’À¥Ü=üÈú2gÄ×PZ2Zp_–ÀêøÖ séìýi ó‹~WR(ZÁt/Açhuü¸O{úÈU ü¬¾wÄýÌ@Iág€ƒ:{õýÌOHñê0ÁA×TVŒÖV)‚)mm‡¹ÅµTß&íß¿þî£sjö܇׮õÖ×¶Cì Í|ç¯O,÷7¯Ü¼õãw?xè¯^;sî¿v8ŽDWê®ä );R8Âò‚g ƒˆ¸\u‰šdSÝ•s‹¹ÂX¶…ÆTw™Dßš¨I®03¨:$5æjË&“mfºBw©žäjâOLõäsI,ߺ÷áÅ\M2ÓÝ-ß³Ð/Oë÷ö÷­^9µ¯É-–ýVÔAçhuX…™éRh"¨s5)² iƒw÷š½2õ™éQ%ªõ}X…€÷±ÎáqšB·7¢§1ž«îHÎ+õÙ1Cfc^EPGÌíPC,’©Cï9¹ÅBw±l¼ÏLç_Î-{KgשžP‡´Ab1Qí˜Ú!³scQżqçk‚ÎÑꌩqój,ª1µ;Ôømý(ûG–Kƒ>ê3ө޾¸¹4Hä5æ}WN¡2ûêºKX›•’P ”¤$B1Ó¦ºÛ,«õÔ½xúÜC<õÆOÞýí^›»:Òè`mæ™§N®oì\[Z|ã‹òèK/ÿô_omÊÜPI”NAÌMJ‡‰Å±ÅÈ`ä0­0©1¶82[kj©/j"ƒC;€[€Û·w·%ÆG§9LÆÕnMl1®0q¹ÝߺWyoŸ½õAç¨tÆ·%nqܸ¸£p¤pGÝqÜÇúK2Uö^ä=j[à¶Ä‘Úm :G¨3õéè{¯ÊÝžÛrWÓÏ Ó} Žõ~Ï :÷ŠÎPîúɶÜÝv(wû{夯´úb_Açhuül¼7Þc‹I‰Ûe¯ãc<®v£~ï|ž8LÌê/ÕøòP³:©CR!³A×JWR° TA3äÍZªOŸ}ÿñï<ñâ¥þâêÎÆðùç^Ø›?…4:úåWýÏæ®Ÿ>ûö¼ôý7>¾±!x‡«ÃâöN¹µ­¶‡b;o¥£ÍŒ­gf9²ó‘Y»…Ø.ÇõRZ-EÕ­±^Šª…Ø.ŽoŒ¯YŠm¯¬7®ñnµœ¬Õj9YãÝRlçcµ™…D/'n)³K±½É¥Ø.$z12¾u>V¾ænéL{.¦f12¾çbj–·è…±ž•oõÊ+iå{ÞÃüHù֥̣ÕY§8 íé´õå^V{?9tÜÆz!Ñ~/{}fƒaŸ4ý¼é“Æ{× hï[Ø.ÅöÐ8]مخ$Íbâ–¢jãGx^‹©éç÷›cXŠírîE»RTƒ¢í“ft}Ò¬–“U6ñûZë[‘ôì÷tî-^Vh{›ã*›xµAÑúy~:Lç ï?Açu#³˜š½ñ¾Û¥Ì.'Î÷YÊìJZùmSÓËj?“Oçÿ}sþÞÖåÜ}£:+±ÙÈêÔlÆâö¸Ü&[Ãäö0^fÛ¹ÚÌÍÕäô›ÿü‘'gϾ{óÊÜàÖòóÏ…U ƒí·™çþ»Ýrûmâˆâ0o¥J<ÐÒB«‚Ú*í ­h!Žãµ×—õ'TPTµ/•Rh$Áq‚¯ñ5f/³W{½;»;³ööᔕ•ËK2Ð=Y;g~ç“GsΙùäË—‡µýÊÞj*óÑ•¾½ýð—?ö¥£_ïHǽIw`´Gƒ6?lòZKÀMÉiˆ¸.Ø ·ÛBnCÄ5ÞjË^[öÀ<[ÕÁN“?hÕ¡¾×AÏZÂî±Té«MÉnˆV]˜6D«…p á†hÕx³-;mÙ ðlU{o„S¦MÉn!¼Ø”ìSSÔ…iGõZ×…éÑh|4×…i áŽêEœårö»2ŒJ_}ÑSö:èiãÕã£ÁEç^ãÍÅ 9•¾º{,=k {Têm§ÉoU{.çü:­ñÖbEC7%§Ò×—²¯=åh4†Z®ôÕý®\êuaú¼-î4yàìwåÝci¯ƒ*}b!Á`€'â¼_œíú2¤ÒWwšüßµ“çm±:Ô«CýðÄ€æãðĨõˆ³\è_¾šÆ‹áMÉ®ñf]˜†×n7›’ÝÕH aXB7!°Æ›àq á®FÞ2Ç© ¸%8í‘Ù vŸï ùfŸ?<>Ù«*ÇâÖóVœ½åÊl’«ïVùvïFôŒŽìŒ]úúó›½öñQíàÃ~|õöw?=ø9ýh{p'UŒ³©ôo©Ì¯ë¹l,ÇÞÍmÆrc©Ò“_còñda=U\OãÉB,Á%ØR8 —éB<Å1™b:¿‘)l²\9•»—ÊÝKç7â)n-™[Kæâ)nͯ³ùxŠ‹1ÙDºNÃ¥7È •c²kɬ†ÓEpÖ’¹ÕDf5‘qÄY:'ß`¹2Ë•Óù øñEçc²‹æL:¿¹ÄråLa3ß`2ÅxŠû`9çÖ)[Š%¸ÅŠþ·Æ—·¯u6ÏdЩܽd¶ÓDº%ÏdŠÐ Òùd¶”Ì–2…MÐ$DEœ÷Ž“HX®œ-ÞÏ6™LqÍ'³¥lñ>,1™"˜L<géœP¦A2[‚[2tfè §8à‡ž^ ‹ü·ÊYeKw³îr¿Çrbl9Á–XžÐÆ ”IDAT®œÈ–ï$ kÙÍôæë܃ïf._¾–XežìŒ­èÙY»tóúWƒN·Z­Ü^¯ÜJ?­}É©ö šˆŠ%k>Ò!_`oŠãû>ÆØ²,×u)¥N ?(¥®ëZ–âP¬FœåràÛu]ÇqÇñ<Ï÷ýù|~ѹB@0›Í(¥3„Ó4Ç,()¥A|èœÓuJˆïûþ|Ìf3JgPRËÝ—ëºcÛ¶mÛÆ»®K±, :—mÛÓé”R É©B)N§¶mƒÆ²¬ˆónrÞµ{!âü7Žs½÷ËÔžL1Xžãú„Î<â»õˆïú6±æAмZYù>™`ž>~Ò¨Õ¿¸všÅmæx:$‰»ïÍk€ˆRá*æhÀ<'BEŸ¤ÇÌñtH¿ Pà´éI†èKóú2aá ÕâÂ?(†Jû†5T¿|-^¼˜V5®¡öŒãu´®¥††jllÌþ544ц¢Ü} ÔÐØDkÖµª¨©¡ž[Ö;Ü·•š3Ûr³ëí²NGM²Ú±¡…V­j¤V%K|8­yI }’ž>‰Þ““ëÚׯ¡†úzªol¡¶èÕãh~@1P:Ö­¢>UUTåøë3¥ž8ï·5׿l—2ÊS¼öͬo¦«® ´.cîk—YõT/TNVPR“T¸Žj²Ì}¨)V¯…Ó¦'¢/ÍëKG~Œ`5i±®m7%îVU/&õ0¦ü€b ´6Ù²x ›l ™}8ñ´[ËB ,fÖ¯¥ö¶Vjmµÿ,xÉì›ÂûÚÛhmƒ]ÏÀÚfZ·|‚uÜâ56=´,aÁDÃ6Ö™õʶ54Å’‘嬦f î„ äG¸æI~Ÿd½<’•¤Åºæš>VܬßÐFM5Õº—#QN§Jóû¦ò¾PÖN–SÒ f*ï¢)‹ëiíú ´¶¹êš­Ë85 ë¨CAH[ÿÙ€d_­BkU«zúÔ4µ6Ò@µ>×ó×0PT/³.Éq¡ëÌœu[S%¯=ê@1?éT^`CŸÚç% m*KZ¬ÛÐRO —5Z£Ùëë§PåËBm¯óq”@ ïäg%Þ°–׌¶’¾=”؇¦,k²/ñdF;²CŒÖ¥›k˜Q@#g_Ÿ´Ê5i§å#x„£†Ö®«·.!M©·ç¤¸§ÊñÐ¥ë¾l™Óno¥65„³f1ÊP ;è$©:)qhëT]Âb]FþÖ–eöeø ˳ó;ZÉ7$Õ†J'7Å@Ù°¦)3«ƒ6¬[CËgV[°²lm{jšÔµ…AÖ˃ôIDAT,üg{‚ÀÄ”UÍԸМêZ1±?òke`5Sjã ©SêæeËB–S×ëÜÝzéŒIº„ äÛ@tžË5'1Ö­]epœ²“dåË|›*ü;%£uÚ5‹m ™°¸Ö®m¡ÅSøZgÕ¯ïÈÊèÚUÔÔ$wÜ4ÒZu대ÆÂû’Oýu©¨~-p{ °F\ÔHÊ„UÖˆ ïã ªÓ¡ JᎇÄÝ™i`5i±nC£}9›ˆ-SwS®ZÕ@*,ÇôP êXO˦Ø"—kf.o±œ@`B¶Ë²¶Y]fÉ\þYØ’™¥Ú±63yuDfâj5Ì´a§¶IÏ=—ã¤.Y«Sûf‹u‰GÚÒÛ£]ƒÓš™ØÐ/éè—h½7Y±®–Y—ÉùR¹üa’lœv@1P21A&ÁÆèÇ¢hªG"Œ3@ ­J³·h¼6¿Vĺ|¸}¯4Ûë|¾ƒÅÍD±-_I: ”Êòý ß˧Rõirë PÊ絜\gCEß™o9%ŠDð %׊®R8­ùI}”Ü>ŠÎsQsX À(”°^SæòpÚä&?ôù}Wf÷Fó À_,@Ñ3”e¦rîÒË©*å8‡Å`µÌp³¹JµO¯óÆvïÀîf?Îm^ºs–q[OËqevo4ïÐ@šíÌíÜx[¾‡Ö«^ÈvGÑœUÙïµÌ)ìøâU^¶;Šæ¬Ê~¯eNaǯò²ÝQ4gUö{-s ;¾x•—펢9«²ßk™SØñÅ«¼lwÍY•ý^˜Ž/^åe»£hΪì÷Zæv|ñ*/ÛEsVe¿×2§°ã‹WyÙî(š³*û½–º°wâÍwl|wו—Že»Öuîšì÷Zæ–Öß¼ÊËv]2wMö{-sKëo^åe».™»&û½–¹¥õ7¯ò²]—Ì]“ý^ËÜÒú›WyÙ®Kæ®É~¯eniýÍ«¼l×%s×d¿×2·´þæU^¶ë’¹k²ßk™[Zó*/ÛuÉÜ5ÙïµÌ-­¿y•—íº¤í¿™KØT8 ¨Œ£Êka€@Ï–×g¡ÿÂô6Ö©òöu&ïßnö„1fŒÐ[0½…óÁÂ’3 Æú5f 1#Ö³Kø/ôTjë(AÁ(R™m ¬Ó6 ƒ´pzFr*urB}°©p>ˆK¨ A} KÌ8ÎÖ[=ë2ñô}%ùblJû—ѼÿÏú,¥A‚õî»ÇÒá½w¦]·ïA#NÜ‚.<­»õÇë¼í°ƒv²ÊTÒíØaÀQ¨ѳ.“n›‹3æ!ÖÁ–ÄÞ´y£‰½'(v°ó/¬+…¢EÑÅ,?ø –Ò‹úì»=rQ}Q§ž—·(÷·ñ>.Ãe?ûìFÕIÐu ýËß&…I®Þüu¬û¾V _C¬ƒ9íHû—¿ŠÏâI²1'} î¤ïôíNæBI>¤ðw.³×ݬcØÙõt8X§çŲ`ñŒº/Òa[匈u°¡|ûÓþåï‡Û(1 uòh‡Œømc ácq¹'}NÖiÅy± ä'S¾#Ö¥/N•Â¶ÂÆ:JŒ€r×]cèuÉ&ÈÈI>¬ð1|¹‡ç­”ÂPP‡9$¬ÓL 9Æÿ—›î sì#‰¾ŠXûq³[í_þ~(1€ ð/ žôÊóJlW>íôIDATòá#èw>–ëÀ(Jºœ?ˆÓê2’h±” n鵞ÓecnÉ"ªmˆu°/ÛÒþå퇺ŒzP›íÜÞ…y>Š÷R|WÛù¶a¾3ÇmBlP@ác¹®««ö°?9¶ªýËÛu€I8 É×—·ŽyÖsrìÇ4_G¬ƒíxÙ¤ö/o?Ôeð$Y¥¥xŒéöÛϧá'l¡š+|…å[ù9)qÉv¢·íÁœ¶¸Ÿ°+ç{8=Gßïiõ-Ä:ØŽ—m‡óAJl‰~Μ³iâ©ÝU¿(ü¬~ ›—`{ò‚CX§ Uáôœ<;2Å÷ë`;^¶Î(±%z8-œ¶TN @ xÙ’ Ûëë¼ì€Ó%¯ðÚŽaO8­—m„uZ ÅË–LØŽX‡Xçe‡ac*GÝ{)³”Û1q NëeOá|°°ä ¨±†‹­I°Þ3}0IÖËFÃlG¬C¬ó²—°±€Óˆ ßzÇïÝ)ö6c®·§+„uZÀFaNÏé²1¯„ÅvÄ:ØŽ—]…óAÌA‰m w^ÇusÜ Nkÿê/,1hl½ѳ.[u³Õ Ûë`?n¶¢ýËo“÷e}—xÜÅ6þeqúÉÒ`õØú°wóðãññ¨ût:}X§çÅ2°ѳ.“N[‹"®¹Õ‰Xûq³ í_Årþ…u¥Pº›ÒÃlãþ±>{*àòÈ{.ÃïáácðFãtÚŸö/?„w}ùëX÷E:m-L¬*¶,bl(߆´ùû¡ø.ž$ÓgG1hðH ¿—‡ç¤¸=]–·ñ>.Ã#'x“qz=¬ÓŠóbY°øFÝéµ7g,Šz±vä´1í_þ~(± €R@áã!P¾NË“^ùñõü”Y~ÿñ:oã÷îp.ëìd¬§ËéÃ:­8/–”$ÄĺtÅ«bl.l¬ ” P¤“Ùyù¶¼Ë.;ƒ¾úÕ/ÓÎ;oMÇ¿?Ýyç&eî飨—a`R˜È1þ¿Üt_ ©”Úîë`SÚ¿üýPb¥ŒI°µu.]wÝ9tè¡{RïÞ»Óܹ߳æšÌ›÷=:ä=èàƒ÷ ^ç¹A)c?•:Pç×ÄiuI´XJ ·ôŒZÏH&ùvZÌwÄ:ØÛö/o?ÔeÔmƶs{æ=ú(¹'•c{l{î±´ýö[ÒðáÇУ^¤´lë–u-ë\nÄ]®±ñâì>)ƒeòmRû—ê^.0 $ùúòPpf³Ösòíʄ؀X;rÚ¡ö/o?Ôeð¥¥x Èù ‚GFx´Ämd„;B]ÈôIDAT'_&9VFU¼ŽÍ?ßãéÛbõ¬2˜Ó— óvå|§çdØN±¶Åñ¯xd±vä´±p>@éNe–bÝï„[ýn€â,¶>ç±X73X„uZJaPNÏfÚŠÉ>66!ÖUž…óAJ$€"¿ ñèÊi%@I]ýJ‘òXš Â:-€bŠ?KB¬37¾˜b+,GØX§ÊãI²¥êÀü_…Ìáþ+´»Ýv[аa¹sZÂÖ…òñšp>XXrÔX¿Æ¬é}! óí‚ú¿Ä™G‡XÜÚW&• ëTyJ1XÌ/·v¹?ܶÙöî»ó¨¶vhö® k¯=ÛužKºP&¾€Î(…ÂV8=Ç×ÿIñ5Ä:ØD±¶Îq‰§`à_QÜaS  8çñÇ'ÑyçGU1?¨„uÚBt¥NÏæÛÓߣ\G¬ƒ-”ʾÂù % äÿ‚ˆâ%¥1(·Q•ÿýïÚPç-uaM  â´ªÃÔ£'Åè ˆžu™hú:)>„XWÙý•jÿ²#š÷ÿv¬Såq‰§«Îˆê„[»ÜnÛK±MFUøº1ÏUyäýü•RÔ: jA|Ðvd ¥0 ꛈuÑê7h?¤µ\ØX§²!=ÿ®´r:A”㉮QнŒªvØžtÐA»ÏUÁ¨Jùl[û—¿“œq¬À¿Žu_”ÏÄOãZ"ÖUN_ÇeS^íhÿò÷C‰W”¼µ9Að“Bf§{uNíqŠSŽ'ž¸Äš«Â£*?øÁÑUɳ§®¢Zë´â¼X t„YúFÝéOZˆuAŽ*¦yÕ«ýËß%¶PTB*×/·NŒPDU¹þú¡$£*sæ`TEtõ2¬ÓŠóbL¤¬`Ô}‘N@A¬[Dˆuå³mí_þ~(±­¢¥Ü¿ Ü_¹Å) ªŒù5ë Uyøá •5•ϨÓÞvX§çÅR #ÌÒ?0ê¾H—½#Ö¹÷'b»^¢Š¹Ú¿üýPb[Å ¿ÿ¦sK‚v¸ €"²¾÷ÞuÖ¨Êá‡ïE½zíFU‰Æ™ƒ8­.&£¬:½ôŒZÏÑôµøVKĺà}ˆX\WÅØ®ö/o?ÔeÔmƶãzæ=ú€xN¢x+¿ vØaKëù%qÏ-ñ’+;ë:› ßÿüg=ªòýï÷èJ G”´yû¡.èаQˆ.¼uÌ{´ž뢎;ˆuɵ±BmCû—·ê2){ÊÆ é•W® Ûn;Ÿ®¹æ,ºòÊ3­Ûi8`WêÝ{wkäÄí Â…*»ÇAä ôIDATåËÌÃòóyð¾R´QÊ:ä—ßý³ûîÛÑ·¾õºâŠj£e.åùGQ—vÈ`N[\‚.$©§ã˜pz6;yäÇ ÄºÒ÷b]éuÎS(ì¬wß=–ï½3íº}qâtáiÝéÄ^Ýh¿]7¡í·îN‡´“UÆ”¤ï%ó×èfÉÏçaºÌß쳺W7KϬseŽ(J]gX§ Láô\úà\ »ñŠˆu¥í¯|=#Ö•F¿á|0€òÁµ4è”^ÔgßÍè‘‹ªè‹:u‰dQîoã}\†Ë~öÙe™€Ì¥1öR|êë´”ÊÄxâFõlB "CØX§Ê'÷I² ,ÿwúv§ s¡$Rø;—Ùk‡nÖ1l„AZê2•"sÏ2ë¹Ôýe}á|°°ä ¨±~Y¾ï}!ÍÜ9(•7Ÿã°(ã™_Ýac*ŸL@á!8 aƒvƒ¿m 4|,×á§ÌRïKªÌ§Ÿ| 1pøéÔm_¹ô\ê~‹º¾p>@)¶ÂéÙœD‘Ô¸ølŽ EÂÖÎ|‰ç®»ÆÐê’M‘“üäÉÇðåž·T±¥(™á°nvÖi MЕ~\8=›c«ˆñôEõìOLÞÎ (ü‹‚'ò¼’|øúå:âEÌñŽV™ì¤ù²qZû²FOЬ zÖeâIŠù¶ÿq#ž¸‘D=çÛJ¾kÿò»ÐÊûìX§Ê'ïß‚ËwŒ¸Mˆ (|,×ÁuÅѱ9=ÇÑ—¥n#ˆÚî @‘ÀUÈ2ˆžu3q#ž¸‘D=—:ÅQŸö/;¢yÿŸÛѽ‹ñ]©N{ûíçÓð¶Pú,|…å[cù™#qt dŽGÏqôe©ÛÐþå$e“uþ:Ö}XW¨#Ö!ÖyÙŽö/?”¸¥2<;°ÿGWj†Óò#×'žÚ]é 8@ág¥ðݼ”YÊí9=—²ÏâªKû—¿ŠÓb™a¾ûëX÷b]¡öX‡Xçe;Ú¿üýPb€â hI 4^Žaòö°N+΋e0‘²þQ÷¥PŸIbÜH¢Ì…öO9Óþåï‡Û (BŒg1‰z.§óÚvX§çÅR #ÌÒ?0ê¾0P’èƒ9žø\h¼)çqÚ¿üýPb["%‰š s<“ÝÊé|…¶Äiu™0Ée%Ðé¥`Ôz6P7â‰IÔs¡ñ¦œÇiÿòöC]FÝfl;®waÞ£0Ãiù–0~ïN±·s\W™ãÑs}Yê6´yû¡.èаQˆ.¼uÌ{´žë µs^®KôIDATÄ:Ä:/ÛÑþå퇺LBŸƒÂ'ŸÄ‡ê@f3‚¾—ó³_ðÜs—)¯ ŽÚ!ƒ9mq º¤žŽcÂéÙ½Ÿ}ö—ôî»ó êçBlƒAÜpï‹Bõéu\õìu.¦nçƒ ¦t~û`õØú°wóðãñËõ¨û$Ê̺âÇÖ'EÏq:gGÇTW7Œvß}[š>ýÛ%®°N @) ˜ÂéÙ=)ro¿ý–V_Ç*ˆuî}Qj?O¢žK­ƒ¨ë çƒ V$ÿjåæ÷Äyä=—áDËÇ”ëÆ9ž`µ£qýüª„Þ½wÏ^ qW¸àèn§ÜÇROœ ‚¸áÞ¥öó$ê¹Ô:ˆ²>ñï±bçeÖ„ +’AƒG%ø½<<'Åíé²¼÷q `#Œ²ºª2Çlºê‡B÷?ñÄ%tüñûg•8¥²Eú=.PA܈'n$QυƲ¸Ÿ©@aóÐ_?äI¯üøz~Ê,?Èÿx·ñ{w¸ —»SÜÚƒÌñ7ݺíŸÿœAßýnßN`"N'€ÂßeÛ òËÈÇ.?]¾æ´Ç£ öþi—ËȾ³¾»ÖëIþ¦OÏÕ¯|×ûõå:[¿öw犔•e ‚¸OÜH¢ž kq'¾’ n.+ºL FPœÊe£âÛÅø>|~ðÿñ:oã}⦬'Yæ[o=¾ùÍCi—]¶¡³Î:’^|q†±z.¦¿ßzë;ö$êÑc“l‚'r.Jx›¬s»A¾sùØå§Ë×Ìñø. ‰R?\·]x@‘cãĺèaEâs¥ÄºbâdcÅGÄ—Ý–ºLÊ%ˆ‚P¦4N}ß}?£Ø•† 9Ò è<²Àßëë¦l®4m˜P'}÷ÝÙ:Gq¯¥JÂÈ.õ¹9+¶Å«Ýî6ì7‚"ÇÊòCö ×^›*c×i)[)±.ŽþßèÚ«íËŪ|òÞf‡"ц{€^·îJª®>‚kVµUîW¿L|\Zúï‹/ê¬;w¾öµÎdYÿ8’„½]Å:/@ÙvÛÍiܸô÷¿OO½§ÅoÞb]tùFûWWÑ€‚@ðRŒsˆóÕW;Y;E‚—Këe>G¾“‡Ïo“MºeGUÊ (í¨©¡­¿††FjYsù—ôIDAT×jG¶uÔèØÇexÿÚÖ¢œ}||3mhÏUg}µ¬oˉ&­k©Am_³Ao}Úims-[¼˜–-o u¹Å‰ÚUûõõÔ´fC¶ž kš,D^[¦&zcC~Ý´NÕ½|ÙbZ¼l•jS„SUYòÕSs¶ÁZÛ¤ÎÁÑN¶Á€+:€ºé|@9ì°=é†~@mmå½3PüK÷~ ªĺâôוžµu奡h;#éõ» qº“ H9¾ ”ÆË>r~/½4Óš@»å–›–u¥­Ù~&Y._ÛN­M5Yˆ’í¼\µ¾ÃcßLZÇìÒb×9³Qƒ‡•õ S¬ú¶0‰l Új}wŒ]ÿh²vqaõi˜ÒÇn¿O-ÙìÒFµ™KËNyªªúн÷äÖ½pD~ÝU´lM¦–Œ|U#–‘…-kh×[Ód7\Àÿ"ôoþ’…'Kï{_¥‡¾Pµ€’ ÖÅcÇÚ¿ºrN ‚‹Gpå!N¾<ãv9Ç-±Ñ¹m—mi¾ì#çøöÛ×Ò_ÿ:ÕWR6Üi½zÍâ úЪµ­ÔÞ®F4êm(™°|-µ,¬VûRƒihokS¿öÛ¨µµxü#»o½Jñíëiñh‚jjR °f™]gC.ŸPSÍ@Ufµ¨C64ÌTëê˜5´VµÛ´©¢ õë-a;Ö-·÷s™ª)´63èbÉÑÚBx{õBZÏ2µ©ãu¯[5Á>vÄb…ADëê3m^nÉnŸ3׫΋Ö×[åkš2#G–áþÓ}á°››§Òúõ5ªR÷ýØž,½ ÖÅÛ_Ú¿ºòK ‚ŒK•!N¾|áv9Ç-³Ñ¹mÏß–öË>ùçô{p§õrêvZnÅZ›)ÒÚ²ØJÖ£—ÿƒ–Y££©¹µÝ‚†ƒöv&…ö̾™dãQ£ HSh]N³­´p ‚‚ ©Uý[hžŒ ,´¯¡Ú)ShqÕF‹­¶GPmíh $|²ŸÖF¨l§zaKf“[Ýj4&{U§•jûHÛ,_fdFÕ1zÙZjµF‘úP>PeÛ °¢û"ÞÀÔVP®tý‚XW:]µKí_ÞΨ˨ی1I6þN Ú™q– :Äé&”Ûv¯mi¿ìãuÞ^ÛµCz;­ÿžu4Eõ×Ó§O¨þ¬:N¡5_dBfŸ´ÃËËÖ¨9k²ûªGŒ°`÷U×6ªŠLj´"Ë,D{‹ue _Fik¦jU~`m³«x­Mr‰Hh¬_fÉd_²‹Ëe©Z¡—º«sênËÀQ­B#[¾>£gÒ†¤3iY-å•«hžEG^}…<ðóôIDATíÉ—ˆuåëCí_ž.hÇ.W2¥´›wqç³ñËwb Ñè>ì§[?°Ñ¹m÷ÛV —}üÎß¹/ˆÓúù§ºÎB}f®¢µkêíyê² _èèX¿ÊÚ7bq³‚Ž{E]ŠáñÙ7pÂBZU¿JýñDÖÌXJ¦N™p~Ú×Ú 1³A•ËŠQ—a—ÑÂeõ´¾c=ÍäÑ%WŸêT=І¦)«Öe«k±.K ¤ÆÌ·ºG«KTÙº„Ã#.UêÕüXûœùRRkcæÒµ/¨²[Ñ}¿9ûëñê±.^}»Ù·ö/oÔeÔŠþä€òŸ ÛIc[aýRȧ›®Ù†Ü¶Ù†Ë>‹¿¼}ÐoŒTÔ6Û×Od^HMÓÚÐhÏE½PÝíÒÔDMêï˜á;p6dûâ59c$VSR§gÝmÓ´–þ™²ŠgѪQŒ™ j>Ê2u÷OÓr»-¾üÓ¼|‚u^S¯RÏÇ©§úU‹í‘—ìˆHçËK2çĪ»CF~캛–Y£5ܸˆ|ˬI-RV -V#CE|t<,̧‚Ø<Êį[ĺøuîfçÚ¿¼T— (-™ÑqqÊÁCœü05çÃÖŠi¿@‘v+ù²vHo§õÛcOtµ'¶ZåTr·.ùT/¦Fk‚¬=’!íð’! ÙÚWM®Éi¢%3ÙÕyL•ª¯Áš£2šÖÈd×u 4:3RÂe«§,£õjÊh—œËCí´˜/Ũ:,Œy4$Ó²=AÖ»î>#j¨9sk³}Ϊl†­Ö,ã9.U伄”sB¿ÈùŠ]b™ìøˆXgVÿiÿòvH]€¢´dVF)sˆ“¦æ|ØZ1í²As¼[©—}´Cz;­Ù{:¬;ƒÚ¬‰·¥—Ôºã'¢ºó¥Õ}Q9qAü/MKÄ:3íWûW¾çéïº EiÅÌŽ,µ\òú0wç• *h٠圗}xh6È1I.£R;)Öʣݕ’ì7^²#Ö™k»Ú¿¼ý[— (-™Û™¥M†8ùaiòîœRÔ묃 Êù½Të,/_†âwÿðy”ª^ÓêÑéí´Øt_¤;.˜æ¥±Î|›ÕþåíϺ EiÉüN-Dƨ†8ÝdaƒrÛ^Šm|Ù‡ßýÃKÛ»}D?Ú!½{âÑ€î‹tƱ¹4-ë’c«Ú¿ºòç̃ڂ Ë$GirÀ°çå§›,lnÛK¹-Í—}´uå´Øµt_ Ö•Ò£ª ±.Yvªý«+OÆ“d•†’Õ¹]Éǧ› qŠ´›ÆË>Á¶+§Æþb5 û"]±Aü'-KÄQ›ÜàôIDATºdÚ§ö¯®<€¢4”ÌNΗ;Î!Îü¶ù{œ€ÂíÉeŸwÜŠfÎL|þnr%e[X§µŸþl;0ÖÃêÁ?0ê¾HGlHŠ•±®²b›g÷ÿÀiÍ Vqqº’¸EdxíµÙ4dÈ‘´ÿþ»P’ïöÑþå‘°0âVÞ_Ǻ/Ìõy±ÿJ["ÖU^¬ $t¥\CœnA±\€"²<ðÀÏ}·NŠþÉ€âa·ùëX÷Eü«ÜKÄ:m‹•ë( ”rqº«r Ë”äË>:)z'O]&lBFù\°óÖ1ïÑzÖIÁÍæ±-zý ֹ븒b%A€b§[`6PD®$^öÑIÑ;yê2Ž\à«oPÜ¢øVœKĺ®û¢bŠ{ö{:üÜV—éZiqq¥´eÒ§›ÎÙ>ܶ—s[’†Bµy{¡.6!£¼Öa=#Ö•ÃëÂÛ]šcòYJ91H›&qºÉÍ6ä¶½ÜÛ’2Îa—áô>Q”ÛÞ“Ü>b]qö–ÖX§|€b¢có'?æ=Šwç”ú|Ù†J]g)ë3}(4œP‚‰”§çâF)í7íu!Ö•ÎÖÒë”ÏPÊ6n\H¯¼rÝvÛùtÍ5gYÏõ8ê¨/Ó—¿¼“±·ÎæËÌ6Äòóyð¾rêÓ«m–ë7¿ù!í¶Û¶tØa{Zz6Eæp>@à» §çÒ% /›¬´íùqƒŸa„XWz;KS¬S> @)G `#ºûî±txïi×í{Ј·  OëNý{u£#öéA»l×;h'« —-‡ŒùmzÉüµºYòóy˜.óŽßœN8°¨ôÌ:7Aæp>@ &R>œžKŸ8òý©R¾{ŠĺÒÚX¾žÓë(e¸‹çƒjiÐ)½¨Ï¾›Ñ#UÑuêÉ¢Ü?ÞÆû¸ —ýì³Ë )¹´ÁÄ™œÂ%NŠGØe8=G×ßξOû:âFh;2€̦âŒnm%5Ö%1>'Iæ°±Ný„ç çÿÑ•&Ã9ÜÆ„mwÝ5†ŽP—l‚Œœä'|>†/÷ð¼•8Ï2GoóÚ¿üý°˜äŒcîüu¬û"ú~Óãn q#ûIšžµùû¡Ä+J£€Ì«ôIDATLsPøO åy%ùðô;ËuÄ5Š™ã­ ë´â¼X t„YúFÝñ$˜¸Á!Žö7â‰IÔ³ö/?”Ø@‰ Pø\¾cÄmBlP@ác¹®+Ž@™ãÑsX§çÅ2 ˜HYÿÀ¨û€RhŒA܈'n$QÏÚ¿üýPb%&@¹ýöóiø [(Ÿ/|…å[cùù…0ÇAæxôÖiÅy±è³ôŒº/(ab…³,âFëÜõ•ï Þdé;œ–PÖX€Ò—ZÚÝ”UK‹«ùapƒh½—Ú>¼~2­U#-~Ç „È ÉÒ (²Í^¡VŒ dÑ­°ÍÔ`&v¬:Ïó£Ë–œ5V°³~ y*YíÐzv÷¿˜E„XgªA.w{Ž[/Ú¿¼½P—) ´,ä‘uéfƒ(a5¨É²õÍjôcý8ꓹ¼3°ZÏS©iœC~Ç  ÌT#(íÖÊÄÎ#(MsÔYÖQGÿIÛXÆmth¯8›ÓÌi…AZ8=»÷©_ÌB¬s×âô"6ÎK(ëÇÛR=˜Z¦QýÂþÖ/‘>µ“©¹Ö†—ÚUꮾó¦~¤U#FÑ>Ç  øÍA];Žššì;y&QöŽŒ¤¨L§HŠÂ:-¥|€‚X‡¸’”¸b¢œac*Ï—\ÔÍ<>]ÆÛ8[׌sL„­¢ê™c¨µcMàúGŒtܹ³ˆ­;~zSƒºÙõ8•\P¶èI²<Ѷz¡žƒ"rɲ¶yž: o±º1ÑÄ~}\Ðqé¡°ä ¨)Í%¶ט…X‡Ø‹ÜÓ¥ „uª|iÅüuö„ÕöÜg t = ·kÝBG¦ë(ˆÚð8)´‚èY—éÊo Y…ו<ØoºŸC¾EŽZ~?ÇxŸë”?–Pà$0BØ@X⃔âá,ˆžuØqX;FyØLW6 ý+  Ø¤â_XWŠèª°6Ö´ùû¡üªÀ²Xñ×±î ØqX;FyØ “;¼ôIDATLW6 ýËß%Æ•èI²è˜®:ûa#^6ÖiÅy±,Tü£î Ø«—½b;l£PÐþåï‡Û(˜Ø¤,WN„uZq^,(å´[´¸ÖÂÆ: ’3¥Ì6Öi&…€‰ãÿËM÷’OØäƒò°™®l@û—¿JŒ ”99uÕ¡ØŸ~§â´ºŒ$Z,%ˆ…[úF­çôÛb ú8nÐþå퇺Œz4€íÜÞ…y>w‡¢½ôÛœö/o?Ôe&á€$__Þ:F¬K¿¯!ž–·uóöC]¦DO’E§—·Ó¡ÿdë_;d0§-.Aç'ìÊùNÏɶ)ÄôŸ‰6Î(*#À¡ƒòÚ@X§ Uáô\^›€OBÿi´p>@ ÐÊna€@IcòÂ9¥ÊÂÆ:UO’…c¤ß1Lîãp>XXrÔX¿ÆJòÞ1“m ²!–™lac#eA0Ù¡â-¬Ó6 ƒ´pzF¢‹ÃöÑFeÙY8Ä%$gZÙm ¬ÓP(Hì••ØÓÒßac*ü”ÅÒÖô=”Ò)uñ),1hl½I_ù)YÊ` ߆ Dg~>hïËú¬-„ßè¨è: º…nÅü|Ðé´ŽÂ@-ˆž¥ –ðKØ@t64ÖzP[וÅ[B gÑ3U„?è ¬ ˆýÄkµ¥h­°Ä  qê­ý_b«amåÙÄ~â³ØRµ”A±ƒW©*§Q:œNXˆ ˆýÄc­¥lÅ™h±^x•²?¢¯Klµ;Ç1ˆb?Ñ[j©[ `ô¥BG ’î´…%f ~Œ!aW´%=ÖáO…&éJsTçù&Ýi(ÅÀV©éE[ŸØªÓ~±Ð jb?ÑZiµc¥ÓJÝê*šXWE“nvü-­¢xûü*šóTÕ5VÑdµ¶Z:ÄíX®‹ár ¬¢ñ5U4rzSÇ/`0Rû&«2—¨z”2Û&©v=Seg©c&:åTë?¯UuçÉ/2ˆ|ü}^F¦9+ìó¬–žçÂò©ºg+t*£t2­>w»ëy*y¥}§nóeã}¼m²:w‘ÑÙf©Ö“è´Zæb’3Ž"”FY§ô{©l_êqóGŽ9ˆuˆuQÚsºÅæù‡FPTò§­U*Êq.¯ÎlŸ¬ DÊ Q€ ÇñR¶;ãõI꘺{ª¨§Zwîëy¾ %c†ØÛǬ´ë›1Ëþ>H%¿ã†äÕç¬;epÊ7.C{Ùmõì\Æ;`dÒØÌù ÈÀCˆÏy:ÛwÊ—/›sߌ'rår께ui#ˆ³˜RÄmâôIDATFË È(nÉ” &‡ô{1öîv¬—?"ÖÙq ±.˜}FQJlÞý%Ц¢©Sdvs¼b¶ÕÞaç`µ*©ÏyPý’çäžÙ>Õ±>TA‹³-)ÃÇ.Påæ©þã‘‘yKìzG®°œžEê¯/ÃÆ{_^d'}¿ãjUÝügÕÕO¨¨õ«nÑòçË ò±î~©äoµ«¾÷½Ôn[ÎGÊV£uj´e–*ÏÇeÏY{,Ë™ùgö•ס[/ÙX®ê~ŽÚÆJ¤.ï̪W nÆ«mrùgV½¿&1KÂïê8®{¤Á¨R€RëÓM9–£§ý˜“þî(CÔ%ncÞ {H¥&s>½¹m>þâ®ÏSÚ÷“m¸Ò ·ÇÐfÐ0ð¬m¼½D,3ÿ%é£e PŠ÷ñGÄ:; Ö™ ±N­'7X—*aI=â´¢^QIS¶ç±¼>/“TÇŒ·õ,ûÍÊM¾CúÙûóa¡«ãÜEÚ%Ë ç0DÁI•ÁžYž¬Úí}qnÀ“²r¼,'ª%Ö•Õ¦:·µ.—‰fe`ÎK^¯:² eÛP²‰þ¤J±”ó1Ç%»–DË @ äúk!>áæˆu:F§-Ö½úê«]CJ8cZ·;ÅÙ‰!2â˜~LjÓž«£JÆ5j´£V%^Ùî”ájÂY—”‘¹)<ú"#0<u¼àQŽ9÷Tц¥÷Q+u3Ôè o³.û¨r\wãÜÅM‘'äÊ|˜c˜ ä (Ö¯ uó”ÌÖ¥'5br½Ò‰/#(,7—õ“WÚ÷“Í©Ó! ¢ªT{¬3§žK±.öÈØ )¤e PŠ÷ ñGÄ:;î¦=ÖqÆôÄOÍüÅpÆ:µnwÿ!æì}ÿý÷­DÎr/|ºxGu&v¨Ú6I]Ꙥ®a÷Íì»Lïs;OißO6Ù7\]Úáz+ýŠ.J¹äºù/I-3€R¼_ˆ?"ÖÙ± í±nûí·§}÷Ý—8šþqÆ:µž¬`=|øp+¹°ÜÓÿ¯xGu&¾¬ÓÖ¢æôIDATzŒŽ8Eôf-Õ¯ý«2€’³]Éh]¶P££2P"û×äÊ.0â¾ ¹«ãF*hÈiC–,ƒœÛ4Uçø LSseFùŠÏËžªÜäû3p£Ú´n“ΜÜÑ3Qí÷’WÚwÖÉëNÙœû†Ôä^söS±ëÒŽéŽê”OË @ äÆŽBüAüÑ9jÉõÈvĺtÅ:‰œ?Mÿˆ¬ìçj=9€²téÒ,œ°Üçþ²xG-Ĺ =F&ÏfŸy’Ið]ÕWèq]ÕÕ~ÓåM’ÍçÀI¡p’ v‰9‹ÊW£ª·ÐPèqQGWõš.¯Ä:ç’ó¨ù;Ö)¹“(¯¼ò m·Ýv9€rBu²¥+cÇþxú3)6ß9ˆP(ñøbQ:ô,±Î¹ä<ÊùÔìOPl‡7[ÔÏ?ÿœŽ?þø8a…ï¹_:ŒÁ Þ~g5Ûêݤ (Iˆuù½.¶ŠoŒH‹¾Å~ò—œO9¯šûI 477Ó1ÇC›l²I'H™÷0 7-Î×yˆ³šëœ^’P(ˆwqʼn4´#±Î¹ä<Êù”󪹟Š(ñÍ7ߤ³Ï>;RÆ«»HÊmH³Õ$ÔQÓÕÝ9³Ô;ê.§<Ö;/”ŒÓÔí¹Îí¼îvœ”绬òjR+¿¯gòJ5iTmãwÕ¸¾ÏFÝýãöŽ i“Û£äc¹.Ù.Ki×)§³½üwä8÷‰ÎCN©Óí9²ÏOë8U÷,5êÃzÉÞJ¹úÍê#%O’>Zf@Jq’¤^'Ä:õ£ÆëÝ]ˆu]êŽD?ÿùÏéꫯ6Þ´Ì {›ñ9çœC·ß~»1N;M]ÖaeÊK9ᎻT]JÉ<`Ìë5~ÇIâϾ³F]ê«Úà÷ÝtÚ§€CÞ÷#ûòVç”±F9=Ë-e½äwôôIDAT”:³sRíYÀ¥FdäÁtY9ýdQ—¦øòßš @ +¸¿ø¯¸ä ¸ ®q3JJ¿—ËW¤]¿˜Åe¼bˆßq_²1±.²¶æßþö·tÖYg™aØ>RˆÍs¬Sëø|Ž0d×>ûìC/½ô’1€"W㞉#;—#ÕeÖor`ç;küާ•~‘¥Pd›,½ÞgòH€ÈŽ–¨mÒ†Š—œùå¸>«l?ïÓ9eù¬e/o+ÜõäÔYë"—!fH -3 £PHÓ: ¤r# ‰Ìqø…_~1‹óŠ!~ÇI|‘s”%b]éâ¤è”ùßÿþ7í½÷ÞFصŸZæÊÿû_Úa‡¬ó’ðs¨8öÉèÄxÇÄØ‰jTe¼ºµn¿*–Øì|ß—ÿdÈ©jä…õå”Q®ýò›Lýät«SÞ‘suæÉ¹rýØM–Aê’×$5¢ÄíËãè—®ÚûñsÓöi™(”Ò%°®|EöûÅ,¿âwœÄĺèúSÇ ;¢ñdÙõë×›ÞräÑ2'PV­ZE§Ÿ~ºu"râ<åZfS”LT {|&÷TP `àöΚì ÷\ާ•ÑE Rúª"ï¬q¾ïGŽËGÐ 2OFzŒS@2U×Ç:äÉ®~r^›iÏùŽœl{ê\óß=$û–ÆgóÏsRÊÕWÎvÅ~r¼Âð/Zf %~?B¬sÏZÒbÝ·¾õ-ºûvÎX§Öíde´ÄJ¸)S¦Ðå—_n‰)2;“N¹Öç©ËΉ°}Õ]=óÔ„Yë…|ƒrïÜ‘Yîü*o×ãÔPi¾Á3 pý}üÈ>9Y2̸îë¥ÚQuZm©u)_¥Àh*út!çOnr“±yGŽk{ªŒSngP¦ýœw )ÙÊÑo¢ÓmÞ)Ÿ–€@)߸Ƭ.bb]yúJ⪎v4ùÕ¯~E—\r‰3´·®eNÐÊ7¾ñ ª¯¯·”)' `Â’'«Ö*˜+K¡Ç…m‡Ëólx™T[Èñi9FìÇ8ÏôHË @ „3¥ôÝBcV¡Ç";bm#:nØÁåOú 0À'Ò”—–9!€Â÷póûÞyçK{r….Ž)op3Aÿb?åwÅàh™(ø° q$ 2è¸aÇš÷Þ{¶Ùf£s¯eN üýï§^½ze£¹œ@ 2šLÅ~²•˜ÀI¡pÂÇ%±ßEfÄóâHúDìÇâ>ø`zî¹çœ› \·c’ßü9(¿þõ¯iĈY%ŠÌI0Èh^`ûÉèÓ“ôIDATTbV(óü 1ÎÜ>q‹u#Gޤ›nºÉð¨—ÛáÍ–uôèÑ4þü¬¢t8†¹Žar߈ýd *1+”b% ±.ßÅVMö'ÈfnûqÚÕ7ÞH£Frn2p=A€Â/9úË_þ’U¢(Ža®c˜Ü7b?YƒJÌ €Ÿ79¶˜&›[¬ã·~øá†G½„ʇ~H[mµ}öÙgY…ŠÒM3È“Œà)ö“5¨Ä¬P(Éð1ÄB3úÉ-ÖuttÐÖ[oMmmmG¾„Ê#Ÿ´xSNy½bÝ 'œ@«W¯68ê%Pjjjh„ 9Š¥—³ãÑvr¥ØOŽQþEË H)R ïè<ñ¤ßo’oÊÙwb?yfE^x!Íž=;³ßµÌêÎ;ÛÙËUˆ!C†Ðï~÷»œ}råìx´Ü€!ö“cT†Ñ2P(Éõ=ÄÍxûNÇÜwçwÒ™gž™»ÑoZæ<¥gÏžôòË/ç¨NNË΢‡N‚ë$Ǩ ÿ"ýZ\r®l¸Ñ:4¼³â‰ÌX÷k誳®&e­®[·ŽöÜsÏüÍF|—þãX§Öí“1B²‚Âo]ä·/æä`ìñ{Zô-ö“oW&×2P -­C“{:W6‘9-¾‡óˆ7f‹ýäZ•ýíŠ+® ‹/¾ØmWY·i™ ~ë"¿}1ÿ#'cרӢo±Ÿ|»2ù»–€@ß§%E}:ntŽn=ôõïß¿óŽ2oÑ2 (Ÿþ¹õÖE~ûbþGN êÎEýé „b?ùveòw-3€’N¿D¼-}¿ê¸Ñ9ºñƒÚø!¨œkMúh™ ”¿ýíotÈ!‡¸êMN .ƒžµ¤Š&Þ\E“ü}Þ3¶AÍ^ZE£¦WÑÈYU4ãA{[ÝjuL}ŒµTuÌy¢³æ—›¬êš÷Tç:¤m®kŽÚ_×XE“• ³ÕÒ©g}ùÇH¹šU4FÉ:RÉ·c²ò*9&óHú;V­³\€¬zÉ6Þ>>3Šâ¥‘cª:^Öù8 œ”Núªõ¾ е!çùD±Îòñ_ò>”bÅÔXçg‡b«Qø[^~žõ_Ä:JS¬óð³Ëè÷(~COq;­8òH5¢À€R›I¤ÓÔ%–eä ã.U—OÔ¥IÖÃ3ûæ¨K\¶ÓˆG4† h¨Do°*º.³oˆ:¶NC-C@$¤~/@q;FÊyÌ<ÕÆ@rN59{ó¹*y.äüy™¯‘#ßiùؚ̹ôVuiÛÙN”ë,ÿ%ï@ äúcT~’ïçˆu:¦§)ÖùM¥(o|4Pxò¿m‘ߺèö‘•szÕ›ï´32€2MÁCþ1’¸hŽÍ${–{Z0¤¼”sBƒ´sUPä|eÉ׃ݎã:e»”•%3U·äU£C‡TÑ ± šÔßÄÌÈŽÕ¶*S£ÊU¿–¸¼ó²”È&@#í9vèxû¸þjɰŤmÑIÔKщ›m™½ €@ék¢ð—|?G¬Ó±5M±Îïf”òÆBƒ¥¡¡N<ñDOýH‚‰Â1ýêÌwÚij´„eÏ—KTB翉 ZÆ/ÕÆÜW%ÿ15êÒŽ¥à)'KIðÌȶ!  j®VÛ¸~u©SPÁ\Îí8çv·cf̲ë³Ò®c¼ú>\ÉÇm0 Õ©Ë9ž¥«¶¥þ8–,ÿ%ï@ h_ŒÒWòý±NÇܴź“N:‰|ðAáÁ€rå•WÒE]ä©0I0Q:¨[ÝÃy$¤Ÿ2Ô ŒdºÚ>QÁÄø °ôT Y·<Üê”rýÕ1“—(`P—vøü;.É>¾+‡ïâá90ÙãT›|7µO/—…ÜŽY¤Žë«ê®RÄ—¡f,Uð£FQ¸½!j]F9†ÎUõ©v&)¨²Ê+غŇÕôIDATɲù:9œN;U ·Õ›ÛRÖe¢.ÚvÓMTÛX&þKÒGË H)R’ÔëdÙ)÷}T¾àUo¾Ÿ#Ö阛¶X7iÒ$š5k–1ŽáŒu*JsÀ3ëÓÕ#x弜+ªí#yT ŸngÞŠÜI¥±.xœOB¬{ýõ×i·ÝvëÂãÛ-6Ï~®ÖÍ” ¯™Kíx¨/¸ã%YWb?ñ¹\ñ-i™(”ÊðÓ$ÇSd×qÃ?íµ×^ôÊ+¯øŠi¯–Ù@@¹óÎ;éÌ3ÏôU…œ€)F9’0Å~|̰Zf %Yþ†øX¾þÒqÃ? }÷»ß¥;î¸Ã¿PL{µÌÊ…^H³gÏöU…œ ¿|†ŸdÝ‹ýø™a;µÌ ü>Éñ'NÙuÜðhW]uMœ8Ñ¿PL{µÌÊ 'œ@«W¯öU…œ@œí×Öl5i5ÿ]kÙK]jZQžà*º¥ÈÕ¥¡W€R  ˜낚–تØn¹–~1‹eòŠ!~ÇI|‘s”%b]éâ¤è4ˆ½ÝrË-tî¹ç)C¥wïÞÔÒÒÒåÉ‹ÒËå¬Ò®ŒN¸½‹'ûhh¾´áxrë5aÔï8qÚÁê²Öº ,± 8È»yäñõNåÚïPUŸœ"‹ói¸aÞ4h®šÛ¢ž"Ëýe=î^2Ñ]9—b?]šq(”òø_Ìò‹!~ÇI|A¬‹®OÃĺ矞:è C¢ža€òÞ{ïYd¿øâ‹.$J/g’ã¶³Ž©dbÞ»x ÜÞks½‚ë¥|.ljÓÊèÆ")}r—xÜÞ³#ÇõÏ{7Ï jΉ¥/5ê1NÉT>\oãÉ®~r^›™ƒ"í…}O€ÍøóíöxNJ¹ûŒÛ·ô¡Î?y ¥<>„X§n,P1˜ß—æöN´4Ä:νÛn»-µ¶¶ ”x€¾þõ¯RŒ$’ëû)¼×Æõ8•<4ÄàPx¢m_²OÎ_– 3®ûzÙï²ÚRëR¾JÑT~·Nrþä&Ç1J>>Ì{‚²ç Ú”iŸßcQî~=26£ P(åóטÕE áwx¹‡XK ëH÷ß¿QÏ0@™9s&ý⿤Qz¹³ýBßOQèq惮/pLª zLˉý26C i™)ÅAŠ!P éw“ü°Ð˜Uèq…œ;b ²b?Í&OžL3fÌZ<’rZfuçíì‘´ªÒo}ë[t÷Ýw:FN ÃÅ1åûfŠîÅ~›!…´Ì |Ø”Xbº:n d¿ÿýïéŒ3ÎV8¢RZfƒžƒ²óÎ;Óo¼è”åL7Ègf û dl†Ò2P ­CC:5€"3b‰™±Äô~û `jV‘7ß|“vÚi§ Å#)§e6PÖ®]K_úÒ—Ÿ¬œ€éÆùÌ *b? ΀‚Zf ÅL¿B¼3¯_tÜÄ8sN.×GËl ,[¶ŒÎ>ûìÀúWúÇôIDAT€C˜çI豟Àg@A-3€¿OBœ1AF7‚1ÎÅœ“ËõÑ2(ãǧk®¹&°>äL0ȼ`)öØà (¨e P’çsˆ“åé37‚1ÎÅœ“ËõÑ2(Ç{,566Ö‡œŒ¾þøcÚrË-‰ss9>Zfå©§ž¢¾}û†Òƒœ ;:ÃN³nÅ~B…'… —Ä~™Óì8·èâ¸ØOØðÅ9™ssù>v¬SòÛO-— ×_=3&Tó"3 ;:ÃN³nÅ~B…(ø|šcS©Ï­ÐXÇ9™ssù>@±¾|b >œn½õÖPˆÒ±ìü(zè$¸NB…(ÅJ¹c]!&îÏЕ·®ÂÚçdÎÍåû(p½ð ¡ôCô6Dè&¸nB…(”àöX]‰ „ _œ“97—ïc üïÿ£í¶ÛŽ6nÜX>= åX4Àÿü#–¶ÒÛ¥Ò%N[~ùå—éù石I´e¨8'snæ]ž€òÇ?þ‘N9å”òœ?ZUüºuëF‡z(]~ù倕‚´@ d8ž}ðÁ´xñb0`€åŸ?ü°gYì¨, pnæ]ž€2mÚ4š:ujyέƮ‚2äÈKÀJð.½Ÿ +r‚ë<­%ù×1ƒÈÈ‘#ië­·Îúäæ›oNíííi=mœWH pnæ÷ÇëÊú6ão~ó›ô‡?ü!îóG{eÒÿRãË_vØaôÒK/•I2ó›}PŠ,óû:J _|ñEÚÿý]ý@à ˆ87sŽŽûãŒujÝžH·ÜÞ;ì@ÿýïËÑ4Ú,ƒx8Ùù‹Ml tçw–A¢ä4)º (Z‡Éé÷($e_cŸ}ÈrúôéQ4‡:ª 6X9:î9¢bëÔzy…I~ß}÷Mh×AìB5ÀÃÊbs¼Ü}÷Ýéé§Ÿ.´ºŠ9Nt@ ”Âèzè!Ú~ûís|‘/ûà 85À9šsuœg¬Sëå”Ûn»†çy£-4ÀAPl®_¿~´~ýz¤2_Ñ€R*keßcdÛÂü“Ri5]õpŽæ\çÇëÔzyeܸq4oÞ¼8Ïm .Üo¿ýèœsÎ!~ç?NyåÊ•Hf¶ÚXÓ°ôIDATO OЕ7Z‡f÷uÔÒýéO¢ûî»Ïj†}}óO¢Öz2ëŸ;w.ýä'?‰Uxí§e¼ÄóÕ¯~•þüç?ÇzâhÌ üõ¯Íyö Ì'Ÿ|Ò á •Âé´•…ž¿Ö¡¡ƒXo¼ñ†õæøÏ?ÿ<ÛÿhhiiÉ~Ç 4 xâ‰'¬Q6ùÇRûiÌ€òÌ3ÏX¿˜¿øâ ºâŠ+¨££#ŽóEÐ@â5àtÚBt¥§u˜xs(èJP>û쳂ŽÇA•§ÎÑœ«ùÃy›G¼9GùÑ~3 ÜrË-ÖõÎ]vÙÅzÎÿÒ¥Kéí·ßŽò\QwB4ðî»ïÒ 7Ü`Єˆ\1qy§ÈÒ¯ ]Wæ&Nn¾ùfjnn.³$h>IàüÌyšßËÃy›}ˆóxô;Ö©ö⛃rÿý÷g'HJ»›l² sÌ1bŒ¾ÇoaõêÕôÉ'Ÿ/ky P ³¼>úˆø6|  àK~œ—9?K®–%çñè?@±>úæ¸~‹œ¤s9bĈx@+Æk€ƒ(FÕ¼º €R  Äë¼z°ÛùnN*¸¤^í'·MÎËÎ<-ëñ¼S­ €òþûïw:áþýûÓ§Ÿ~šÜ^„ä%×Àïÿ{<ÅU«Š«axnlkk£›nº ÄôÔvxi€ó2çgYrþS@á“â7$ʉöêÕ«ŒoKŒ^Åh¡0 ð]ÿú׿`Ô@ t2 l€"ӿ͘ó´älÎßñ|Ê(ü‚8>ÙwÞï^‰§§Ù Óû³Ï>Kü Ñ€"¶Ðõò‘G¡»îº«ë‚( øh€ß‘Æùšó6çïx>e~ùÐf›mfÝ‹ôDyHé•W^¡¿ýíoÖq÷Þ{/ýö·¿¥… ÒÕW_M—_~9Mš4Éz ÌøCëÁCßùÎwèÔSO¥“N:‰Ž=öXêÛ·/rÈ!Ö‹²¾üå/Ó^{íE»îº+í¸ãŽ´í¶ÛZï¦à§)²l=zô îÝ»çLâÉB¼mÓM7µÊpY~ŸåN;íD»í¶õìÙ“öÙg:ðÀ‰_~wÔQGÑ×¾ö58p ~úétæ™gÒ°aÃè‚ . Ÿÿüçt饗ҬY³èºë®£[o½•V¬XAüL¦¦&zá…èÍ7߬èË_¯½ö]ýõôÞ{ï5•Ô–“_0Å'èJ‡œÔšHΉñ/_~†æä¨%û9%«Š@+VÞ‹ãåÎXûÛŒ99ó[m×­[g9ÐòåËiþüùtÙe—ÑèÑ£­$~üñÇSïÞ½-€`X+uÉ/ØûÒ—¾DGß×7ôIDATq|òÉäL˜0®¼òJúõ¯m=’g]·¶¶2¶$â;zâ¹æi¶VÄö(Å–Ùý\ éø–bþA×ÞÞ^ŠêQ?Û9ž#Wb…ß²œrûí·[?®£6‘›cZæ6cþÕËOýÍo~cÁßGÍàÁ# ݺu¾uIäp[n¾Õ¶´ÓûÐÞ½Ž ûö§¯œø-êwÚ÷©ÿwGÓ©#.¦oÿx: ùY }ÿ’tÞ´[édzï¤qsÿ@?_ð'ºè¦Giò’&úå²géòå/Ь•ÿ¦Ù÷¾J5õëi΃hîêVª}´æ?þ1-øó'tÓŸÒMT÷ÔçT÷—/h‘z& ÿñ:o»±é3« —åc®{ä}šÛð]ó§ÿÒU÷ý‡®¼gýê÷ÿ¢éwþ¦.m¦Kný3]¸p5ýìúzús~O£f-£So¢s.ºŽªÇ]AƒÎ¿”¾ñýŸÓñß9ŸŽüÆYtð1§Ð¾‡C{ì{0m»ÓîԽǦ¡m«­¶¢ƒ:ˆN;í4 øjjj¬aÞ¿ÿýï‰á'ÏVú(Šø¥p@Ñ:Œ:Ä–¯~ž¿Å1—GOÒöñÊ)<ŽœONá+Qçí§%¾q÷ÝwÓ”)S¬Kl0ÒˆÛ²Û&Ýi‡Ýö¦ý?–Ž8„œ=޾=úr6¹ŽÆ\}Mºåqš±b•ô*uYÛøU Y ]çϼΞp-}sä/èkßþ!zÜ7i¯§­¶ÝÑWï›(½óÈÔÙgŸm¾ðå¤$ŒNð ÷»ßY—¾Òtƒžø€âg3è9%Z )&/ÆSœ±N­~OFåÉ¥r/7ÛbkêÝOMýád}Õršy÷K9—GŠQŽ-á.x¢]]vú«uìëçü”ö9ôèN—öÞ{oš>}º‘Kã¸-Y²„ø±ø•ö PÜlŸßJÌíù¡lIú §”.¾—#WF‘Sœ±N­žÈºÕV[gÁd}!áË1¸ “\#»þ±iüüû‰GZvÞsßlÿn»ívÖ»;L vü|”;î¸#±ói Õ§öÓÂt¥ÃÖa¡½€ãJ©ä”äæ ?*6§h? 8eÞ¼yÙÄõÕS¾GS~ó\¢ÉLž2—y¹ôIDATõ먤í[øôFºhÑ#ê’з³ýÍAŸòkÀé´•…ž¿Öaùû³”üùϦeË–•²ÊÈëBNI'œäç¼BrŠöÓ€€"—tøòM¾øžNC5ë·¤ð]W&~øöôG}ÔDÑ"” £'… §_„]sÕü çŸ~šxG’>È)éÌ~<.§Ø±Nùl×—x¤Ì¸kWPR8râfTcjVdGQL |è?ÿù‰âE$€¢M‹Ÿwòâ‹/Ò‡~¨7&d 9¥ò%\NÉŠíðþV-ÆÄw|ð³GøYnI Û’ot3Vü“Ž=ã\'ÝŒ¶XЧ(ü„Ôã¾užº5Õ62~Òi¯£ÐwÆÌPOL}X=]õ#‹á—ø º|¹ŽŸÂû¥ÞGf¡„ûs³-¶¢“Îk=N‚ˆéAêÄ ’þ€B´aÃë]dI¼´#>*±9%€RúœR U±0C/®U?±Óÿ6éÞÃz$ýñƒ¤Ê\oÝríCoZÊ-üx~~´?ÃHõÿ›e=Jç½öËŽŒHè±ÙtØñƒè¼Ë~3"&û%¨˜¼|ùå—+àÖcJ¥ ?)–ïØá÷Î$ù#±9%Y€_N)PĨxÉï¡á»{øqõü®·§•²1n³Ã.tÀ'Xïœ<öWô£_-¥K~ý]}ÿÄ·"9ëÄzp£åwͼëEëY&üª€ÓλÄ‘ž~…<$8—|ù†ßiÄÖãg xzÉ1I„îà£O¦ϼÀzCòWüŽ.½ýK>¯þ²]ôîmæía@á— ¦á…iNíê¾  (Z‡NÍ&ceÿÜsÏ¥æ2¦ôE8äU9%9El#V@ñ22ÞÎ Ÿ“ûE7=jÝtÖϯQ8/²nw=ìk§Ó—>Êzû±×% 9¡ô,»Ñ–Ûì@»}ù Âú~ý»ÔÈ lø2θ¹PïÔi¶à*ÊËc¢Ïd„p-%ÿÒ¼ýöÛ}—ƒ>{M÷¥Ò…GLþþ÷¿§êTbÏ~y¡˜}È)öóÍDÏü؈¤ä‘Ù@ cˆ<ÊÁä<ýο[—’8Yó|NÜ2:Áï•áËK<²Ò÷ëgÒ¡Ç}“zy’õr¼½8œvß§7ñdÑvÛ›¶Ýq7kÔbó-·±ÞÀÌ#4|ɉ'û:G2x·ñ>%žÇÁ—®¶Ún'Úv§ÝiÇÝ¿ddì±ïÁÖ\œý?Îz"_Ž9ògY°Å‘;Ês ýxöôóX£; i<"ÃÃqatUYm(ùéßãÖ€î J¥ÊÊ•+­·ÇÇmsQ¶'öUì S/rJñ—‚ê[úÝ϶t™€º—‚ rñux”º–~÷3&S÷ñ¯N~õ|KK‹©"†’K÷¥Ò%”¡$¤°Øs”ñ u›—‡¤ßýÌT—I Ô5®£É77ÑìÆ—†š½´ÆÌZL#g-§É+[;•áý£¦óþzšñ`nu«×ÒÄù 4­¾-{œ{vç[ÇÕ5Ð$5™ÕþSëK×Ñ «×¨úšhÎSD^õt>¶‘&ÖÙǘà\ÚPüÌÉÜ}üð¶ßüæ7ôÚk¯™+d@Ét_P*PZ[[-û}ÿý÷ZIrЉ=—;ÎyÅf[.ä”R÷ô»Ÿ¥ê2 ”Ú¥µÖÝ@C–äÃG;Ö§ÓBýgµd`£ÆØiÿ¨F&Í?`-È<³Ä»=M¦RF”j/kéê¥5V{“‰¤L¾Ü²=÷Ø*š¤Ž)µaRŸÈågLØt_P*Pø)±wÝu­]»6‹¹±çBâR)‘œ›=ƒœRJ=K]Òï~æ¦Ë$ PîXh%ý¡Ks¥nåò |L i*¹×ÝSO½Õ?UUÕ° !¡ôIDATÄ€PwOf¯™4[hÌž;Å.~ƒ O´Ð±Vy>¦¯·GWj=ÚeóRÊ ž¿–j[‰b7OðØÛÒTzd;»@;oµýW—$g;åX׆âgNæï{þùçiÉ’%)¸ýpR(œðqI²g¾D™†Û‰½¢ƒôE9âš³M‰ÁÈ)ñü(–~÷² ½ÝŽuª¼=ÛWïè¼&eœ[Žu/cšvé 8F®hÏŽ<ÔÌ·!„ oÚôüý­4îÒZsó«|MÝLëøÞª­eß‹›­í^í9Ï=[fÉuL‡zƈn‚ʺuT÷D›þÓçàl§ëÒï-"y[þö·¿Ñ“O>™<Ás$ T 0P×ÕÕ¥ú%˜[ÊלmfãwÞ^ä”h€Eú='¬¹~ÉŠíð®%²¥RgÇ–cÝÓ˜,h–ˆ\β3¬ýÕ4í 7¥·ÑÈ iS¨æ™VÚ‹×'Ð,5Òâ¬CêÍ_JÑ/‡(c··ç±UU54#(YÛ+åÊ|ðç£PŠ” ±®”öVH]ü(û5kÖÐ'Ÿ|RÈá‰9Fb^~<û»ÄïüûG-rJ©ûCú½kCM0  ¿#w”aêÅ<ò¡.Í<¨D®-UóU¦]š¿¿&ªÉ²ãÕdÖºúUÔ33ŠÔ{€ž§2äæ Y@ÉoÏÙibàƒç®¡y®·n®ÍÂM. ä×#ÇqŒ¾Ô©cõ—s=¸1umn&”à‡]Ýpà ôÏþÓq €’f@á7óHßÿûßl#Y‡Hl)g|ã¶%çÇfä”hòô{×Öš`@éé*u7ß5Ó@—¬¥îYe]š©ê7Æ-i¡©u‹©¯£i†5©«¯·!dÀLš¨ž$;þÒÑVùž7‘âйêî¾g~æØAËèÚÌÜ‘üöœsD²¾TO¸Õ†Ÿ (ùõ\'õ_¬Îg‰}ÐĺFšµ:÷£r9qpcêÚÜL)ÁwöðûzV’÷ ¤PxàúÃþ<³,@b‰-åŠmÒ®ÄïüØŒœ@ =R Æ$Æm-{ÕZ—Dæ­XEÇZ—gì95U F¦:n%¶ögFJø¸¾c—Ó¼§ÖÒɼmÐâì;l¸“¬;~ÒOnºÑÇqU™öò |øn€R3I6_î«2€’³]µ•?Ü(m޹ ˆ?FòÞ{ïY£(|§D²>”4J²l±8i%¶ÄÓòÛCN‰Dòõ,ߥ߻¶žŽ ÈIú-øN4Uw×ðþ'Ì¡ð;ö7¦®ÍÍ´?ü°õÚzVOÂG÷ ¥8H1¯·ÛÚÚ¬—ò{¤* > ôIDATå#ölBœëJä”ÒAŒô»Ÿë2êÎ;ÛÙýŠSv¡«ŽÄþÒu¤ ºÔ†âoIÝûÎ;ïPR‚¥û€’&@á[‰ï¿ÿ~zñÅ“êFÉ-ölBœƒ ñå-éw?£ÑeöR|†ÄºÖ†âgNÉÝ×ÑÑAôŸÿüÇø“Ð}@)P´Íén¾k'moÞ¢]é Äôxcz¹õ-ýîg#º %ô<˜rwpœíkCñ3§dïãlj/X°€Þ|óM£OD÷%-€òÒK/ÑüùóSK±›c‰=ÇÏÐVùaHúÝÍ&d›.S€²àÁ54¾fT·Sf³cßA³Ô]3³ïÏÉÜAsssæ$Þï`È}Žº‹H½hÆ=ö“ms÷É]9æ¼['¬ƒjCÓÁ²\Ð}@I ðèÝ+¯¼R‘pÂ>$ö6&™P9¥pБ~÷‹£ºLÊ¥NÝ~,Ï8‘“îy~=Õ©² qÞ™“]ï£öæÿy¾ŠÔ'ËÁó×eß¹#ÛdiÊ»uÂ:·ÈïgLiÙ÷ÜsÏY·xš:iV÷%é€Â6öÌ3Ϥâ%–…ú¿ØsؘTîòÈ)…à ÷ô»ŸÝè2)”yKì— ÚÀß@ƒ­Ûí§Öª÷ÞÔª7[ÛúÕ¨g´©wá´QWïõ‘ÛÒñûsÔãégÕÙmôVÇ—}¦¾['¬skCñ3§ôìãçP444yBº/(I”G}”V¬Xa¤Å%”ØsؘTîòÈ)”’Í ™“yÇΠéõ4«~ÍZÚ@ãëšiNöi­™ÇÜ÷[HµŠîØø»zƒ@ÈPy0Ûê&ë¡p=Ç7eÅÔwë„un "q-´ã­Ý”¤Šw/Wαç°1©Üå‘S(%”EÏl 1ãí§ÆÚчÍjR—xDÉ.€ÒÅ{}PÄÁ¬e¯4n…z¾Šëƒ×Ìy·NXç–s¬œ°IÄϤ¸é¦›èõ×_7ð´'… Wn{æ§ß|óÍ´~ýzm+^‘¤/ÂÆ¤ò—GN)¦¤ß»¶6;Ö©òö“Wý2ÅVŽcç¬h¢ñ7·¨Ñ‘šsÏ3¶Úº6j¥¼Ë§3 È£ï½Þë#2hn3MÊ<2¿¯zd>ŸŸì3õÝ:aû@úÝÏ6Ò¸oÆ ´jÕ*â„bÖ€’d@áK;ü"@|ô\„°1©Üå‘SäÇ}aËà9%(¶Ãû»ŒTZnãÛþŒé6œ<½f­l¡‘çÛ/_/O’m£áü&cÇ%že¯Ç{}BäåRãÏïc•Ä“d ·NXýI¿û[G:÷~öÙg5êP ˜ylyì•ßýÄ£sø³7U(ôIDATØØ6&•»"§Ï#Î>–~÷² ½½‚Å© ¬7¬àƤÍ*mküÆãµk×Ò|`À©¥â>§x»ñé§Ÿ&~b,>¹Ø‚x<§AWÒï¹Öàö €RÔPUŒ¥«snLn–žmü ˜‡èùå‚åú辈;¡§­½øzð/ù ÝqÇdêÃÿâÓDç–Äž»ŠAØŸ.€‘~ïlz‹.£î¼Kó$YwqÆ­ EO¥®}øá‡Öí¡åJ6º/Ò qŸO¥Z°Yç-öŒ]\ŒNšþ¤ßý¬Q—Iù“d“Öy¦É« ÅÏœ*gÕ?ûì³e9aÝq'ôô´§um~òÉ'tË-·ÐË/¿mC ®]ú´˜y¢&éw?ÓÕe(¸Ìã3yVŠŸ9UÎ>=á!ûGy$ö“Ö}‘`ˆû6d­Ãh»ç¼ð Ñ6’ðÚ¥/Ñiú•~÷3_]&  ì¶ÛnÖí³ã®]…„î“ÐM3†bäS³ÂêóÝwßÝÏ–*rß™÷¥§ÓÆØÓÒžÖatfË—ß~ûíèHIÍÈ)•&œ‹‚æí§åÚk¯µ’U·n›Pÿ+ïYPI)¨ÌXñO:öŒsUw³ú¼¶¶6%!±t§ñî»ïZ#)qNšu:mZ€!îóÐ:,-8kzóÍ7‰cå{ï½çÜŒu §T „Í)ÚO Û×5×\C›nº™ *›t§¯œøm5ë·tÝ#ïV+Wßÿ& ¿tôÕ¯gÁd‹-¶¤ùó组lb ðü‚%K–P{{{, q:m܉=-íi–¾ËxDõÓO?-}å)­9%½RLNÑ~PØGþõ¯Ñyç—®¨{M©×Qè;cfÐ… ¦ù`1Xæ<¸ørÝ©#.¦/õ>2 %ÜŸ[n¹;–øé—ø˜£§Ó¦â>­ÃÒ÷ëSO=EÏ=÷\é+NyÈ)途Ræí§!E|…_¦ÆCÿ'žx"m¢FS¤B^nÒ½íÝë:~ðhèÅ×ÓE‹¡kz«çÿáôIDATÐRh©ûËêDÿ¶`¤úÿÍ¢#¿qí¼×~9ýÅ}¶ùæ[РAƒè׿þ5½ÿþûÒÍXÐÀþðZ½zu€’¥(‚ ²Å@Ä©Rô„³~Úð²eˈç&áS˜S’*ñå;Ö)ŸíúmÆ~&ÇÚ˗/§qãÆÑGÑ X¤þmvØ…8â:þ;çÓ౿¢ýj)]òë'èêûß …OoÀ0 þü ͼëE?ÿ~6¹ŽN;ï Dzøê±Ù`„ûƒ/ßôïߟ&OžL÷ß?Þâgà]ìëèè {ï½×zÚlEK°€b" ” cQ…CÈ)å…3rJPl‡wXG‘«<‹ý±Ç£ë¯¿žFEÇ{,m»í¶®‰Rà¥{Íhç=÷µæ«§œC'›HCÆ_M?¼| ýôº?Ò”Ûž¦Ù÷¾ª.}\ Ô:·áâÉEßÜhÍ~6ùFúö§Ó‰gþ˜?á Úû ¾´Í»úê•õÛ³gO:õÔSé /´æLð04?¾ŸÒi€çð\”èAPŠ”RÇ:~ã5?ïdÍš5¥3&ÔÔIÈ)ÅK¸œbß !ù9ON‰P:YWfÃúõë­¡ðE‹Y¿Ü‡JÇs íºk×IÖ© ›nNÛí¼í¹ß¡ÐðdÝcNF'5–N;w’™Iß»pûË›Õ$Þe4öš»ég××[—š¦üæ/4íŽ+ñ_±êeºê¯Ó5¼EsW·ª— ~`Óã O~J76uwªÜªËë¼÷-x¢ÝškSûh›%.Ë—®vÚi'⇙}éK_²F2>ø`k.ÎqÇG¤3Î8ƒÎ:ë, ¶2&MšD3gδ^>ÇÃÃwÞy'=ðÀÄ/cHãë¯Ñ_*£q” £†Æè.¡PL”ÿýïÖóNx‰Oò5€œbb&PLT%d‚D '|7TSS“l*ÉR~ Ÿ +rŠïžÁóOð ÒkÀë½Í¸ô" Fh ½`HY·nñ/³R}œN H)²Šëé|òÉ'‹«GCЀ§œ±N­w›±g+Ø T°øy2þóŸKö¤Yí§Å$çÊ>Vë°0Ãä·Yß~ûíÄ·–ã @Ñh@ûij‹F,Ô ¤KûÛ߬ÛPKq9Àé´A) ´´Óeg8h MÐ~ @IS¿â\ ÔÿÚ ¥1øÂ OZ_¼x1=ûì³áDih­í§”ÐÊÃÐ@ ðm¨úÓŸˆ‡^ÌÇé´¥NÜ•RŸÖa¸žø÷¿ÿ‡±…SJCk@û)¥`%â@h ¨x²ì­·ÞZÔãðN[)@QêóÔ: Úsd=xoÿÇ­ÿÁu†’Ð@1Ð~ @)F8ˆMN§-uâ®”ú´ƒuOtž7o½ñÆÁ@)h(ZÚO(E+@A5À¯–ÿÍo~CŸ|òIÐCòÊ™1—#©@£_žZ=¾òü!Œœx(›¡H5`Ç:峸Í8R=£rhÀ¡~EÏI)ìé”bà(L¬{úé§©¡¡ÁÑsX… ø4Ûáãk-A•®þU^Ø/sJ1€4Ö½úê«Öë3 éªt ÇùCÅj€R¬q<4P8ñÝtÓMÄï“ ÷ Ä(áú¥¡h ô ”^§¨¨>úˆ‰'bÿP¢~R,eÜ"QˆF”hôŠZ¡€àÛ_ýõo? D (ÿùÏèÅ_ Ø{( @Ñi€nQ34PO<ñ„ugOWsRôO@JqâÞ1mmmÖ%7¼gÇ]?Ø Ä¥g¬ÃÛŒãÒ:Ú<4°~ýzzë­·<öÚ›N[\‚®tÀé¬f¾ÜV[[K<9h(¯œ±N­ã6ãòvZ¯t ðè ¿çåÍ7ßôT…öÓJŒÂÏ_ëÐSÍØ @eÖ€öS<¨­Ì]æ¡[ï½÷]ýõž¿âN‹” EëP[ƒá=÷Ü£7` €ʪí§”²v‡œøüóω/7¸=ÄÍé´”Ò áÈØôIDAT _V[¶lñde| hÀ 8cZÇ%3ºR@D/½ôÝÿýîìÑ~ZXrÔX¿Æˆõ(7”}XBÐ@y4àŒuj€Ržn@«Ð€»øQø>úhÎNí§”BaKëèŽ;î Õ«Wçè_ h üÐ~ŠK<åï H ЀÓi MЕ~œèðí·ß¦µk×Ð:Š@Ð@Ü?åx¥Ö1‚w =h + ð\~óqî­¯=)²$Öñ ?ýôÓ®ºû¡h l°c¥l€†¡ ð/}žÄÉwøØJ)å_ÿú—¿â±€ʬ  Ø_fYÐ<4 Ð¥@A¬ `b( ¡ ÈЧ×ÒK^¯ò²ÇÙ—ÏD²„^ § ˆ]x-hÅŠêJ¡:xþù–ÃW¬X~–—Že»³_œë²ßké,ë\÷*/Ûeë²ßké,ë\÷*/Ûeë²ßké,ë\÷*/Ûeë²ßké,ë\÷*/Ûeë²ßké,ë\÷*/Ûeë²ßké,ë\÷*/Ûeë²ßké,ë\÷*/Ûeë²ßké,ë\÷*/ÛuÙP€â¥R¯¥W0ñ*/ÛqœWG?ˆ8—i·—¿èöôÓé÷ÒK/öò:Wlw÷ ÖKkë;;._þgðAÛ6N»q[÷Ò£[Yç6‡˜å´§m¸­;Ë:×ÝÊ:·9Ë:×eÜÖeëneÛœeëÎ2nëβÎu·²Îmβ¼žy·sç€ïÐl ~øâ‹Ï7yë­7wÿøã¶„þÃ鿽ýã->ÿ¼£;ôNoÐôUn `ؼ[¹í „o¿½a—yóæNøïßÚ : ¦³ßÿþî3ï»ïƒ ¯`ú‚ž '“l€@ $ÈÞÿ½í^|ñŸaD ëDºZ·î•}ø™IA²tÝwÐtÄ6@IPr‚ÓÂiÙ^ýµ½_xá‡À¼íáÃ?Øú‰'?—ļuûnL· ¿.h<éóî?ÍôSù::>ëqË-7úÇ?þ~h9ÚG›Hü°ÒØ%É Æ_ãOºÛÚÞßöÓO?Ù,éçùaϰØ€› P(AI¨ ðeŒ?þñÞ3Þyç;¹9w%nûç?×ô¾ýöÛF`Ž^%ÚÚ΀’Ðä”6CÄù–P^yåå}-Z8ša¥ÒuÈÏ9Yµjå`ž[éºÀùæOЛYz P0‚¹ èç#š±fV –ºÖ\[[[Œ/L¢!s0[ƒžLÑ%òäÀMXßôIDATc7ÅØË'G×ɵØ=ô½þúë«I¢MúŸÚ}÷ÝGwß}·¡’îM¢!sùbt_ˆî(Œ Dn%ÍŒ®•}þùçôûßÿžžþy×ý¹“,sÏÀùí£>¢õë×ÓÆ›#^O¢!s!IÇ”Ïn(‘'§òu.ËÝGœ+CWoŠ^ÂÈá~’ü1=üðÃÄ—wâý„‘e‹`…Ø€‚”ÈmÀ+uvÐÚ¦Fjhl¤FÇ_Cc3µfY×Ü@Ë/¦ÅËêi͆kkGëZâ7ó1Ö²©…6´Ûüç?ÿ¡o¼‘Þ{ï=¯FÕö$ËΧóÅ_Ðí·/¡U¿¹¥¤:ìÜ’Û–$ê2'Óö+·ß(‰ Ö•k°É 0n Ž·µRM•Û[`ûPS[;ÕÏHÎ7}òú²5mÔÖ\Ûi{UÕZkó ½úê«ôÈ#_öqÿ$Ñ~ÜÏ$*zµ¦·'Q‡9™ñ£rû €@ÁJä6 ÓZþZ[k+µ©‘™}¨ ¬¡µ­mÔªþ:Ö-·!¤ÏLZ§Àcݪ)ö÷) ÔÖ²ÐZŸR¿–Ú[×P톜>´j]fE5—<¼çd$1àåjîå—_¦[n¹Åºk'*涘ÿ-‰:„Ì”dÙ%òä”,ƒ€GÑ_ùÉ-ÿ{-®f@YH2“¢eñ B¯èh¥U kiyãš, ,ÌìÛÐ0Ó*[Ó$†ˆøòÇm·ÝF=öX~cê{çuú4ÚÛÛé wß}WoTš+µ•»¬F}¾¨?™vŠ~+e¿P¬á¥t‚èërÉo9›:'×5 TS‹ð‰£¼Œ T ¬¦Õrh4µÝdÊvttÐ?þñ—ù(I´­€wÞy‡øÎÜO4:ÌmÃù-‰:„ÌÑû:t\JP(¸Ä¹ 8›ÛzçäÚ²°ÚºlS¿AÊ·Sƒš,[ß¼.;‚R=s!-_¾œVÕ7Ñú<8‘£ø.—––âQýIbµ¥çIÁ¿ûÝïô©d×¢Óa¶‰œ•$ê2—2y¢®èí €yrоá(¦ë8'³¹|i£…y—x:Ö×Sž@[=“`Ô/m]ÆéSÛ””…nÃ+.µó( ßÙóÉ'Ÿdöš®/7ùÈšoòöÛo{Ì­‰V‡Õê&#¶!ÁJi FP"·Îé-w‹úõÏ]sPxëšU4Âq—OõÌåÖíÇr‰gaþ5ÜJs¾}øá‡ŽK=É ¢ W÷ß?ñå÷Oô:Ìm7y:,eâ@]èÿ8l€yr‚!ÇaÈf·‘›ÚÂ}ë°îÈikÏÜCîàœÒO>ù¤5'Ål§a?ôIDAT]¹û _Êzê©§rÎ'ø—ÒéP·é.gu ™Ñ—¦Ú€‚”Èm@§µr®}úé§Ö­¹/¼ðCL HÞr•Ssnm#©y÷tÝ”Æ(‘'§Òt >ÉztKpåÜ–]¾öÚ«_ºñÆþOö5ë“"v ¯’j FP"·³R뫯®ûò²eK‡µ·¼…Éë³Ï>ÝôÑG9éwþ·“Ydiô Ø@Ô6@A  Dn¦¥×Ýš›Ÿ9jåÊßWñÅç›Dd ­ÿ­·ÞÜýÝw[w°7O‡…žŽCb‡ ³JäÉ)XGÀ`Ó¬'$×°öýÄÿ›ß,9oãÆ/2 †Õ!ʧ9¦TƹP(A©Pèèø¬Ç’%‹GþíoÏaR2ûüóŽîï¿ÿÞvN*#›Ô6g‚ P*49™`|¡üAð£>Üê¾ûþ8èÿ{{gúãÓO?ÙìÞ{ïùÖo¬ßÓy Cùm}P¹}@ `¥Âm€GRZ[ßÙÑ„ù(÷Üó‡o?þøc' )UnRBߣïÅ(žœÄ°¬ì ð—¿ù¤}s9¯eRÏOäö:/Ù.åò—²ßk™_^¾{•—íR.ÙÕþüòøž¬dŸÄþ PÊ(îO•éµt?ŠràÇíXç6îšqÓ¡s›ûQáú¡££ƒV®\I¯¼ò úÏC¡N»­{V´>“˜È s:a €@‰ P¼*¶'G7n¤gŸ}¶ ù8>ž?=ô=òÈ#Õƒƒ¢Ñ€@’}:“}û€@ DïSQë§Ÿ~JwÞy§5ÒññÇÓ9çœC‡zhAçÆÇññ\>æi€01 b(Šy¹Â8‰Ö¯_OýúõË^>øÇ?þJF./ pß}÷¥^x!Ôñ(½¤LKR§rÁ €@ DûÝÂÓO?M{íµW08‘MŸ>=Ô9qyI€¼äú¸^|ÌÑ€ô€ rÀ´¾ P"ÛèÍ °2““ôIDATÄ$¸øòΖ[n™œÈÂ^æáò’eÉõrýø˜¡éÓ’ä©\` P(fäã¤X³f í¾ûîÀBYÐË<ÎË;r¬,¹~nŸòk@ú@P¹@`ZßP(”òçc%hoo§[n¹…;ì°N ô2OþåN„\×Ëõãc’œMKÒ•,€@qÉòkK÷g¨@/ÁôâbZ†o T2˜vî Å%e KÀГ¿ž\LËðMÓ’t%Ë@ P\R†$Þgá:kb?.¦eø&J%iç@ D(I Ö"sØÄ„ò€¶±ÃiÄE<ŠiIº’å P(.aZ €ÀQˆ ˆý¸˜–á›(• ¦;€@qI’` IN8P#öãbZÆnÒ2RLKÔ•*€@qI¬Kêí½“©nR#Ýìø«›ÔL«3ó\î™Ú@³F.¦é£êiÅœkîËSóÖRÝÄëk9¹…\à nuÈ9Xuo ;jÚ:Í«q;NʯœgËòL]+-™XOKfµ’S.9Ÿº‰Mô`]gÙž˜³†æYFÓչʹ†žÈœ¯_¹û”ÞšhÅìVz¦nƒ%ÃÍÓÖ;Ρ•–*¹x[îq¶®E®Ü}Ž:#˜g$öãbZÆnÒ2P*L;o €â’2$XKr/~ÙJãÕó?¤^½ìCKjÛiþàöÍšÑFSk;m¯ªš@+;€w"û̓ûØuõ]–…„gy'mŸ¬À€“xmõSçÐkH“‡\Utsm. <5{õÊ;ï^ƒêé)UŸÔ¯uaë‡ëðÚ7v|sFèYˬó0¼Åó8ÿ:×9`'W~Ñ]Ø¥œ“‹i»IË @1-QWª< Å%eH°›˜üÊ7Îk¥F5"2¶§JĽkhå¼6Z­þžš½Ü‡ž3é÷Œ›bÔ@ÓZëç_KOÌ[CËI¼ÍÝž“Týê°dZÐBƒ² Ð‡æ×dFh´}ñÔ̈˂f êè7´9+×X–«¶U‡ýÇàáÔÁêÉ6`MŸÁòn°Ï½j ‚2!™ss«CöYç½ VN´ëá¶ï3ÁÒÉtpÜÖÃGX:©›SxN™K±.öãbZÆnÒ2P*L;o €â’2$X—"YåÖÑFÓû2 ,¤ÆL2¿c$'Xõ>+‰s‚o¥¹Ãk©F]‘D}if߃gZe³£êàöë3Çõë[m?@%zÞ¤íñ×ÑS Ú©±¦Ñ>Vä²÷©‘žZþË…iEýôIDAT&®_ä=d=­¬Ù@+Õe¬ù›­KA~uȾ,Ík²ÚæÑ›gæ5Z#9ýx]éj<_fTHŽs“Kö¹Ö™Ñcn_åÂV˜}b?.¦eì&-3Å´D]©òP(—”!Á:LR V¶3 ¬°¥šîp™["Iµªw5 ê+—FÓjÂÙž_Ï,Ê´Y5…êUB¿˜zæ2‘ßqÙ¶³#/|\9Eôd/k²ói´l¨fÈhë8»L:T“}‰'3‚âV‡kÛ=GÐ\kÔ¤j¬‘$5 5»Þº„tþx{NŠëqU¶\®û²uæêSË_Øv9'Ó2v“–€R©@`ÚyP(‘ŠmôÆÆeWÁ$X›¨:ßPîΣ겋ºDa—o§:5¡tþÔuÙ‘ŠƒR͘å4w|ÕçÁ ãWÇS5zH¿,äTÑøI|“„>V qÖëWÇÒ¡6]]sÍYÖ¯ó6Þ‡Qˆ`£IÖ¥sò˜¯ €’R@aø¸ûî±txïi×í{Ј·  Oënýñ:o;ì ¬2•tC Åüd `êÜG %…€òÁµ4è”^ÔgßÍè‘‹ªè‹:åê‹rÿxïã2\ö³ÏnÄhJ mG~(“€À|P(‘м¯}ó@ÓýK6ŽË ¬ÏïôíNæBI>¤ðw.³×ݬclâmÄkçÚ¿¼ýP—1?q.*£(JŠ~5ó¥šÓO>z*àpƒ¿m 4<’‚Ë=ñÂC°¦á€¸IÜP(”Ê]w¡#Ô%› #'ù°ÂÇðåž·GÒDñP@±Ñ%9É  ‘þ¾ P()ùàI¯<¯$>‚~çc¹Œ¢Äq€%ýÉ<À@ PR(|Û0ß™ã6!6( ð±\×GâDñ€€’D€ P()”Ûo?Ÿ†Ÿ°EÁ£'1| 2?'ð<Ä¡g €‚dS²O–³ ŽºLz’B‰ÇÙÆœ9gÓÄS»«M…_âácùY)ü@<ínŸôIDAT7gÝXO¶]jÿ²gšxÿŸ¬Ø’Ĥ ™ƒÛFPU‘CUà¨Ë$;”3‘P`;^ö§ýËMì=Á“-tµ P(\âQyMºàOú@' è2H¼Q'^ÔÌÆ(JJ“dÓ^#"a·køðAÑe‚%$Yè)j P()¾5˜ß»SìmÆ\n3Nìhø DTQéÀ €@ ¤PøW5Ô–.°;RâU€Rº¤ ‰O— %E€Â#ü¸z~l½s^Iu~uŸNÀ Ä—T0¥Ó5€@I ð/h~á'$~OGÞs>o4  Á–.ÁB—Åé€@‰Pl'õ¾öÍ{ô/¼t&¯¡÷¨¶3hðKù½<<'Åíé²¼÷q9Á›ŒÓk{Ú¿¼ýP—).© )C¥² €’²¾ÜÃsRxÒ+?¾žŸ2Ërã?^çmüÞ.ƒI±é…¶ ”R%OÔ=ˆP(””ŠTøäË.;ƒ¾úÕ/ÓÎ;oMÇ¿?Ýyç&)ï{± €b£KôI‰:j %ÅIªµu.]wÝ9tè¡{RïÞ»Óܹ߳~MÏ›÷=:ä=èàƒ÷ ^ç¹*?¥{¡’Ï€( &• P()LÌ=6‰Î=÷XÚ~û-iøðcèÑG/Ê'+IÖ\nÄ]®±ñâì>)ƒeòÁ €@1 <‚Ê@ PR(ÎÑáÑ·‘' |ȱ2ªâu¬”Ç2YÐ@ …“ÊP(‘Jà¨Ë$+ð›¨ýFKÜäsg¹°õ9ź™ö«ýK ù~ÈMJЕ. €@IàJ1#]Š@†´ÁóWüFd¤<–f ÷ à•DØ P( ”üÑBæŒ'pH»Ûm· –;§ÅYëfBJ@ÑeÌ“˜ÌÓ(3€@1Pd$£TóC wßGµµC³w]{íÙ®ó\¤<–f‹†ïë;º %É>‰ç@ P µˆâ›bÅ ?>‰Î;ï8¨Šâì›üu ”$&êJ•€@ (ù£%Q<£¤T€"IÐmTåÿ»VeBów¥È@Á¨P!€@ H£-qKÀ¥g2ªÂÏ_á¹*<¢Ÿ¿â,‡õxá €@  ÙGžìidêÁ`vR 2¼oà.g¢äÑ’r<Ñ5J@}ʨÊa‡íI´ñ\Œª”϶:6NôIDAT(”DæŽ$ ™“èlÞpÂ{t-_—äõÒ9ZÂsL ¹§ã§|OÞ±¥\£%NPõ¸EÚåQ•ë¯J2ª2gFUD7Q/5|xû¡.ƒdŸÆdŸÄs P(þ¢-÷h‰[â+ 8eáQ•‘#¿fÝÄ£*?|¡Êœé=+×9jøð{à$‰‰<­2P(”'F~ÿM9æ–M~&ŠÈúÞ{×Y£*‡¾õêµaT%H ¼’1 ¥D€"£%;ì°¥õü’¸ç–HÒïji 8eýóŸõ¨Ê÷¿ß£*%²KÖ1€@A²)Ù'ÑÙü‡–uæ¤3³¾qãBzå•+è¶ÛΧk®9‹®¼òLëvÚØ•z÷ÞÝ9q{ƒp1m{l¾Ì¬k–ŸÏƒ÷[©—Q¾ûg÷Ý·£o}ë+tÅÕFË\j”º>í_þ~˜Ä$™“˜‚ÉŒ@UäP$8ê2f 'ò»ïK‡÷Þ™vݾ8q ºð´îtb¯n´ß®›Ðö[w§ÃÚÉ*cJÒ÷’ùkt³äçó0]æoöٌݫ›¥gÖ¹‰2—&¢¨Oû@ LЀ@éb(ýƒjiÐ)½¨Ï¾›Ñ#UÑuÊmåþñ6ÞÇe¸ìgŸÝXÖ‘ Èl&èFAê $')›¦È@ P|…Aƒƒûwúv§ s¡$Rø;—é¹C7놄 É£Ôe ‘y/È\–¾*uß{ÕPt$sSt¥Ë@ P<…/‘œ~òp¸Áˆß6I‰ûrORef]1$ùéÔm_¹ôì¦n×ðá}‰G— T:˜rþ ÅPîºk ¡.Ù9ÉOž| _îáy+q&-ÈŒK;nö¦á€bJò…]ƒ0€@q‰à ¤<¯$>‚~çc¹Ž¸FQ ³yw$¹ÁB9¶PºN†ót@ P\…oÁå;FÜ&Ä>–ëàºâHJ9=ÇÑ—¥n€b^òuÝ' ÅPn¿ý|~Â*O>‚ÂÇò­±üÌ‘R'·ú s ÀNr`€@ ¸ .—Äs¹$‰zŽu&öôIDAT(JÝF@±Ñ%9É  ‘þ¾ P(.€‚ §ñL8M¢žK qÔ@I2O#°P(@á[vù½;ÅÞfÌuÄy›1d6cSЦ %‰@ P\…ƒ?zO²O¢žÃÀ e( ’}LÉ>YÎ$8ê2ñ$Å IƒG>øìü8õ°wóðcÛËõ¨û$Ê̯œ =±“Êhÿòž$‹9(ÉŠ«Iް2cP9T ŽºŒ9€Â †_øÇ²ñ ƒ<òžËp¢åcÊõFãBdfƒÌfÙ^)Gû%l’Dùò€@ñ¸Ä# ‚Aƒáó{yxNŠÛÓeyïã2\¶\o2.TfuÌ• (bÊ—нÓ(J€ÂIŸ/÷ð\ ž„ʯç§ÌòƒÜø×y¿w‡ËÄ5)V`Äk ™Ó ^}îµ]Ç÷Š.ƒ$éL’X/Ÿ=P(”€"Ÿ“>ßËÏïà‡Œñ¯ó6SÀDd•e’e¾õÖóè›ß<”vÙe:ë¬#éÅg«gÑ·‰K Gù€#¬î(J@11ù¤U¦ûîûpÀ®4dÈ‘Öü˜ï~·¯õ½¾þg*Ëbt$Œ(ÉIÊa“xšËP($;£þºuWRuõtà»Òý÷·dãË ™á„¡åÌ3û— “¤+¹,€’D P(#ý'Ÿ, ™3ÓN;mEW\QMŸ~zCV.† Þ>kVµUîW¿L|\%ÃGs P(Hö‘'û$™-³Ê!>@1t$᤭ŒórΫ¯ÎV–’kN@‘}\—}rõ$ºÉ_jÿòvB]É<¹q6]}‡@ULPåyŽÁn~Æ÷dêÍírŽ[_ºŠ”ãË@¸ìãßÿÚ¿¼ýP—IW’l$·?(JÞ¯uI|Xú'½bô×eøòŒÛå·zý…Ëã²_iøð{Or“@$}}@ P(*7ù'¸Rî—Ë9|yÆírŽ[[]ŠƒË>îý@I_ò®  P(1&gI¤•¸ z9ÇM7AEŽÅeŸ\P P’4 €éJØË9ÎeX@ácqÙGC €@A²)Ù'ËÙ‚G]FUg‚Âz2õRÈå·¾.P¤\öY䘄Ž9(ILÔ•*3FPU‘C•†ïà¨Ë$3K2ÄÒî?¾œÃSs>l­Ý(Òn%_öÑþåíƒöždýø©ÔÄ])ç @ Pp‰Gå¦Ò€¡ór?LÍù°;ŽƒüôIDATµbÚ( pû•zÙ' è2€”JÓÏ€@ ”(9“€Óp¬<†>ÌÝ9AÏ»T€"í9/ûðe(ÙžÖ¥†ï]€bzâ®ù( ¥¨-—søaiòîœR'úRŠÈÇòòe(~÷Ÿ‡lOÛRÃ¥R’{΀@ P JÌQ]Îqƒƒ¨…ÛâË>üî~h\Zßí@Á¨P€@ PBJ”—sâi/Í—}( ’}äÉ>‘FVUeÝæè=¸ŒwñH’4}Çå7D9‚’ß^/ûP(‰ÌI2'ÑÙüð€’Ÿ$Mûçå·sP¸}¹ì³ãŽ[ÑÌ™ƒ‰ÏßM®¤l $1fBf\âÁ¨OL£>”¤$³|9㾜“ß>PD†×^›MC†Iûï¿ %ùn ’}ØP(ÌAq(×åç²\€"2<ðÀÏ}·O@±B ‘'1‘§Uf €@É”r_Î(p.Ë (,K’/ûP^I„ €@ÉŠ —sœ`"ë&ŠÈ’ÄË> É>¦dŸDg³½þ×´4\—d‚e0}št9Ç­ÏL‘/I—}´yy lOblÌI„ 2cP9T ŽºL°„*IËâôeâå·>5PXΤ\öÑþ% âµD²’8Q&; P(z‰‡/çðcÞ£xwŽd³ÍT@‘s2ý²%ž„ p)­ž(JÊeãÆ…ôÊ+WÐm·O×\s–õ\£Žú2}ùË;{ël¾Ìœ`Y~>Þ'``Ò’åúÍo~H»í¶-vØž–žM‘9 è2¥M2HÚÐg¡6@ PR (œ0ï¾{,Þ{gÚuû4âÄ-èÂÓºSÿ^Ýèˆ}zÐ.Ûu§ÃÚÉ*cJÒ÷’ùkt³äçó0]æ¿9p`7:Qé™un‚Ì>¼.í8–ˆ„ZhBÅq¥µ €’B@ùàƒZtJ/ê³ïfôÈEUôErõE¹¼÷q.ûÙg7–ud27ŸÈo4 €RÚÄ ‰GŸ %e€Â Á é;}»ÓÆ…¹P’)üËìµC7놿DÕ>ÈœpŸPâI¨—Òê€@ ¤PøÉé'H=p¸Áˆß6I‰ûr·Çí2$ùÉç¶2Ji'@$}P(”Ê]w¡#Ô%› #'ù ŸáË=(…&K?hP(””Êí·ŸOÃOØ¢àѾ5–Ÿß @æxôPlt‰? !ñCç^6@ PR(sæœMOí^4 ð³Rønq dŽGÏ@€˜¼€@ Pr €â?*‰P@ ˜ "^²P(”” .—Äs¹$‰z P¼ ÀäíJä€$8ê2þ¿^ã¸ìÔ60á4ž §IÔ³ö/ïI²˜ƒˆ1 V(JJFPø–]~ïN±·sqÞf ™£‡r àÃ4ø"€@I  ðÈz}²O¢žƒŠ.ƒd$y¢Lôv@ PR(<òÁçGÀËmÃA—ü¨ùr=êžÏ?2Gv甆ïK<ºLô‰É:b %E€Â¿îù…œlø}_îá9)< •_ÏO™å¹ñ¯ó6~ï—‰kR¬Àˆ×2G*AAò7 <‚Ê@ PR (œôùÖX~~?dŒÿx·™&"«,!siA€@ &• P"Û཯}ó@K˜%áa ½V² hÿòöC]Éܤ$]ɲP(”” TrbƹÛ`ªá€RÉ ?iç@ P(*ka„%Í:(6º`ô$iI<ÍòP(‰”uÍiÙâ‘´xÙxZ³¡.'±v´Î¢†ú‰Ô²¾6g;'·ã¤üÚÖL=ó¨©a<5­GÖ¾†‰ÔØ8)û×Ð0™6t ‘§9‘§õÜ(¯$‚ €’@Y@õ3{gçªHÀ]¶FÃHãÌžöþêQÔžð>®­y¨U¾¦iž 4m“i zHŸšÉ$û¤Y6¶PÒšÄÓ|^b¿Êлø ‘'1‘§Uf %€Ò±nŒ }Ó:5бnÕ ûû”‰*à*hhŸF#\ظ'Õ¯·GEüŽkkn•¯mÎ@NûTªVu ¬J²ofý,jo›G­­ö_G|*iNèi;7 À+‰@ D(A‚£.ãžø[kÁÄâ5 l ¡y´jáPZÞ8Ãú¾¾a°µ`u_kY­ ƒ“Œß±«)côIDATq!5 ³©£}µ­Ÿd ›»¯–ÚÚøOÚv—1mI 瓞~Öþ¥ÜÂ÷ƒDžÄDžV™(”DÊ PúRK»[Ò¨¥ÅÕ™Ë;«õ<•šÆ9äwœÊL5‚Òn Lì<‚Ò4G…í:êèà?iK““1dëlŸAE—¤$1™§Qf %€Ò±~¼ !Õƒ©¡eÕ/ìo]ÊéS;™škmx©]¥îºá;oêGZ Q5b}àsœŠß”ѵ㨩ɾ“§¡aeïøÁ(FRd>¼ÇPtJ“}Ï €@I ð¯âÖ5ãa«¨zæjí˜ExôdÄHÇ;‹¨Ñºã§75¨[‘]Sõ  ,l©µ“­š$Ëm«ê9(´eYÛ<‰9A‰£)öhŠØ¯7ž8Ÿæ @Ib2O£ÌJbÅN6uö„ÕöÜg tˆ =®ópy×máèÈ, º’0JÂŬÀDŒþH‚ P($ûÈ“}",35Øð2^dL–P(‰ÌI2'ÑÙüðÄyý;Y‰ý• $1fBf\âÁ¨OL£>”$$2ȘNà  Ù'ñ‡=€@Á])ŠÓ™˜q^v¿û'yyZe P(HΔ”Ûà•Dˆ P()ONEÀè€@A²)Ù'ÑÙ0 P(— P’3!3FPU‘CUà¨Ë ‰•+‰¡ÝôÚžö/ÿ Iü• ™Ó 2 —x0%å6@IoO3 P(””''ŒŒ¤wd$hß]É<ÍI?Iç@ P(AI¹ høð¾Ä£ËP’”ÄÓ,+€@Iyr ú+åÒ;Ңဒ愞¶s PŒ ¤XVt”Ú¼ñÄùº Œ ¤-Ñ'õ|(Jæe†¥N¨€aš P_I‚ Å@ñ œåØ'‰eÑ3U„?è ¬ ˆý”Ãv mSËŒ$ž¤$žfY(”ÈÅv BÃfyŽ“`61¡<`†m@ì§<Ö[X«ZfJš“~’Î €@ ¸Äs ÖG!6 öãbZÆnÒ2P’”ÄÓ,+€@qI¬ IN8P#öãbZ†oœ¤9á'íÜ(ŠKÊc lÌ^ZE£¦WÑÂ7žôIDATÈYU4ãÁ\¨[]EçWÑ´úÜí,»ÛqR~–:Î:¿§ªhrú[YEÖ>µ>éfý7Q}Ÿ£ÊÔ5ª2jûlµtÓ ·5FÉÇ2r]ùe¤]§œÎö¸ÉªŽyª->Ö¹OäÉÊÂçìs¢’kÆ=ÛÌ—!®ïb?.¦eø&JÒ’xšå P(.)CL\ ͯñcõœ‘kÔ Œ'ÉþU´@%v©Ëë¸Z\Ï%™² 8z«ï=ÇW‘ì“vd9I•‘}ÙãmÖYÆþ TD^ºÉ)uJ;²œñ„nO¶ÉÒ)‹l“å`jÎ6˵.ò¸˜–á›(iNøI;7 €â’2$Á”+ÁI»ujTÀ’¥—¹P# ³çf¾ŸŸIÄ*‘«àBä_oo÷;®ö»üP*V;ªŽ¾ªŽÞ+(ÈìãD¿@AÉ<5RÁu 0d_ö¸  Ô­ÔíOSÇpÛ <,Ódõ]Úp“SêÄí©vF±ãï²ÏOë8%ÿ,5šÂíñ9ˆîʹ”þp1-Ã7P’–ÄÓ,/€9 $1X‹ÌåLrÜö4uY‡e¹B'Þq—ªK)ê’﯑ĬFO¸\ßL‚ö;NÿulJîµ júfŽí´OFm2d_> LSòäËX£ƒ·IY/9¥ÎáKª- dÔˆÌuXÊÊé'‹‚>*wŸqû|îü—¼%Í ?iç@ P\²ˆ$˜r'»@™¦@ÂM–‘0©QIqh/;)ÎR#-~Ç È9Ê’áÆkß“Œ{*(0ÊIZÁÆ$uY¥¯…ªAUt½À‹ËqÙ‘)|œsJUÿd|÷ ß-ÃwüÈqý• |7µO•¹áPªú© õ}ª*oÉ¡êäÉ®~r^›io¸º´Ãº¬ê϶ç#‹ÀÒx5'‡ã9)¢»r.YþKÒGË @IRO³¬ Å%‹H°.g’“¶ç©ËÎ ¦}ÇÚ·âžÌIPÁˆóιSf⃪ŒÛqŽË/’Ü)ˆàúû*ð(ó—%ÃŒJ#èÏôIDATë>5ÚÁ—\¬¶Ôº”ç©J†EjôÅOΟÜä8&“Ô‡Ô¨Q‡œÙ:3û²dÏAµ3(ÓþÔÌœÑ_9–"³‹i»IË @IsÒOÒ¹P(—”!ÁºÉÍ«Mž¬Z«`Âk¿×öBóªÏo;ßù#“jýÊ¥}ŸØ‹i»IË @IRO³¬ Å%eH°N{"Åù…¾ :ûq1-c7i™(iNúI:7 €â’2$XIF(M’O²^Å~\LËØMZfJ’’xše P"یˮ‚I°Nr’„ìå'±Wã2t£–€’椟¤s P(. C‚5’|ù’|’u/öãbZÆnÒ2P’”ÄÓ,+€@qI¬“œ$!{ùàJìÇÅ´ ß8IsÂOÚ¹P(—”! I¾|I>ɺûq1-Ã7P’–ÄÓ,/€@qI’`’œ$!{ùàJìÇÅ´ ß@IsÂOÚ¹P(—”! I¾|I>ɺûq1-Ã7P’–ÄÓ,/€9 $1X‹ÌIN’½|p%öc8¸ˆ@IsÂOÚ¹P(—0- I¾|I>ɺûq1-Ã7P’–ÄÓ,/€@qI’`’œ$!{ùàJìÇÅ´ŒÝ¤e¤¤9é'éÜ(ŠKÊ`$_¾$ŸdÝ‹ý¸˜–±›´Ì”$%ñ4Ë @ P\R†k,;¿m: ®Ó2v“ôkšÎ-Yð @ P\R†k,ƒ'c誳®\LËØMÒHâÉJâiî/ €blÊ0[°wß}—Ž<òHÚ¸q£Ù‚Bº@ LLƒ €(|£P¾¦OŸNœÔ~øáü]øž@ P(Aä@`š‘Ùò$0bCdO ðèÉöÛooÊÈ‘#=ËaGr4@ ˜–;0‚`Š ˜’¨!i×ÑNjÛl³ }øá‡]„Fk€@ bÓœÍèØ áBhÀ9z"ImñâÅ!j@Qs5`ZÜ€<¦ACœò`À0™’!Y8 8GOP ®”6T‚80Úò·7 €bhª0Q,·Ñ†”nݺÑË/¿l¢È)”ü*ô§ P(”P¼² ?ûì³Ä#(2Š"ë¼|úé§+[9©8{$à80Úò·7 %r@‘Ë©ˆß8‰¬¸_ñI›ü*ô§ P(”´å˜˜Î€“¢cm 8ÎŒ¶üí €@ ÄšÒÓ%=}Ég¢G:ý“’*ô— ¡×q¾ôIDATP(”t器΀›ªci€ðˆ <‚¶@ Pb ÿék€’®> P‚‚C\å(JºòLlg@‰MÕ±4@ ÄAÛ P(±„ÿô5@IWŸP(AÁ!®r %]y&¶³ Ä¦êX Pâ íP(‘ŠmŒ±ÄX4£(1*;†¦(” àW9 €CðOc”tõ*€xm€@ ¤+ÏÄv6”ØTcCHÒA“'ÊEo+ %ÆðŸ¦¦(iêM9—è“;tÔ(ŠÄf,Ci€J] )Œä4y¢\ô¶@ P’:L€bZ”Bžè“;tÔ(”È…’Y)’‡Yu OÍêÒHƒä4y¢\ô¶@ PJÙ+®J»<ú¤ƒÄµ €’Æ<Ã9PbPrŒMpÚ}Š4¢\´¶@ PbLij €’¦Þ$ N(Ñ&\M8ýP(”t器΀›ªciˆû€.8¢Õ€@‰%ü§¯Jºú€m²Ì„×/€@IWž‰íl(±©:–†(á( #ZP(”XÂú ¤«O(Ñ&[ÀLxýP(‘Ší˜é æ8{R%ô PÂ'P@G´: P(éÉ1±ž FPbUwäP¢M¶€™ðú P(‘‡þt6@Ic¿†O"H¼ÐYT6@ PÒ˜gb8'J J޽ $Û¨’-ê o[ %ö$Ž(éèÇܳŸDx¡³¨l€@ äFh| ¨J@E%ª’mTÉõ†·- %r@áD†d–¨,HXôi 5%¬Pø$‚Ä Ee %a)Äq(¦ôD)å@²*Ù¢Þð¶@ PJß+¨.Jº:›ûÓîÓð‰É:‹Â(JºòLlg@‰MÕ±4@dDÅÔ @ Pb ÿék€’®> PЉ(Ž P(éÊ3± %6UÇÒ€dS'€@‰%ü§¯Jºú€@)&¢8€@ ¤+ÏÄv6”ØTKCJQLJä€bh,1ĨJŒÊŽ¡) ¥˜ˆâX €CðOc”tõ*€dS'€@IWž‰íl(±©:Ɔ¤‹I¨8¶´ö@ Pb ÿij €’¦Þ”s)m¶îôIDAT‚A†>‹± €"±ËP „RWB #¡“Pqlií€@ $$u˜&&Å´)…<¥M0HØÐg16@ D(œÈÌJ‘<̪}jV”F$Ôb*Ž-­ýP(”ÒDöŠ«€’Æ./m‚A†>‹± €’Æ<Ã9PbPrŒMpÚ}ФZLRű¥³ €cHSS”4õ&Yp@)]r¨¯K €’®<ÛÙPbSu, qPŠOª“Òé€@ ÄþÓ×%]} @)]b¤”F— %]y&¶³ Ä¦êX ”&©NJ§G €KøO_#”tõ)¥t‰R]P(‘Ší¬é æ8{R%ô PJ“T'¥Ó#€@IOމõL0‚«º#o €RºÄ H).(Jä¡? PÒØ¯¥I,HÐÐc)l€@ ¤1ÏÄpN””{H¬¥H¬¨£4v@ PbOéh€’Ž~Ì=‹Ò$$hè±6@ Pr#4¾Ô% ¢U ‰µ‰u”ÆŽ(”È…’Y¢²”§°---4}útëoÀ€ÙuÞÖÜÜìyv$E¥I,HÐÐc)l€@ $%w çG}D{ï½wö½-Ÿ›m¶½ùæ›HŠÓk)+ê(P(”â"zŽtéÒN€2|øðŠÓCÚNX`ɵ4Éz,^ %m™&âóÙ¸q#wÜq9òÔSOEÜ*ªZ”â* ¤´: P(QGþÖßÔÔDݺu³ å˜cŽIáVÞ)PJ›\+Åë€@ T^.*ÉóeNj|ÉŸäk€R|B””V‡ ¥ ¹¥££ƒÞÿ}kbéÚµkéù矧¿üå/ôÈ#P}}=Ý}÷ÝôÛßþ–n½õVºñÆiîܹ4{ölºüòËiÊ”)tá…ÒO~òúñL?üáiذatöÙgÓàÁƒiРAtÊ)§ßesüñÇpuÔQÔ§O:ôÐCé ƒ¢ý÷ߟöÝw_úÒ—¾D={ö¤=ö؃vÝuWÚyçi‡v í¶Û޶ÞzkÚrË-i‹-¶ Í7ßœx"즛nJ=zô°þ6Ùd“œË<œàxT…·wïÞ=[Žác¹®‹ë亹 n‹Ûä¶Y–…ebÙXF–•efÙùŽ>úhëœøÜøù\ùœÏ:ë,úÁ~@#GŽ´tº™8q"Mž<ÙºÓèÊ+¯¤k¯w¶i¤ôIDAT½–n¸áúõ¯MË–-£»îº‹þøÇ?ÒêÕ«‰G„ž{î9z饗è?ÿùµ¶¶Ò§Ÿ~ZË(_“”Ò&WÀJñú P(!r‡~H¯½öñí¶ô‡?ün»í6š?>]qÅtÉ%—ÐØ±c‰G8q8úõëG‡rˆ•xwÜqG+YK2ÀÒ¾ÝT=0h1Híµ×^Ô«W/:òÈ#©ÿþtÆgÐ9çœC£F²@ˆo³fˆ¼å–[hùòåôàƒÒÓO?Mÿú׿èÿû}þùç!¬¬½‘=CÖ¯ó6Þ'åø>–ëຸN®›ÛමÍ9n°d¨©_oÉIJ±Œ,+Ë̲ó9ð¹\òë'¬sãsäsåsS³Béà·ôÃ˗Ј©7Ñ÷/Y tt-}÷§³éÛ£/Wº›B§ ¿œ=ŽŽü#:æôatäÀ!tØñƒè ¯~ö;üXêy`ÚíK½h‡]{Ò–Ûì@Ý{”®¿«ªºÑöÛoOûí·Ÿ5 túé§Óˆ#h„ Ä£; 6 »<ÙøÕW_-ËèØ4’ki’+ôX¼(”Ô ßmòúë¯[Cø<¬?mÚ4:ï¼óèßø†ué`«­¶ ›n¾¥•À(ì{"}åÄoÓ±ƒFÐ׿÷+ ~÷§WѰÉ7Òù3o§ÿ7g%M¬k )¿ù ]¾ü+ñÎmxÇJ֒б´ÁÆT=0h]÷È{Äàİ4ué_éâ›é§×ý‘.¸ò:÷—7[ Äàxò°‰üù³èà£O¦/üUÚuïh«ívR—¾:_ ðZò¥¯#Ž8­a`æK||ÙïÉ'Ÿ¤·ß~»+ö½_ä@b->±B‡¥Ñ!€’ @yçwèOú]uÕUÖ啾}ûZs$èz-8xã€#N N,ÎþÖ/n†ŒÑWý]¸p5M»£EýºÃúõoj"…\fƒ,;)1ôIDATÍ]ݪF…ÖªQ & p~xùo¬‘°ÓλÄ)ãÑœ/õ>’¶ßeOâ6/›•í<‡o÷þÑ~D×]w=úè£ôÁ„“ÜJ“X ¡ÇRØ€’H@ùì³Ïè¾ûþô§Ö$JB—Àí\ò¯W¾t—Kx˜Øä:ë²C' $v³{¥öÜð¥7¾¤5æê»è{çZ—ùŽP­.E}…6ÛÂ}ôo6h°Ô• P(‰”C¦îbØ:û«+ß*ÌÏ#ᇗñƒ¼øA`•˜œpΕ5ZĹãgíœõók訓ÏV·(ï•ãü0@~@†oDÑe8»JœØP(‰NµXCûê)ß³žú)ÁU/»YÐÒ§ÿwè´s'©jÝBÝô¨uë¦óɧH蕕ГØßü^~HÜÏ®¯·FFø¡€ü 8~ ±¶wýÊ€mwÜÍ‚u~Ž?µ—ÏYÊy㉳L<ÉIzîÊ(”DŠ3Ñð£Õù Ÿüxóã¾uí¹ÿaÖ% ÊùK¾%y÷}z«GÊŸF'žùc…–4¸Ï!ý¨ÿwG[ΓbÝd  º‚“öP(‰·€Ìï}ùåoÿF\ñ;ë–K~P?°m«mwÌþš” í¶äÀ¿ó^ûYÏVá÷âô2†ÎõKõ ¶ÖzçËÏüÉ‚"žÿ‚ËI±A~§ƒ?‹‡_wÀÃç÷'ñ£ð9}8rì©ÖÃÖx¤#Ø#ð»Y¯Wèu”z;µz¿NëåQ†siÛo)6@ª0ÂôIDAT ˜ ]É@ ¤Pü‚5¿[…_>Ç/™ã_¬ßù +qðKãvûòA´ù–Û‚ ú¼äËñûûBû÷9ž?á ëW0¿œnÐù—ZI…Ÿÿ£_-¥ŸÌ»ÇzV¿üŽ“ ?F¿öѶÀÉÆïܰ¯0XâË~óÿÈ‚ ~=Ã-¿Œß¹Ã/ ä4òóE¾ñýŸ«GÓ´^èÈ6³w¯#h§=¾L›oµmh»a@a»áGܳÍðHè7íVšpãCô«ßÿ«$ïn[ Pº‚“öP(‘Šmð~¡1Ø> ²q$ažã‰ŠßÌçGägÌ ë–Í~§µæì}P_kžG[D¶b—\×6;ì¢Þ>´ç~‡ZÃ÷ü†c¾c©ï׿kA?5”ç!œ:âbkTgðØ_Y“$ùÎ ~»1¿Ñ—ßì›óvc•ìx?ÔŽÁˆõÏ¿òø<¯¼gõdR†%~³0¿ÔAÎ~£ñGö[óÞhìmÆúMÆ<‚!À~“ñ{Vòc¾–e°Þ`¬îBaÙXF–•eæôqÂæy –|nη󹵕Üsß\<<ûæb…à‘4~Ù#ñ(†=áÚýU aû“ïšÙf‡]¨l]¢ésÒ`kÔƒ!˜çü·Ñøù÷Ó/—=k½½™u‡=Ëyøyš.ƒ$nR’®dY(J nÑåKJœÜy CÍO¯»×-á~Ú-¿ÆZ€Áo@fèà'}îö¥^Ö/hµ 6ܯ'CJBÁ²4:á÷Û0\ða=ìcƒáWO9GÍUºÀ‚žo¾œ¾wál½€oÚÏ[£5×<ð–uGXй¥êç¸ë (6¼ ')§]V €b Ä¸Ð^asV’¦7 À+‰0@ PR (u«×Zòš¤æpð½&/]CóžÊMÊV™ù 4­¾-3¢ƒf-QåÕ1|œüM¼¹™Ù2ôIDATæetÕù"g[“T[on¢Ù«;ræX¸ÇÉ~Áƒkh|Í2ëÒɸ›×Ðg;ª.‘Á>&š“wI†¸å P(Hö1%û$:›ßÕï`û$ÈÆÜ“Ü^íÒZ— ¼Sh†z±­œ×¤±}ì2–eÀ •†¨;“DßzÙ‡&7ÚÇu>F=<ϵ­4¾^CŠÛqu÷¬¢žyíõ<¿žê¤¸×YE“2rÈ9`©ûÓMÒ‡]{Zc dN"|‘#(€ªÈ¡*xpôŸR[Æ6÷U{ÇB 4Í_K ÔhÊÈ!6Œ š¿Þ”'ZèØ,ôÉÂDíêVªUå÷R Ò¯†f­n£yêÏÒ³×1™¶×­£º§ÚiÖü«í¡K[}›·Ä†¨‘+ÚU¹ v›U-ù³ü­Jûá}\â;þÆ{‘ì¡sl€‡ ¤8ÙI‚¾4Í6 ³G(jêfZÑ{@µµì{q³#ñ·ÑÈ ( ©Ö¡#¯c¤­!73ütмöÈÈÐLÛ^ÇÍÉÈ0hz=ͪß@³–6Ðøºfë2N¶N†ž'ÚÔ„Vþc žœQÖùû®ÅœäP@_P(”';IðÙQŒgÚ3ÐQ«æ“d¤j Õ<ÓJCy´¤jÍÊÎïpïc¤-ùµn/ûÐÄ&¼ãQ“1ãG[€$Ç šÕd_âÉŒÊäÖY“ øj¢??<ÑeGfØ€@©@~‡Œ:l ! "Ó¼z=÷£÷€Y@ró†ÌEg@©ó9FÅAyJ ÜÓHýùò‘šÛr½ÏqsV4Ñø›[Ô(M͹g kæŒZÙNÙ:—°LêÒÿKÊ€­' Þˆ¢Ë˜‘œ è €R€ÒÿâUêù 4|˜ "ƒ„L½˜A  «î’á;eæ/¦¾ ƒd²l Ï»Äãw̵™ÑŽþ—r[M4qîBê©ï>m]6Ý’“§7¨g“´ÐÈómyr­Š-¿}GÑĺF5'FO¼ˆhñÒ…† À'9à@ P*P$AU©É§CjZÔ(ÄZ:Ù‚‡ÅÙ[z9¹MËp0P]–aP#(ƒ°È”.ŽùÉM7fGa¤½ž¦ÐäûÿéßÖý¯Ò¨ómH‘ã+YÙ.K}ɪëäì•´+i»èÍO(ÛwHàÉIàiï+ €’b@IR–I°¸C§ôÐ@t$f( %3ç¤ô‰1I€”fY( ’}äÉ>‰FfËì7¸lŸÙ4'œ ) ßñó4]É<¹q6]}‡@ULI—ôIDATPåƒí“EGƒ4Û€øŽŸ§é2éJr€äö'€@Á%ëûûpºz/OÍVV3k1œµœ&¯Ì<5Vé4çÝ<7Ûïæ™q½?wŸÜƒwìDI>ü…÷%7™Aöôõ @ ÷ûpüÞËÓNã†eÞãÃweþúÏÊÜãúnž*<Þ±³ÍIßt…'HòéKòIîS €s²ˆêWr1õú¾Çã½YÎ<8ú@K=ÅŒàX¯_ìÞïÃñ”é (ö[‡E¯J4 H¿YË^#hÜ õÂ?¼c'0‘~‘>ð÷0Þ›¬ØyÓÝ_ACFUÁƒ£ø”z$èbéá·û½ÇÖsçyìýxëe€v›µ™y'C—h@4·™&]j¿ °ïÅMVb@‚wìÄ*â;þ@ð˜< —xh†ÏûpPòßËÃk­Ä×o[ÒBSë2ïò©M3žÐ©—Ž?ßžP;ˆ'ÉÊ{{¬wÉ]ƒ¬²#hÒ’…ú9V-ñŽð£_A FúÇoE—©Ü„2«ï( %{™¡Ð÷á,hTóJÛ³õIš( Œ¸éUÇ7¢è2f%)@Cåö€@ ,Rn>(žä€@IyrrûEmñ^˜ kJr’2J÷€@ `%å6@ÑI]P(‘м‡–ƒî‘ kÂ/RÈPY#Iïoñ?_Óe’“Àéî+ €’ò_ÏIO®¿xÔðá(ºLº“ &9ý @ $PöÛo?ëÖÔo|<]}ÿ¸,°‚ taWýñuúú9?µüfÿý÷÷¦“ìžä$/€Fúû €@I  466Ò;ìhÛMº÷ ÃŽDÃ/]D³ï}‰ª‹D…QˆâG!’¢Ã™w¿DC/¾ž>údê¶IwË_vÚigzüñdzâ½’þ¤°INP(‰ªo½õýìg?£­¶ÚÚ ¼2,½KÏýéØA#èû“æÓ%¿~‚®ìC@  %õK¢jôIDAT6píCÿ£‰u tÖϯ¡£N>›¶ße¯¿ØvÛíè /¤·ß~Û›Irö$'y4ÒßWJ¢Ebé|@·ß~;}ï{ߣí·ß!'(ÛÐÒZúôÿvî$:÷—·ÐE7=JWÝ÷ZøôÆÔ'®¤üÚ‡œ]ìÜðä§4ó®ég××[##_ÿÞO¬Ñ‘ívÞÃÅî«h·Ýv£#FÐÿýßÿÑÇ,.p™þ¤°INP(‘ŠŒrŒ¡‹}ñÅô׿þ•,X@çwvØaÔ]]’vó—=6Û‚vß§7zÜitâ™?¦ÁcgÒÈé‹i ÒôÿûÍ{ø] F_b±›:,hžrÛÓôÿ欤ï_²€Nÿád:æôatÀ'лíMݺmâiË[n¹%õë×FM·Ür ½ð ¡ý'÷€ä$/€Fúû €@I< äXûÛ'Ÿ|BûÛßèw¿ûM›6¾ÿýïÓÑGM;îhÏaɇ–üï›n¾%í¼×~´ßáÇQ߯ŸIý‡Œ¡3FýRý‚­¥Q³~K?_ð'šºô¯Öü\Nêz RFJ8®ùÓéòå/Ф[³ ãÜ_ÞLgþäJ:yØDÃécO¥ž~…¶Ýq7_øÐ6ÙzöìI  ýèGtÕUWÑÊ•+éÅ_$†óÒ~ÒŸô6Ééc %•€â´ß{ï=zî¹çhÕªUÖ¨Ë/~ñ >|8}ýë_§ƒ:ˆ¶ÙfÏ_¬:i8^§^r×½ÇfÄCî{ì{íßçx:ü„3¬_ÁÎGƒÎ¿”¾ûÓ«hØäéG¿ZJ?™w]´è8ÍÖÐ=ß‘TûhÕýå‹X~µW ,„9O¾ì7ÿñ,¸˜µr-ýò·³ãgµ÷ÑWÞA#¦ÞDgO˜Cߺ`}ãû?§¯}{¤®}õë´w¯#h§=¾L›oµmh»áÑ‘=ö؃Ž<òH:ãŒ3¬‘™3gÒ­·ÞJ=ôýë_ÿ¢O?ýÔÏœK¶Ol <9 <í}@ T ‰è<ÇeíÚµÖwÝuÕÕÕÑŒ3è§?ý) :”N>ùdêÛ·/í½÷ÞÄÃìÜ‹]òÈÍ6;ì¢Þ>´ç~‡Ò>‡ô£^GžD‡}ít•¿kAÏ Õ£ˆç!œ:âbkTgðØ_Y“$ùÎ ¾«é‡—/±FyF_µœÆ]»Š8ÉN¸ñ!kÎ/?iÑ´;Z¬_ù<·ò•÷¬#¾%•ai΃hnÃ;tÝ#ïY“9qÏücZðçOˆçCÜØô™úë º§>· Ê9§‡×´x—á²| Ëup]<âÄusÜÖÕé7gôIDAT÷¿iµÍ2ÌZùoâ»Px‚edˆc™/¾¹Ñ:ž‡ÁçÄçÆ#Y|® |îCÆ_m]®c û,½ùæ›ôùçŸ1ÇXʈí¦=éáü’` ¥áŸ/)½ñÆôüš{ï½—–.]jÐÌš5‹.¹ä;v, 6Œ¾ýíoÓI'DGuõêÕËúÍ£6›lâ=×@’–¹#W¥ÔÇV[meÁ?/¤OŸ>`œ~úétÎ9çÐ\`Ý sùå—Ó¼yóhñâÅt÷ÝwÓêÕ«­ùOëÖ­£÷ߟ6nÜXk*O¢K$ðä$ð´÷€@)O>pmõ£>¢ 6Ð+¯¼bÁÎÓO?M>ú(Õ××ä,[¶Ìš 9þ|ºæškèW¿úM:•.¾øbëöëÿøÇ4räHúÁ~@gŸ}¶õ ~РAÖˆOÿþýé¸ã޳Àè+_ù rÈ!Ö%­8€öÙgk4hÏ=÷´’ôN;í¤îŽÚž¶ÞzkuK÷VÖ(Ñæ›oN›m¶mºé¦Ô£G5¹»UݺéÑ^gÐâ}\†Ëò1|,4q]\'×Ímðh_âà‘(–အecâXf–G­xT⬳βΑϕϙo=Ÿ4i]vÙetÅWX£zøá‡é©§ž¢çŸžþýï[·­óhYéçr¸v¯Ñ(Ó€€@ 6 4( € r 0ÎÈÔ$R~ø@Ѐ¹ PŒË¦ yÒê$æfH @”$ŒœÖœ¼óÂ%ŒèÄ4¢ƒ @&k#(ÉKài‡I €brÖ€lÐ@¬@’N{ÒOÒùP(”XƒLÖ%I <í²P(“ódƒbÕ%íI?Iç@ Ä(r}Ûk都ÊËvçþà2èzqÚ€ø‹×R— $)§]V Å@qŒ^U¶{9¨ì÷ZÆyœþX+¿Òkgl륶k¯ú°ÝÝŽ —Òê€@‰P*ÛqËŸ–!h ´´²íºDÿGk %räˆeù5m@E‚~a¥³JäÉ©tÃOª.ËŸ–!h ©6¹ÿ*Ï(Œ Dn’£[¶oXCõË—YoÙ]Õ¸†Ú3Mu´®¥††jllÌþ54‹܉ôIDAT4ц¢Ü} ÔÐØDkÖµª¨©¡ž[Ö;n¥æÌ¶Üãìz»¬ÓQ¯¶­_C êˆ ÍkI‰ã§ò‚<;ú<©6@‰<9Á9’꥓;ÚüÛ±nõɼïH&÷™Ro%þ¶æÚì#Ìe/Û$xì›YßL5V}h]FôŽµË¬zª¶xç_§ÔDÔ¾vyŽLÕµMÑ*(§vøcé캄.£µ #(‘Û@N†,ù—Ö&B¯áq“ 4³ßb;šBZZ00³~-µ·µRk«ýÇ£²o ïko£µ v=k›iÝò Öq‹×¨JÔ§eáõ½5lÐÇ…­ÓªHý×\ÓÇ–¯u-¡dXKjÜ&¦O´ ú… ”Î(‘'§Òu ?©ºŒ6÷nh˜iÁÄ”Åõ´výZÛÜ@õ ÍÖeš†uÔ¡ ¤­ÿì @²¯¶Ù†jm¢j5rÒ§Fh´6Ò@µ>×>Ô0ôT/³.Éq¡ę̈/G551­£…ÕªÞË(#A´Š²jOª AnĿʳ #(‘Û@Ôyw-¯mAŠ}§MYÖd_âÉŒ 8/ïTUÕX#9ûúŒ UÖ¨I;-çÑ UvíºzëÒ”z{NŠëqêtê¡Z#=U„K<•—xèó 6@‰<9ÁƒbºË8sé×7¬i¢úF57D!ɆukhùÌj V–­mÏ^Æ©iR×fÔþŽþ³eИ²ª™Ú€ã„™XÍ—dªIZä¸Bê´ZîPreFqÖÕÛ£?|é(žü1ݾ†þMSÿP(A‰Ü¢M½kÛ@2aq­]ÛB‹§ ´¥~}GPF×®R—U䎛FZÛª÷-l±/ùÔOa©¢)õëlÛ[h„5YV¤LXeÈð”‚êTÇ·Ô²|©~ÍZZeÁ”=_Æn4êÿ‘ÀÒ”Àp.é¶gJäÉ)Ý„¤#NºëiÙRärÍÌå-V£²]–µÍ­YÐXØ’™Ò±–¦X—]FdFK:¨a¦ ;µMzkqu*±ÚZh&Ï=ÉÀÏÌUk"V³ú ý…2ðkØ€ 6@ `%rp&ÈèÖel¼Ï)ü|ÚÕ„ÝöØ…Eâ1!ñ@Øa Džœ`ˆA 1Ýe Oâ8²Ô€?¦Û×пiê_ #(‘Û@©“,ê+\H`iJ`8—tÛ3%òä”nB€Ò¿…§SYj é/”_ÃL° #(‘Û@©“,ê+\H<&$È; b”È“ 1ˆ!¦»LáéG–ZðÇtÜD®IDATûú7Mý @ `6€ À`ÆÙÀÿÛ‘¾ÜóÚÔIEND®B`‚jbosscache-core-2.2.2.GA/src/main/docbook/images/CacheLoader.png0000644000175000017500000001755510660354341024252 0ustar twernertwerner‰PNG  IHDR,š£nK4IDATxÚíÝͪlIZ‡ñsu u=ª¦¬’Q<èL”Æa‹ÕT ¢c±i? kÐ#ôH( º"B£×!œ‘P£‚pcÁ¡<™+â}#b}ÿ^žÁ9{g®Œ\™;žŒXÿ|U”RJ©ê•S ”RŠ„”RJ‘RJ)EBJ)¥n'¡ï)¥”Rc5$!fv6”Rj›ž“„HH)¥Hˆ„”RŠ„t»Î†RJKB¯þ¯žßÿ;•xÔØSÇœx·O‡„”Rêš+‰ƒKèí½HH)¥*¡æØ¨òÃoÿ±t³æã>Þ¬òˆ·|çøO‡q$¤”R§”Ðã žvñ¿­üãéƒV~ûôfOÿQä‘RJOBOÇ=M—<Ž]âªÜ1u|RJ©sK¨{@S¿YvˆÓw|RJ©sH¨)§#H(>®"!¥”:™„Jõúÿ;hN—=½YóPõG,áY»ÇçõæÍ›—³ñõ×_{K)¥ÔA%tÕzyÊŸ}öÙ_|áý¤”RûKèVzy²ï½÷Þ§Ÿ~úÍ7ßx?)¥Ôκ[½yóæõë×_}õ•S¡”R$´u™ˆSJ)Ú§¾üòKqJ)µ›„”‰8¥”2Ú§¬ÉVJ)RJ)EBJ)¥ )¥”"!¥”RŠ„”RJ‘RJ)EBJ)¥HH)¥ ‘RJ)RJ)EB$¤”RŠ„”RJ‘ )¥”"!¥”R$DBJ)¥HH)¥ )¥”RW’o WJ©Õ%TþósÀñ!! ‘ Hˆ„pt^ýÿŠßeî-×;0ÈýÓ_~öÇ¿÷ÉGßé–?þðƒOðú×ÿø$L3ÐwÿKBÀ[¾ùõÏþæÏþèõo}ô‹Ÿüð¿ÿõ¯_~òæW?ýç¿ûÓßÿíßü«ýÁ×ÿþ÷$LîßŸŽŠÞùáã?Þ¹Yå!ß|ÄÇ[¾sp–ÂJ¼èOþðwÿçßþöŸ¿üäelôâ!Öd,™æÔÍTqÒ’ÿ–ްôÂz³p/c G½õÐËxèí¼ óGBu =•ÄÒ`%uÇŽ›Óyëüâ'?¬Üà—?ÿñ§?xMBÀŠÓqYÕ/8=•SÊ@®a3>ùèûß^Zâͯ~úñ‡0MBK&hJ¨©«àÜZüø$„-V]‡oCBÀœõÙõŸÄ4‘CÅÛ¢FB$® ‘`xuÜï|òõÕqÿñNBû„H®ÅÛÄ„ùÙÞ&&üòç?–˜Øn^îeÜóñ‡|›÷26’8Øzn aþnž¹Ç9Ô›lcFÿö® ‘ÖêIˆ‡@B$„toø4n ´’ –FBõxìúƒ.Ô¿|aé6%–úKbJ¿?NäçOÇ=Í6ÇÇsÍáQðø)Ý$ÚHBñ:òˆq ¥UÁ±TJ9$òÒ’P§„:ÆÙÑOê'GPvÜCB !/-B ž^bivÇ}kêm¨SC|º¬r³ŽeO—Ô§ã$DBê }´¿Ì*v áb}ˆãL2Hˆ„€„H@B$$DB"! !ÜZBJ)¥ÎRFB#!"! ‘ ]”W¯ÞK÷Ý¿ýÇ.-yèøßÞ²r—§¿Ê¶íÛ÷½:ÁOyÉ:žàfï·éïÉ)/1Hèó¾Þ$rãY=Åz`}ÜfPw¿s„tÏ ¾N!¡-ßo›‚„H(-¡Joøø«ÇOmßýïw[¿åÛ7ëãÝûºì¥eÁÖ6-lüÓ£=ý´›jð܇^jÀ;G¨¼ú·¬’–îøx—¥³TcÔߥõ§3þŠT^âÊ ·ñû->vY:'õ÷ðà7•P¶Óov[õwðÓg¼UͶu´vé^KÿŽ<ßf—Zoð¬‡N²¥bð d_©ì aýéÔïùsh¾ÏãÎÛàEï;“- Õ$ÔÝÑ?g? w7㻇}<~wk› 'þ0èÑé}Gï³¶„*/Ps$qIð±Fî?þ–ï·‰êþ0úÞ”Gð“cü£ndv%ò«ÁÖFþb¨9á6ضˆÒž6»C$}ç¿Ã»©?Ý Ô´1 M¾&Ô¡¢Ê{.ø«æœÉ” BãMJýÕõÍ]L™?Œ?ôª“0w N”5»ÔŽ÷dê#Hö)ßðÛ¼ßÆ%Ôw@B‰ÕqÁ‘DpBäªf|YZßÒ¸ŽÖÖ//Ç?n°*aä¡ãÂë[\?O5¼³¶%þê¯ØWÞÍC51þ†ßøý–ÕRäʪ²Oè|;öšµÐ5Üüýæm@B$dëëÖ=‚§Þo DB$ !"¡ Lk¤MŒï¶ý²óÆ ‚„î˜WÎWŽ‘ ·£®öz¹O!¡íãÝ€";®d"ÊIâãž ×äSÄ»…—¶—óÄ»Ev\ dÇuì1Ü7>ndSdÙ$AN¼[ꕊ¼—NïÈŽË¥Z9>n¤ÁqŸí"¡ÔÅ»u¯x7@vÜûS2¸öë õÚ+AN¼[ö•*wŠì¸¥>k$ª«l×êµW‚œx·ì+Ÿ»;W¼PdÇU:ˆî¨«²y|\_ƒË r}«º¤^Ù"ÞmÃx7À>¡k.<=NƒSëÊpùíhNHh£-{§ÛpºMK|¶ ! ‘€„Ì–¬}v–F6WÌ :Š/3ñþ ÉŽë¹ð>ØE®7«3Íöï»\ÍîÎg«lÁÙÒ$’÷ùIƒãš­-›DŸÅÛ9e‡ï¬|¶n Å÷ùêhàŽ MÈî^Ü78®{¯es›NjGçø,Ó6\’Çàßl«o Ý=;.˜ÙœÝGÙÝŒzp\wkã÷jöª‘íãšØÈìHe–„Æ?%$tåì¸àgíàÅ¡µƒã²­-{DŸ•áo›ÞȵŸH*h€@B²ã¢Ž©_•õƒã¦4©¬}6e$4±‘K¨te¾$tÇì¸È¥ïr˜à¸ŽÖ–•£ÏÆW%lÐÈH›G´y¥¬Jì‚EÆž@B$¤¿öŒ"! ‘ Ý`Ò,µÅg¬³È¢†mÎÃzguÕ©?+@BwÌŽ+g‹Ë®ÕîX9} †v¿zÔ€çjHèRÙq§‹O«ˆ!’ Qyš•ƒ/íž©Ü=a\­žz^}+¹›},ã$в゠iljëhp¥ÿJª©®ÊÁ#›FõO"öìñ´·ìv®ŽÃ«/ ¡CgÇU6±3>n¤Á³|Öa× Å›IÕë“P‡‚O|Ê<çÈÛ ¡ceÇu$¤í×é ”mg|š.冸hc–:žøøÚ„ìÛ ¡ƒfLJljëhð¸„:æ¾RêøÂˆŽt¸TÊX´Ò6*ë,@BëfÇ•VjYäCô–ñq} î˜5ÊÎùd§¤ê‘Pó‚|Ä”©9ÌÁU Ý×®êÏñ«Þ:ÊZÛ÷»x[O9?+ɉ ¯º¦„N—g,€ùD°­ ! ‘ áÔs‰©Ë­ÙSß9œmÜ@Kg €„."¡ú%-%t¥p¶Yî© FI ¡ëH(Þo.]Zü62ºL8ÛÄU ‘SDB Ù¬zÖÇ;†³ ÝÝC;†³ Hˆ„€„HèDÌý¾‰õ¾!’"±ý¤ßô‡Ûì™v|[Ç1_àvù F朷];Xoîb¶#aÇgÚ·@ÿh/@B‹1nOopÉD¸æVžî5ÜÍýUñÔ¸ÒŠì‹ìÄZz¸xŽóLÛ0€„Òªe÷rž4®´véV¢ úš×Ì—kžæ)á­ÄÙ Gx¦‡m@B¹ì¸ oRóéçJ„k~ò]CB%°)x¢„"/D½ G{¦‡m@B=Ùqk¨œ!®9 ªÜ&{­oäÑÑý-EWd‡A‡}¦}Ÿ¶| JÌÅU> ƒ²³Û'Â¥nŸº P¹¶”mgö,5/ÿÔ_î‘Û<Ó mü$Ô¹*¡yµ¦œ?.u1)Ò5×óÈ—%©qÙ ?Í¢òrÇWlóLûÖËmÙ0€„rû„ºÿŠÎžwÀ'~á×âà‹ËI$t> •3'­»Yé{®~sÊšæ•¶s1HHb Hèžš;1"\näî"×:PvܾY#Róà®E®·¸…¢„£Þ:‚È"ßuJ.'r ¡³fÇ5÷06£Þ*Ù6ÁD²æ§ï` \v?Ó­ÂåD®$tÐì¸Òõ¶ÔGƒË²Êv¦Âå¦Kh¥†$tëì¸`×¹T0¾ã}–‚àøî¢³‡Ë‰\H踊ô>Áî©yù§)¡ñ¨SárS$$r ¡M%Ÿ‹+Õ`®æ¢ƒfo\ò0eUBêCý¹ÂåúÖˉ\HèÊû„FÒ²7hÀ•Âåö=“HèÜÚ¾ë¼R¸ÜÚ›œ !±=@B$ !Ùq•e»ÏbÉ^p_ ÉŽ;æ…wHHvœì¸÷ãÔÊs‰/Pžò €„dÇ]3;®ùXÍg=¾÷ ÉŽk«â’ÙqƒJF$$;îó>ÕGWñŽþPÙqñýL €„dÇ…æ×–Pi}ÁA6r ÉŽ»Kv\‡–²ÏÚªö í¹>{ßåËû† ÃC;JˆØ !Ðe® ­zY^’êC¿_N $$;.—WÙRóؘÊ×itV{$$;n0;.ÖÐñÛ"ƒ ÉŽ‹£ünŽ25H©oúqDBE"Hè¼JÅÁUÖg÷}2 .4蛎ˮJ¨\¸Þ=°®²\¢rþë‰Íùظ³—žNsHÔ·¢Z"HÈfÕ3q„®g¯˜"/@B$tënÇà'êÖ$DB"! !ê˜t:o”Ü`”ÑÚñq+=qq 혰Æím£ä¶‰[»µ[öÝâª%••@öWi¥½•³EÉEÖj!>®rXq–bƒ„Κ¢‰áꈢ9i”Ü”!ÚJñqõͪ⌄@BçÈŽ‹ð¬j¾^”ÜÁãã&þ05(  ¡9‰ #çãC«Žƒï2+Jnä×Jñq먈HèPêë4Ï%wðø¸‘¹)q .;.˜Ì?ÈÙ£äêÕìR׎‹ØW@œU !›UÏÁÅBtÄ$DB<´Ñ¶¤Swë €„HHèžÌI+­d;Ì­íøùu»<}év ¡[$&tüE'Ïmã?òvÛä×e>þjJ· ]_BÁo¹nîð_#Ï­TãΚ)gñ´’ :«·³¹r:¾Èû°ùu‘=F‘³!Ý$tw Õ³jRoeÍ<·æ–£frZðÑûÇã{Hëçç,ùuÙ·H·[a :wv\óï§ÙY?æ¹äTÚÜŒAK]É6 %°A 5?ytä× ¾ÒíH忉 ‘?žlšËô<·‘ž.ƒ6kØ1Ë@Ù†m“_×ý@Eº„âf6•k½<·àäáxJ[ªSXÊ’éîqºz4g¨ææ×Õ¨þV‘nW¤Û„âk¬ƒÓä¹EòÐ*Éiñ#¬è› ëhLÙ5¿®y¢ê9uEºU °OèÈ\ãÏrp‘úUϳt;€„üñïüް¥÷bÉ­Þ„ !  Ý\B¢äömüñqë}%’€8ÐÖøk%wõ®7ëÉ ˆî.¡JDX „nÕ×þQr½Qr³ޝW9¬€8K±ABý}VdOh‰¥Ï‰’+cQrS†¡+ÅÇÕ7« ˆ3 5²ãâŸøêWEÉ­%wðø¸‰?LM' ˆ >1aä¬ûƒ¼(¹¾½D‡Š[Ï@E@Hˆ„{«‘îO”ÜD ­727%  ¡ÚåîÁ`7QrƒtK³dñ®sƒø¸ìz¥×¨ˆì:8¢äNwnÄ$t©ÄQr+í õz1Hˆ„€„H@B$´å|TßuãŽ9œà^xÕ¶œ<sGB»IhÖ%Šî̓ ’ÛxÇ¡º³ó®×ºó«¶å³fZQB•¨¶´õ¤:WY"œjIj=t³µ¥èZfŒM 6¬~„H\EäÌ/mó >ôÚ-Lm˜½É«»ˆ¹#¡ãîJuú‘˜Ôò`OðW³ZÛ½&»IöqM}oPüyÅÛVZ1HõŒµmZùðq·W-5RsGBÍŽ›80 Ž'â›ÆƒÍȆà¥> wì-ÍZ­ÒÓEtüf%RÚ_<·…‘kBw{Õ&J¨"¡]FBÝs_ãA“%¹·?ò«Y­7{Ö¸j)2 ØqŒ„öhãŽô­W}Õ:N—˜;:îW9dU ‘ÌFÀM¿ ÔÝÚfNóJAs§’º_ˆ‘6¬ÑÂñu%×{ÕÆ%TÄܑСVÇÅ¿> ²N!r tîßX*M.•ÞŒ#Ë^é ~Zšx™k¾_<Û¾…ÙQÝ^µìŠj1w$dŸÐw¨\£m{¥yÕ¦ì[ò—HB$tÓ­‘WjÛ6Og¯³çU ‘€„$DB+Í'H¾@B'[¢½‹]êûðïs-H¨±-´òÖü>ïÔ—4ÉWHè¶jn#‡6ÕkË7:_vÜRäZs\ÒÜm—J“|w ¥6Ï7gÏ:‚¼Šä+ ¡”$â Tÿ J¨H¾@BW•PsvnV0—ä+°Oh·íA $´‡H@B$ ¡U/­z@ó{Hè‚Ùq#ß&#H¨?;.’ W"úVcˈ€";.5÷É2ŽTdÄ@‘W ÄƒàdÄ åêÓh•˜¸AÅÇU €„î(¡TLœŒ8 ¡tŠvj:®~IFÐnû„ S€„öܬjŒ$$¶Hˆ„$´Ë•¡UhÒ ÉŽ{~AHpPbT±”WZñqñÕq‚〄rÙqKE=$8Hh~v\\%TÄö !±=+è©ÿ ‘P;É-•)—P€„dÇm°*!Þ«}BíQŽa Ðn*‚〄Äö ‘€„&Mµ­Š¸9$$;.×MOéÓÅÍ€ì¸÷—"ã–ÖO?Æ.”gÑ>}{`—Z+n ]9;®¾W´~Çz\$ø@ܺcvÜR‡Þ1÷ôWH¨û@B§KLÎŒÅ;÷Ê1G Tr1:klO¼ËÞxéfÓ%TÄÍ ¡S˜VV%,­2ˆ\.ªOßu¬Jˆ?–U HÈfÕÄd C !ÚaŽ1 €„HHˆ„ÎÂAÖì{q+²ddÊ m”—ꤎp9gídwÅÛ3±å³$ÄC íÛÓÜZ_ ]?Tý —BêRëëê㥪¯öŽgåÕ(x2+I}ñÍ^¥šà×<$Ðz?ZŸkªôþ‘ÙKxvj+žÑwØúö£ÈSK̾i½§vŒŸ|HèˆÙq}Zêõêé¢ÝÛKGžN÷ÑÊrÜC÷Él&õ5•¹t†ƒÏˆ„:bvÜ Êr€éxÂÛÈÓ©÷æhä M%°¼dÒc ¡£dÇ5g±Ràø?Ưí|›CüŽ õÙ5“úR3‡#az$Ð#¡ÊÒ€`G\ß8×u\Ÿ2A7xßæò‡ÈÉl&õÅOBóä»ÐQö ]x-ïÄ/2¿Ò¶*ç ¡+Hèø}Öൖ»Iˆ’˜$DBº›„êßÇzØËWÁæÁС³ã{ó½zöqåð@B²ãægÇóÐêQoõˆà+ H5ud‘tê˜HHv\c–liŸM „ÂuD¨ErÕ⇠î0ÀÅ}ÃC ÉŽ›–ܘ"¡éOª#ºmé*Ò˜à&ÙìÐDtÞø ̶°~6êoì¾–·0×ßi#·ƒo'"¡ëdÇ•L8[åf‘Tî5·O\ê‚TТóR­ÙF¦}G‹ Xg=‘ì{25U@B$túì¸ø½J5Ã-ާ׳…²}bGw.•óGç4{üh•¡öøÐpÐ@}¯5‘Ðu²ã"C“‘é¬ésq•ùôT2P¼µñNDt^ó€Ý'¡¾ÚeÊ‘Ô;p–{º_k"¡sgÇU&|‚×]+-S˦ûzÉJh[ó p0kNtÞÓiގΫ|UD<ÙE"Á£UPdWjL\•PoêI×ÊÃ>¡¡·ée–ŠÎ»ÀRé§;½@N ‘СÿTR×™ümŸ®sô9$$1@B"! Hˆ„$ ¡ùRJ)u–ºš„”RJÝ¡HH)¥ )¥”"!RJ)EBJ)¥Hˆ„”RJ‘RJ)"!¥”R$¤”RŠ„œA¥”R$¤”RŠ„”RJ)RJ)EBJ)¥ )¥”"!¥”RŠ„”RJ‘RJ)"!¥”Rç“RJ)5RýRJ)¥6+RJ)EBJ)¥HH)¥”"!¥”R$¤”RJ‘RJ)RJ)¥HH)¥ )¥”R$¤”RŠ„”RJ)RJ)EBJ)¥Ôbý/¥2Û•±m"¬IEND®B`‚jbosscache-core-2.2.2.GA/src/main/docbook/images/logo.gif0000644000175000017500000001367010750374443023040 0ustar twernertwerner‰PNG  IHDRAZ&häÝIDATxÚí]Í‹œXןf¶Ï?Mֽɲ½ dÈ*Л@6M¯BoBm Hpá„‚¸0 “I Ò‚A‚T¿Ò6E¶p^-?ª¬Ò{¯VuMwúüxdæé)½žû»çëÿxÀø §@ "’ @ "’ @ "’ @ "’ @ "’ @ "’ @ "w ÌgS˜x.ØÖLÓ,®1X¶þ$€Y”à4!öG‚ñÄn0Žã6®'·Þ c‰OÃ5ÞËzñ<‚(‚$É0Ru°f1¾ø? áÄM‘€OemÀpqüTÆ)bµÞÌ!׸ÞŒI„$Ø‘¯„Q·un#Ð6îsIšs”ü{ùĉßVlˆüL’×›êý¹«å?'A@‚ú-’`~ ÁE&¼·š‹£ w' œ Þüa“ Ž$øI0»dÐ"ºg˜ž™n;—Æ“IIð¡‘à8ÙäÁ{D€âmÊÉEƒ$ˆ$ø‡’`v™S¤Áû[á;š¹Ü"0‡›"’àH‚"Øþ|ß߸<Ï×uÀ±m°Æ:ÈÝtTæ®{':‘ 2ŒÝ¢xIeq4ÏÒAäXˆ+L‘ï8 :tÉtñM™,ø¼†Ñâ{¼HËK1}ŠÇPáG.Î/’à'Á”´º¹n0‡dR݉+(IÒÿeW¼øçm")ÛŠcˆÿ ¶úi誧‰ÔÌIÒw®âå|e×L‚Ëq" Þ­vmš½H0žOÁIMnEçÌnއ¡¬‚•šgÛäh'Q®eÂH‘@øf?WÚ¾0”@3,fѽh‹u³$²KCO;>s*%¿pÄHªÑ,ËÐ@…Vÿ#Ç 0”0ÆLç[fë'sð Ôôý<׸!,Þ™µ•윣Ð}$¥òÞ$ë#»“­d}V&¾7¸.x^y¤ím·ž, †¸3s8šº JBG'½†Óu±ÎÁRûåÃq¢v0¿£muÙi<)iNÓ;£Mß°(jŒÁLv˜$] ùžó5¿ótEàèrç¶DÅ€ Ú’G»æ0‰ÌQv³ã“™#‘ë”Ò¤Z$Á+ Bj… yÌÏqÔí’t¹¡LÁèȉÛ>ò­XÁÝjk§2”½;¿¯’˜œjL6øAq“næj;ÈJÁ1OÈ[ŒáÀ ’…-F=²54ÆNmµÿz’ ˜=HìäÃKÀ¥8ÃÇL¬”tOÑ øœb’n®uF¢i·Ï¶ºc¢‹DmÀžÁ^‘L­Îi7íÖÁB*ã:M˜Ûåü¶RÒp(âàÒöíZo"¼Ç)2¸Ó¦Á‚ X\Óé4½òŸL²Tì±CÚ—ØòÂ&ÆpÇ"¶Ûóõ]·%µžŒÙg[½ü2·§¶Ø\»Ü0¾G‡´|§ p»lOiqÜ^^.ÉÊš»ê=Øwwf”e%Ó1ûËàyàXW4¼!(ÄtžanÂMRâÓM`âÁXW¨Td»IçØg[ý¢–ÄB‚¶×â4Ó|დÐt ]y(l•ŽÃ¶ñri›2Èrs0aýšÁ–냡2=ŸÂâoÕv§ 3´%)ê¢-™Áÿj1’`§‹WÀØv–Š6#Ó…ÕÀß|êÊpÆU]Wc„VÒ4ÛµÖd #’–Â7¸öÙÖ-¤nì—)QêTž& ÑØd>…ï‘£šL`H#4͆z“ Llb®+ ¤Ä¶Þ$ÝÙ%3—ÒÏæuNÊÖÈ}}:„kœ–U ‰÷t?íó IPuÙ´zŽšHôï¹4ssm$¡Ýþ¢¥1y™F=e!ÓJŒ18ÞÂùædï³­ž dós¯$ƒ¥¶§¥XaOj$äqÐÛÌÔ6vúz#™µ‘O^#úFa Ê{ší)03›`‰t?ûý`I°2WÅÐ ‡8Ä`ÇT±ežýì2ñ%ç&ª>v!lÌáˆ!œNa1šûl«—4ÕÿòœoÃ,œ‚çX`h©¹6¦T™´o6™1%¹ŸFþaƒ©É Aéàoh¬´õFIO’­CRp‰¼¡dð´v3\éèŠAŸ`©ÉÍ’^/7[„¬dñúibò¯ù“„"¯Í`ÖGûÚg[½”/ŸåÕ»}Ü1žCàÙ ÉbÇt/²¿Œ%9|bé êÙûJ5óYD!iòzãg+·Å: ÎÝÑVéoÉÔlï«ê" öMYiôF.‘$ر ŒIþ¤šú¿ÅØS³TR4°ý)£†´Ï¶nÁ'Èkw§*tªfß7ñ=L]IdL§jqsâÁš%{g™JºÝHÐS 1óFÍç0oºRË#š,˜Ž.’ûK‚œJÈ ÊÏîFÑ<5ׂ…ÉÂrºƒkØí"O%j^KÐ×ÙO¬ÌœÑ"ß"h–O=^´Ï¶î{txSÑËM²D»`B4IëævHP£F»‘ q-lðìvúë'Kwþ©¥tN쌓 Œb'a¤‘`´‰”wT=™Âx2'G=÷ÖÖŽ5ÁÌWµó&©ÆÑçö(pz™c!Áþëævæ™^E¦Ëý«h Ú]æçA^DjGä[]ë¹ÿR§8%EûÇõ>ËÛý$Ç>Ûê¢é´0 û>¹ô»¥fýHǧŸ^øôíÑÎ5—õwOÞ|ï; ÎAã÷Œ3çîÄ×±alè Ž†C¸ˆºOñkß-ŒWÓ:šÆdÍîûµ¼n-ÒxâÐ]ÙAÑÃ÷Ù+hæzß³ÃͲÀ¸¨ú3yÃaâì²]æ†0ÒÇà9z7M(wâ='AÚÆ¶ý·bÜ]“ Ñ/Öxâa5®ôþäæ­8Ä)ÑÆ Ó;ÌIòI$ÈbÞ'E¤qÄv2 1¡›õózûlkËÈzsò/å¸h¬çáÍ`ÄÑ}£Ù÷Œ'á¬^!ñ»¥ÈSƒ8Ƴîq­ºö]"AŸTÏ‘WÀv°m»×eÙ.t‰±‘ I›£¤(>Q[¸%Mrì‰_'AJ {»ÔôâvYÞ?ù,ÈëþÉCf¢ê÷}¶Õ#8Ò#7Œœ§Ö\˜! Lòi ƒ+H*Ö9:Ì&w3G)rb³DvTÝLÉŃ %èMrܳ&Hàz:În±½&H¬âA._E&²Û AzqNÙZ.ÊynÄT½bN1éš„,sÚÏÂütW(²FôAÆólJ½¹6÷Å>Ûêg+TÈdþ\æ TZZPC•¢éX"¦8%m–ï$˿ţÊúÆýû%Aò‰ hÙâÙÛIn¬Ïæœ;ÄSœ4n~!énK¼O±ûG;~c„õÛMyJ8_¤Õ"LM›á€1™„  …Ö ²E×x¼Gè«¿š‡±¿¶¶¥ÐC¹Øì€æ•á“Mš%I{¡ù%Éæ_ó†îRüf:)m…’ß*mQØ/ Ò¸¬é6(W©¶+e©:ØþìH¡ÚCVØp/w¡Ð3©'È¥mÎ*¾,·~U_œs°ÆcеãWÆZ|K!½~œ0²ÝQ`SkÁÕm®éU”ÇCÖÀÏ>ÛÚ…kPa F UpƒYM3‹£sÄX°¹Ü͵3m'“&¶øƒiæ÷â»*Mçßã)hÄ Í\CD}Ï$HûæA³§Sî(cN‘¡¥–,¿À3 -¨°ï#¤EËšG'À(;Væùy‚¶Ì2gCðãZÃbד`ìmžÒˆg˜”òçëI¯ûlk¾A£Ó‡×¹\;vÚüÜS)Ãu¨×шÁ·Túzh-51}hž—T°œtó÷\05…Þ^£)½o¤¹Ù kDw`Vú/“ˆa>»GÎÙóÃVÓm9ñß’ BÖzbzY£^åè›úëyÞÌ@’†-•MÛ>ÛÚÒ÷ s·'üÈ!4­3ïPV@Éjûí ¯­kJËÕ|Ôsÿ$ÈZ¢.î°%W÷ñCwJ–vvXZžžd¼_4ÎÇTó¤ãp´öƒÞ»¬¸Kã>ÛÚ*·‘l+ê”ãV]5Ñm‰©ôEîîˆY{9¬ÿ‚/æ[+2» ÁÌ9½}iqìé+C{#AÍew¤Î=}7à˜î/ Æòî4NkzgÚÚ‘‡Œ½âe“é¼i²c‹ˆ-ˆ”}#g{"ä$³w6Æ­‘àB¹wК '‡Â~Ùùõ¾ž!/ƒ7cOÞÔn™Yo¬L_.(bÏÈKàLÙµ¤™?†!·Ý¦c2FËöÙÖ®ØÚv (û ªÛ´ç¾Ñ¹MÅœ@BJúæè ß‹±­}ã–ÒÒ¶$ÁmîϪS÷ýÚaöùRo‹ª:½ÏO]$cì¤ ¦t,·”•Ün§ÏüY¢(Ï " ‡C•Ñn>€ yùüì#ÔL‚™'µ'=KN%¸cEö͇ãEP³rÿÉ]nkwß6:õ™ÏdÒÙâ#áñL†ã…C%Ýh«Jª¦Ì±¹¢)Œ5…1aYÃ4b›C’©¯Ss0ÉëUg ’Å0±Mæ÷Ȱüí=Ï[Pˆ£L<¬± º®/.MÓ@7²ìtÂy >b˜Mƒülµi€aÅ\0¶]ÂÝHÒùüú|gíé|[–Þ$€ÙŽ*=ï³­ÍO<‡éÄǧýÌû¬§ò¨iißͼôÿ,Jvú>ü¬ºôÊü˜c <Æb [´ ó0+·)s¦U|Gù¯¨$š§²ç½xËñÝÆ;ü âI@ "’ @ "’ @ "’ @ "’ @ "’ @ "wWàÕÁùþ«_Õž(º#ã¸ßÈÇßæx‘ˆ? —Ÿað÷ÿÒë ºÕï¹_>Kï;ïúŒ#Ô[Œã5¹Ö`ïñ"$æÆ`A ü‰Þé¾äâSN&ƒ;A&±+ŸöG8ùBÔðúŽ$ˆø£pSý›÷öhA ò¿¿»YÁÆYN&o~<ˆñ"™¨ˆ s#] OOÁù}]Fûí1ÏÏÀúú ¤ò·Ž@7.6Ÿ÷õH‡O–ÏKïó.oj¿qßgÏÀäò7˜'/ “(Õ^ ô*Á7`¾YÞ3øû1Ho?Áì¦ë¸²~¼áðX¿W`œöIHï_×ûëY:ÈÅ3ùWŸ~¬ÍñÀo:ÏILþ}·2Ž#Ÿÿãyþ£ú¦k”š¹ïjãåÒq_KÒ¸^Ì3ÿèÕO>ßã ÝÛ¥O@Kï ÔËymž¿O0NߣpøÜj.ÇË($Aš^öÉÑ I¼H…o)TãŠ.@9X.žÁ£'©€/ÿ¿n-‰Åy{T[HËçÁòÚ— ®>/kûpyŸÀý$ôù ôÃÿU$,½|¶|ΡP˜x¬ãº„Ñ£üoÖÅÊê¹9‡aÑçÜo¶ÙßìÊ¿êã=8ZY”ÇPVkg›“f‚qÞ¬Œc…DóëäÝoÀzµJ`Gµ÷³ÐžÒ1IkýÏæ+b¹—Ð?ëäIírµ6Ò¹^ÌkÛü}¹ N'ì2^@ 2`¦žV»÷øüj©õ•‚øôC± õ…ßfAPo>WÚƒý¶¾—ŸþœÒo“=Ï´.—Ï+JEn×ß@¨„ö üB (ïÞLµèˆ…#½úÜQø­øÛcp®;Œ«êÇëÚg 3¿Ùb1¼ËÔjNS îæ¿áê§Pµc”ã½ù]m#ãŠ}NšÞÏê½Å8’ËoAgs÷nñ.æß©ZîrC ä×9™”óy󳘧gÕ7€™ïm² Šß,î-5ìëó• ó57íó7¿ø’ËÕ£³Å»d/‹ .ÖÂ4}½¬ÿ§Rh ˆÊ…òè´Áû½êd¿ªv{I^Ó.¿ä¤ò\ZOd½[ÛÙ‹EÿõŒºðàúG± Ró‡û áuN þ¿@ÿø%5‡:Œ«ìGѯe?N ¿Yî„oîoÆÓ¬üç¼ÖÌ45ç¤Wg©éýÌsÒ¤ñ¶Ýë¾yRôï[Nd¿‚óõ 8ß@Eü{ZÛT’ßRL:Ü»©þª4KÕ¸ªÿãqm³i{ßåÁ½úÒi¼tÀÕ$È‚-(_ß…Æp/ÿý³FáÌW’`ôc¹Û·] ó `òÏ‹ºP—Â^9¿/ˆ&˜½b2å¾­P¹à:Œ«ì‡øþ|mÑåÏWÔ¼åïj„•&siÊ5k­¬sÒ~o¡M­À{›“‚R’üÍ%XïÏ`¸a>fçÇ_¹²TÿveÞïÝ@XÌóúƸҎXh¹ó—Éw´”«.ã¥ÉIÉXîÎ/?m¤&ÄçVh©ñd¾´º”ÍËHc–¶Pš6))H/k—|r Ê«cPå_yÐáeîƒR¿¯j× 6·Ó„é÷O >^Y)]v—ù<¿_³Ví§K¬öcÙ_ý­0}ëÎð’’é‘árA-Ï Ðþùê«e_ò2¶q,ûüóS¼¨û˜Š~T'Þ~[g©T³,|Ú—÷ïR °¼ÿ´ ¶9ia™eÈbg+Ï_úÛª`Gö·W©)ûþ¬æ‡ä^~*"áË`‚ðô˜Öû½m¾Ù“•̃ºO‘[ËXŸ¿P=]ó ³—MH‚¬¸¹ýeÝÁ]þfTÌ3Û½íDèpÇ+‰îgU‡ÿü"Î_ù¾k|Æñ2ÉI° ’èf——0 ¯[µsaÎÝÀüâ"ýíñœgr}•>«éy;ôi¦Ï‹>'ǵʪW0Óëúf«¾dížÑNÒ9¿ÌûH¬ž’ŽcvA~”>'ºŽzÝ»Š™!rò®:QSZešKŒq¼Œ2€@Ü•߬ÝAŽx˜p S˜?ù ³èâëìøãQs>)Ið¾b±{Ȉº&8hÉ}ÌÒjðÈ’àŸb&_þýíL+w 61w¿Áü*Ë}| êûO`Æ2’ @ "’ @ "’ @ "’ @ "’ @ì ÿÁVŸWê“ÓÅIEND®B`‚jbosscache-core-2.2.2.GA/src/main/docbook/images/Configuration.png0000644000175000017500000001224410660354341024715 0ustar twernertwerner‰PNG  IHDR€J4y;kIDATxÚíÝïky~pÿm–d ,Ç»MiUÒ€Ìfq1]jνkM[¥÷)aé±É]öŒÝ:´·l!ôŸ¸ÇÂvó(,Ü£>õƒ3CZwººK[}53Kš×‹aI¼²dÏûó·F¿2qänÂ. PÀ € PÀ € 0 €)šÉBº¼>'¬PPÀ{Ÿ÷y¡¶K<ñ“ p~Ü®^ pÞgÀ 0(` (`°0äSÀ$趉÷eÞï®3Íõ+`PÀ0\|±Õ4Ü€J7óëWÀ €aˆ 8¦Þ®žÔ^=Óíø×ø _ºÝ˜[ ¿þñvŸžž¶Z­jµýujjjyyùÅ‹æ0t£œ¨õ,à˜²Ü;>Úœòú/ð;wJ¥ÒÕ舾xóæÍG™7PÀð»ö-T‡œõ&.àH¹\¾Ô¾ÑÙp£ÑèXÌ@ŒÕÕU̘Ÿûí øÒ×3,àéééJ¥2;;[¯×———£ê*9úÊâââöööëׯMôqQÀx:q_W×^K·÷çÏŸ7›Í诵Zmmmíàààìì̘†¼ ¸ÛÛº½Ê)°ö:¾$*þ•Y=o7äEX= PÀ0,<¸wîæð>¥ÄoC0Œ[§ÿp X£€Çó£( qvvöå—_6›Íö{oææævww0 €a ¾ÿþûõõõZ­Öñ'¢2þôÓOð¹¢Ìମ®ùmòSSS—¾233sëÖ­öŸ ^Àú„PÀ ˜˜9>>~üøñââbù7~ðñÇß»w¯Z­F­üÙgŸ¼€÷ 'à«€)n_txx¸¾¾>77íŠÛ·oïîî¶?t°Œ¾¦=£€0(`°VÀી͇VÀ€F+` ° Ø|(à‘.àÓÓÓV«U­VÛoÓZ^^~ñâ…yŒVÀ,à;wî”J¥ŽŸOróæÍG™ PÀ(`œqGÚŸÊyQt6Üh4:3é­®®Zn° Ø|PôÞØØ˜žž®T*³³³õz}yy9ªÞ¨’£¯,..noo¿~ýÚlXƒ(`,~<a=þ¼ÙlF­Õjkkkí'ÁDcñ+`ÿ’5ˆp°ùPÀ kŒÅ¯€°5ˆp°ùPÀ kP¸ ‹_+`ká*`ó¡€0v»p°ù@kŒù‚=S@r·QÀ˜"ZXXˆY__·+¬A„«€Í99==-•JåryjjÊ'OYƒW›ROyØ¿ªÛjµÚW*•ÝÝ]ûÍD¸ Ø|ª} xzzº]À³³³Fî³®6¤:÷ ,àw¯„ª×ër±®6dSÃágÀKKKµZÍ~³®6äQÀ+++ín4kkkö›5ˆp°ù ><<Œâ¨V«årùààÀ~³®6äQÀ‘R©þV*oC²®6äWÀóóóQ/..ÚiÖ ÂUÀæƒülmmE‰looÛÖ ÂUÀæƒüG‰ÙÖ ÂUÀæ‰ q0¿D8ÂUÀæ‰ q0¿D8ÂUÀæ‰ qá*`,~‰ q„«€ÍAâÂUÀæ‰ q0æC"v‚Ä®6H‰£€±ø%‚Ä®6H‰£€±ø%‚Ä®6H‰ WcñK‰#\l>®6¿D8 óD$Žp°ù@"HŒÅ_ «?°¬A„«€ÍBAÜ(`,~¡ n„«€ÍBAÜ(`,~¡ n„«€ÍBAÜÂUÀXüBAÜW›„‚¸…«€ÍBAÜ(`ÌB7ÂUÀæ¡ n0¿P7ÂUÀæ¡ n0¿P7ÂUÀæ¡ ná*`,~¡ n„«€ÍBAÜÂUÀæÃâ âFc>Џ®6q£€±ø…‚¸®6q£€±ø…‚¸®6q£€±ø…‚¸®6q Wcñ q£€1EÜW›„‚¸QÀXüBAÜW›„‚¸QÀ¼¿C¯çÅ,~¡ nä«€é11Srñè`ñ qÓ×½+LÂÅßþºÅ/ÄM‚p0©î}[üBAܤXcñ q£€°ÅOŠPvvvNNN¢P¢ÿ>{öÌÞ7#ZÀ#®VÀBùÿÿûðáý½½(”ýýýÍÍM{OÜŒhD¸ 8ûá¸ô:½wîøE· Û6ö,YiÚFzTä“¶ö,]ÉJÓ¨È]Û°–¦M+`;ÑÚ³t%+M£"wlSÀ’•¦M¯€'ÞxÛ/s Énåê5÷ûíï.œìv/ýä)¿=ä RÀ!»%~IvØÄÉögøå'•þzŒJ†Ý‘í*Ú˜ú+àLCàÁ4ñÈa¥]û]¨Îó*Ùpâý9ülT²íŽÌΘRp·_¦ã}„‰Nbî•tûÞž7Ñí{¯ÞÖÕkNs£!wµ®^Càpé& [ÀWwT·¬%;Bw§âiÌ O-þÛãÏoz~ŨdÒá{&æ’ fãcJõt¿{¶ç1tpWxñé¯?äáî ¯¶à³ó'É´€wdèYV³aT2,à4{fäVôÄ0ìĘû5O“5z·;;ÓãqsaÉŽÊp‚(CNFZK|+FåºNÞÒφ½Ã¾ËBV²VÀ’’3àôQ†¿î$Íyv·[1*!¹ç_ÀÏà´€»=Ó3臠³-àœcñ"¬žOüKv´^„5¸EšyçsoÏCÐzzcJû6¤C¾¿r@/ üÆžAö|‰GšYñ"¬ð™éd‡¤€ûÝŸÇОÏX%¸Ýð1*}ÝîùBª¾î3õ5CÓ5GÊ7cñÛ·ãàòA’•æØo#=*ÅÉ=ML×VÀiÞ ]„Žø–¬4 rpÑQ)Tî‰còQ”>…ÎÒ•¬4ŠÜ}´M+`iÚ°¶­=KW²Ò4*rWÀ6,YiÚ°¶Y{–®d¥iT䮀…ê-Yi¹+`›Ã´¥+Yi*`1)`kÏ![²Ò4*rWÀ6‡iKWÛ°˜°µgéJVšFEîÃZÀ §ñ.`ÉJ“‘é„Ä4q^l ÑZ__?ó zÑ“£Bðééi©T*—ËSSSggg¦¡à̃èENý[Mý^­V«ýA¥RÙÝÝ5 c;åæAô¢Gç³äWÝôôt{ÕÍÎÎ6 Ó0®‡`ó zÑ£€sºÃ¸êÞ=I^¯×ÇûeM?2¢= 8×µ~·wii©V«™†ñ>›Ñ‹<,«nee¥½êÆÚÚši(øQØ<ˆ^ô(àœVÝááa´äªÕj¹\>880 ? ›Ñ‹œÓª‹”J¥è>o¥RñÞGaó zÑ£€ó[uóóóÑÂ[\\4 ŽÂæAô¢Gçgkkkrrr{{Û(`D/zp~Ž£Uwttd0¢= 8WÞö‡y@ô(`«ó€èQÀVæу¶ê0ˆlÕa=(`«ó€èQÀVæу¶ê0ˆlÕa=(`«ó€èQÀVæÑ£€­:Ì¢lÕa= تÃ< zPÀVæÑ£€­:Ì¢lÕa= تÃ< zPÀVæÑ£€­:Ì¢G[u˜D تÃ< z°U‡y@ô €­:Ì¢G[u˜D تÃ< z°U‡y@ô €­:Ì¢G[u˜D¶ê0ˆ°U‡y@ô(`«ó€èA[u˜D¶ê0ˆ°U‡y@ô(`«ó€èA[u˜D¶ê0ˆlÕa=(`«ó€èQÀVæу¶ê0ˆ<\¿ÕÄ„UGø<\¼€y=(àäKN>¿gDo§¡€ÓÞáUÀô5NƒD/zp–kO2—ŽÔæAô €0æÑ£€ÇqÕíì윜œD«.úï³gÏLCÁÂæAô¢Gç´ê>|¸··­ºýýýÍÍMÓPð£°y½èQÀ9­ºW¯^5›ÍhÕݽ{÷åË—¦¡ Gáw½ôuó zÑ£€ó­·hÕEkïíÛ·¦ó zÑ£€s²¿¿­º½½=£€y½èÉždðFzÔÄ7öÓbŸ›4®­€Ï¿ûÜ6¸m Xˆã=-"6i(`ƒ®€M‹¶9.)`›A7! ØfÒPÀ]›Û—°M۰ͤ)àŒÖÞÄûSìxá˜kèëÊ€ßÒï¯ÙíÛC®al 8äß'ïþW‚Ÿl>Ó^¦åRÄ}íÏ~‡¡ß=yéòÉâHð{¥?.¥™pÇ¥‚p&óš['žòL¾½hgÀ)³ËðÛ3¿WðiISÀƒƒn¤?Oªv\RÀ& ~ú/ÝÛšè$ðþ]ü·tüJüÐñ+=ïÄÿŽ1×s÷sŒ øê®è¸OÂw]ÏéŠŘ³”·8ÞÓr5âUÜ-úÀ_¹g ýžjÇ:ÂJY —p0†·rüãG-ü{ûºÐíñ«À»Ÿ)¿X„‡ “ýáz¿˜ìzÆ{ZºpÊ5žìÚB‚‹^#óÐûýQû½Ç%œÓQ/AÇÜTÀC{sá4;¹çƒ. x¤ 8f‡Ÿ&ö5{!'£ýþ¨ýÞŠã’\Ø xŒ 8äÙ2<ÒgÀ}:²š½ðµ¤ù5ã_Y渤€³;ä)–lv>5 €Ÿ±݇ ‹3-™pàÓ·iî²çy`츤€û{8ð¡¿/Æ PÀ¥§OŸÔo}ðëiu|ä9:÷Ú׃π†œõÕ‡|ð“?ýä›Ý‡¿û×¾ýâÛ¯Ÿüõ_þ¸^¯ïíù× ƒñæÍ›ܸqcrrrffæþýûщ¯ç} PÀŒÆ8^s±˜+ ¼dàráLn.ÍK|뀆 *$“ê«SÓÜbÊòÖ¸ €a íÛ±Ÿ.]ìê)`È%c®³Ûeâ¯!ä /Ý5ð—ÕР€!¨€;> S`;)°¿;>ÔØ|ñmÿ-!íÛïrgPÀÐßp`»ô{‚Ru}•nϬçéo‚µß›0ô(àÀ“ÚðöêyF›y/^¼†ó؈Ŝà&» €ACÂö=xF6ýÙaø“²iN…Ÿ?Nö£zü0¤*àðgd{¶Z&¯ÀŠ9?~VÈù}ÊÕ+°@ýâÓ- †ñä>ÿÇì|PÀ€ PÀ € PÀ € 0“ÿ× v'4IvæIEND®B`‚jbosscache-core-2.2.2.GA/src/main/docbook/images/DelegatingCacheLoader.doc0000644000175000017500000006200010660354341026200 0ustar twernertwernerÐÏࡱá>þÿ -/þÿÿÿ,ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿì¥Áa ð¿¥jbjb„,„, 0îNîN‹ÿÿÿÿÿÿˆ˜˜˜˜PPP†ÎÎÎÎ Ú †Þ#¶òòòòòM M M U#W#W#W#W#W#W#,”$Ræ&^ƒ#PM yÔM M M ƒ#m ˜˜òò‡˜#m m m M ˜<òPòU#m dx˜˜˜˜M U#m m  #Ô|PU#æ ’ìäÁÎ] !#U#®#0Þ#)#,D'm D'U#m P††$ª$††ª CACHE TCP DELEGATING CACHELOADER TCP DELEGATING CACHELOADER CACHE REPLICATION CACHELOADER STORE CACHE TCP CACHE SERVER TCP TCP  ;<WX^_klxy€†‡˜™ž¢¥íéÛéÛéÛéÛéËéÛéÛéÛéÛéÛéÛéhï<»hï<»56CJOJQJhï<»hï<»5CJOJQJhï<»#jhï<»UaJmHnHsHtH /;<KWX^_klxy€†‡˜™ž¢£¤¥ýýýõõýõõýýýõýõýõýýýõýýýýýýý$a$gdï<»¤þþ.:pï<»°|. °ÈA!°"°# $ %°°Ä°Ä Ä¥<@ñÿ< NormalCJaJmH sH tH jj b}È Heading 1#$ & Fdh¤ð¤<@&gdÙ@L5CJKH \^J_HaJ DA@òÿ¡D Default Paragraph FontRióÿ³R  Table Normalö4Ö l4Öaö (kôÿÁ(No List#?FS`gn€…Š¥ÿÿÿÿÿÿÿÿ/ÿÿÿÿ'ÿÿÿÿ&ÿÿÿÿ)ÿÿÿÿBÿÿÿÿ+ÿÿÿÿAÿÿÿÿCÿÿÿÿFÿÿÿÿGÿÿÿÿ#?FS`gn€…Š ÿÿ¥ ÿÿÿÿ ÿÿ+£¥ /;<KWX^_klxy€†‡˜™ž¢¦˜0€€Ø0«˜0€€°Ê€˜0€€Ø@­˜0€€°Ê€˜0€€°Ê€˜0€€Ø@­€˜0€€°Ê€˜0€€°Ê€˜0€€Ø@­˜0€€°Ê€˜0€€Ø@­˜0€€°Ê€˜0€€Ø@­˜0€€Ø@­€˜0€€Ø@­€˜@0€€°Ê€š0€€Ø@­€˜0€€Ø@­€˜0€€Ø@­€˜0€€°Ê€˜0€€Ø@­˜@0€€°Ê€š0€€Ø@­€˜@0€€°Ê€š0€€Ø@­€¦š€€ÿ¿(|æ ¯¥¥¤ð8ðIEI@ñÿÿÿ€€€÷ð>ð<H ñðÌð( ð ððN¢ ðG 3 ð€ ŠGÿ ðð ð ðN¢ ðF 3 ð€ ŠFÿ ðð ð ðH¢ ð) # ð €ÿ ðð ððN ð S ðÿÿ™¿ËjJÿˆðð ðH2 ð C ðÿ™¿ÿˆðððH2 ð C ðÿ™¿ÿˆðððH2 ð C ðÿ™¿ÿˆðððH2 ð C ðÿ™¿ÿˆðððH2 ð C ðÿ™¿ÿˆðððH2 ð C ðÿ™¿ÿˆðððH2 ð C ðÿ™¿ÿˆðððNB ð  S ðD¿ÿˆð ððNB ð @ S ðD¿ÿˆð ððNB ð  S ðD¿ÿˆð ððNB ð@ S ðD¿ÿˆð ððNB ð S ðD¿ÿˆð ððNB ð S ðD¿ÿˆðððT ð C ð€‚ÿ ˆðð ððjðT ðU<5+˜ ð1# ð ÿˆðððZ ð S ðÿÿ™¿ËjJÿˆðU<5+Èð ðT2 ð C ðÿ™¿ÿˆð© ð"Xð ðT2 ð C ðÿ™¿ÿˆð%À( ð ðT2 ð C ðÿ™¿ÿˆð© À"( ð ðT2 ð C ðÿ™¿ÿˆð-$À•%( ð ðT2 ð C ðÿ™¿ÿˆð© D "¬ ð ðT2 ð C ðÿ™¿ÿˆð-$D •%¬ ð ðT2 ð C ðÿ™¿ÿˆð±'D )¬ ð ðZB ð  S ðD¿ÿˆð]!X]!Àð ðZB ð!B S ðD¿ÿˆðÙX]!Àð ðZB ð" S ðD¿ÿˆð]!Xá$Àð ðZB ð#B S ðD¿ÿˆð]!( á$D ð ðZB ð$ S ðD¿ÿˆðá$( e(D ð ðZB ð% S ðD¿ÿˆðá$( á$D ð ð` ð& C ð€‚ÿ ˆðU¬ •%Èð ððl ð' c ð$€™Ì¿ËjJÿ ˆð%Èe(˜ð ðð`B ð( ƒ ð0D¿Ë8cÎÐÑÿðððZB ð-@ s ð*D¿ÎÐÑÿðððZB ð. s ð*D¿ÎÐÑÿðððZ ð/ S ð€™Ì¿ËjJÿ ðð ðð²ð@ ð­ „ / ðHðððlB ð+ c ð$€Š+™Ìÿ¿ËjJÿðaè* /ð ððZ ð3 S ðÿÿ™¿ËjJÿˆð­  ,#ð ðT2 ð4 C ðÿ™¿ÿˆðTi¼ð ðT2 ð5 C ðÿ™¿ÿˆð}$åŒð ðT2 ð6 C ðÿ™¿ÿˆð$iŒð ðT2 ð7 C ðÿ™¿ÿˆð…$íŒð ðT2 ð8 C ðÿ™¿ÿˆð¨i!ð ðT2 ð9 C ðÿ™¿ÿˆð…¨í!ð ðT2 ð: C ðÿ™¿ÿˆð ¨q!ð ðZB ð; S ðD¿ÿˆðµ¼µ$ð ðZB ð<B S ðD¿ÿˆð1¼µ$ð ðZB ð= S ðD¿ÿˆðµ¼9$ð ðZB ð>B S ðD¿ÿˆðµŒ9¨ð ðZB ð? S ðD¿ÿˆð9Œ½¨ð ðZB ð@ S ðD¿ÿˆð9Œ9¨ð ðf ðA S ð€ŠA‚ÿ ˆð­ !í,#ð ððr ðB s ð*€ŠB™Ì¿ËjJÿ ˆð},#½H%ð ððl ðC c ð$€ ŠCÌ™ÿ¿ËjJÿ ð}„½ ð ð ð`B ðE c ð$D¿ÐÑÿðH%è*ððB ðS ð¿Ëÿ ?ð ¥H¥ä…€)tGi ´tFñ‘ ´t-ø ½ät.pø ät1Mœþÿÿ-$ø t/ýÿÿ( Áø t±úÿÿ ñ( t=ˆ=¤t=ˆÁ¤t¹ˆ=¤t ¹¸= t 5þÿÿ¸¹ t ¹¸¹ t ¤u t‰¤ñ t¤m t‰ ñˆt mˆtýÿÿ éþÿÿˆtPÿÿÿm¸t±úÿÿœþÿÿ‘ ( t)‘ ˆM( t(u ˆµˆt¦¦Ÿ4v5°2Bÿÿÿÿÿÿÿÿ"„Є˜þÆÐ^„Ð`„˜þ56CJOJQJo(†*‡hˆH.0€ „ „˜þÆ ^„ `„˜þ‡hˆH.‚ „p„LÿÆp^„p`„Lÿ‡hˆH.€ „@ „˜þÆ@ ^„@ `„˜þ‡hˆH.€ „„˜þÆ^„`„˜þ‡hˆH.‚ „à„LÿÆà^„à`„Lÿ‡hˆH.€ „°„˜þư^„°`„˜þ‡hˆH.€ „€„˜þÆ€^„€`„˜þ‡hˆH.‚ „P„LÿÆP^„P`„Lÿ‡hˆH.Ÿ4v5ÿÿÿÿÿÿÿÿt¬        ÿ@à<ìÿ%¼¥ @ÿÿUnknownÿÿÿÿÿÿÿÿÿÿÿÿGTimes New Roman5€Symbol3 Arial ñˆðÐhó±F%ó±F!ð¥À´´€~4µððÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿjV‰ÿÿ Manik Surtani Manik Surtani þÿ à…ŸòùOh«‘+'³Ù0˜¤°ÈÔä ü $ 0 <HPX`h'Manik SurtaniNormalManik Surtani5Microsoft Word 11.2@êVú@Žþ7jDÇ@¾VkDÇG” þÿÿÿPICT Š€Zÿ ÿþHH€Z €€ÿÿšÿ€´€ZHHb±¨Š €Z€Z§ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿÿÿàóÿàûÿ/ÿÿöóþðóóÿõóðóûÿ3ÿÿöó~`ðóóÿõóðóûÿ/ÿÿöóþðóóÿõóðóûÿ-ÿÿôóðóóÿôóðóûÿ-ÿÿöóüòóóÿöóüòóûÿJÿÿùóþóóóóôóóÿùóþóóóóþõóûÿGÿÿüóþþóþýóþ÷óóÿüóþýóýóþ÷óûÿYÿÿüóýó~`ýó~`÷óóÿüó~`ýóýó~`÷óûÿCÿÿüóýóþýóþùóîýóþýóýóþ÷óûÿ'ÿÿîóúóêðó÷óûÿ-ÿÿïóýøóóÿïóýøóûÿEÿÿñóóóóùóóÿðóóóóúóûÿIÿÿòóþóþóûóóÿòóþóþóûóûÿEÿÿöóýþóþýóþþóóÿõóþþóþýóþþóûÿ[ÿÿöó~`ýó~`ýó~`þóóÿõóýó~`ýó~`þóûÿGÿÿöóþýóþýóþþóóÿõóýóþýóþþóûÿ5ÿÿZÐV°ZÐRJOçóóÿZÐV°ZÐRJOçóûÿ!ÿÿâóóÿâóûÿ!ÿÿâóóÿâóûÿÿÿàóÿàûÿ[ùÿO O F‡BgBIO B*> F†> BhBhF‡> BhýO çÿþO F‡> O B*> F†> BhBhF‡> BhýO õÿ!ùÿîO çÿîO õÿYùÿþO BgBhBgBI> F†BIBhO B*> B*ýO çÿþO BgBhBgBIO > F†BIBhB*> B*ýO õÿ!ùÿîO çÿîO õÿùÿìçÿìõÿðÿþÕÿþìÿïÿýÚÿüëÿìÿýßÿýèÿéÿþäÿýåÿçÿýéÿýãÿäÿþîÿýàÿâÿýóÿþÝÿßÿþøÿýÛÿÝÿüÿÿþØÿãÿëÝÿ7ãÿffR7NJNNNfJAòNAòFNAòFþfÝÿãÿífÝÿãÿífÝÿèÿàãÿèÿâóãÿèÿöóþðóãÿèÿöó~`ðóãÿèÿöóþðóãÿèÿõóþñóãÿ%èÿøóþóóþôóãÿ)èÿüóýóóþýóþ÷óãÿ1èÿüóýó~`ýó~`÷óãÿ)èÿüóýóþýóþ÷óãÿèÿîóöóãÿèÿïóýøóãÿ'èÿñóóóóùóãÿ)èÿòóþóþóûóãÿ'èÿöóýþóþýóþþóãÿ3èÿöó~`ýó~`ýó~`þóãÿ'èÿöóþýóþýóþþóãÿèÿZÐV°ZÐRJOçóãÿèÿâóãÿèÿâóãÿèÿàãÿãÿëÝÿãÿíO ÝÿãÿíO ÝÿãÿëÝÿØÿÓÿØÿÓÿØÿÓÿØÿÓÿØÿÓÿØÿÓÿØÿÓÿØÿÓÿØÿÓÿØÿÓÿØÿÓÿåÿäâÿçÿþæO?ãÿçÿáãÿ%çÿöO?BvF—BTB3>ôO?ãÿçÿäO?ãÿçÿäO?ãÿçÿäO?ãÿçÿûìO?üãÿãÿèàÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ ©ÿ§ÿþÿ ÕÍÕœ.“—+,ù®0Ô `hpx€ ˆ˜  ¨ µ'  Titleþÿÿÿ þÿÿÿ !"#þÿÿÿ%&'()*+þÿÿÿýÿÿÿ.þÿÿÿþÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿRoot Entryÿÿÿÿÿÿÿÿ ÀF¿lkDÇ0€1TableÿÿÿÿÿÿÿÿT'WordDocumentÿÿÿÿÿÿÿÿSummaryInformation(ÿÿÿÿDocumentSummaryInformation8ÿÿÿÿÿÿÿÿÿÿÿÿ$CompObjÿÿÿÿÿÿÿÿÿÿÿÿXÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿ ÀFMicrosoft Word DocumentþÿÿÿNB6WWord.Document.8jbosscache-core-2.2.2.GA/src/main/docbook/tutorial/0000755000175000017500000000000011376173772022007 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/main/docbook/tutorial/en/0000755000175000017500000000000011376173772022411 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/main/docbook/tutorial/en/master.xml0000644000175000017500000002223411031017515024405 0ustar twernertwerner

JBoss Cache Core Edition Tutorial Release 2.2.0 Poblano July 2008 Manik Surtani manik@jboss.org 2005 2006 2007 2008 JBoss, a division of Red Hat Inc.
Introduction JBoss Cache is an in-memory replicated, transactional, and fine-grained cache. This tutorial focuses on the core Cache API. Please refer to the accompanying tutorial for the Pojo Cache API if it is the Pojo Cache API you are interested in. For details of configuration, usage and APIs, please refer to the user manuals .
What You Will Learn Cache creation and modification Replication of state Transactions
Configuration First download the JBoss Cache 2.x distribution from the download page . You probably want the JBossCache-core-2.X.Y.zip distribution. Unzip it, and you will get a directory containing the distribution, such as JBossCache-core-2.X.Y . For the sake of this tutorial, I will refer to this as JBossCache . The configuration files are located in the JBossCache/etc directory. You can modify the behavior of the cache by editing the various configuration files. log4j.xml . Logging output. You can enable logging, specify log levels or change the name and path to the log file. META-INF/replSync-service.xml . Cache configuration file used for this tutorial.
Script The only script needed for this tutorial is the JBossCache/build.xml ant script. You also need to have ant installed for running the demo.
Running The Demo GUI The demo is run by calling the ant script with the run.demo target. E.g., ant run.demo This will cause a GUI window to appear, giving you a tree view of the cache in the top pane and a BeanShell view of the JVM in the lower pane. The BeanShell view is preset with the following variables: cache - a reference to the Cache interface, used by the GUI instance. root - a reference to the root Node instance for the above cache. transactionManager - a reference to the registered transaction manager. The references made available to the BeanShell window point to the same cache instance used by the tree view in the GUI above. To run the demo as a replicated demo, it is useful to start another command line window and run the ant script again as you did above. Now you will have two cache instances running in two separate GUIs, replicating state to each other.
Tutorials Note that it is recommended that you shut down and restart the demo GUI for each of the following tutorials, to ensure clean caches every time.
Caches and Nodes For this tutorial, start a single instance of the demo GUI. In this tutorial, we will: Create nodes under the root node. Remove nodes under the root node, both individually and recursively. Add and remove data from nodes. 1. Set up the Fqns you need. In the BeanShell pane, create 3 Fqn variables: 2. Create child nodes under the root node. 3. Query the nodes. 4. Put some data in the nodes. By selecting the nodes in the tree view, you should see the contents of each node. 5. Query some of the data. 6. Remove some data in the nodes. 7. Delete nodes In addition to the above, you should refer to the Cache and Node API docs and try out the APIs in the BeanShell script.
Replication For this tutorial, start two instances instance of the demo GUI. Repeat the exercises in the previous tutorial, only alternating between the two GUI windows when creating/removing nodes or adding/removing data. This demonstrates how the two cache instances in the two GUIs are kept in sync.
Transactions For this tutorial, start two instances instance of the demo GUI. Repeat the exercises in the previous tutorial, only starting transactions before creating/removing nodes or adding/removing data. This will depict how replication only occurs on transaction boundaries. Try rolling back a few transactions as well, to see how nothing gets replicated in these cases. Below is the sample code for managing transactions:
jbosscache-core-2.2.2.GA/src/main/docbook/css/0000755000175000017500000000000011376174000020715 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/main/docbook/css/dummy.css0000644000175000017500000000003310764261746022574 0ustar twernertwerner/* This is a placeholder */jbosscache-core-2.2.2.GA/src/main/docbook/faq/0000755000175000017500000000000011376174000020674 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/main/docbook/faq/en/0000755000175000017500000000000011376174000021276 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/main/docbook/faq/en/master.xml0000644000175000017500000020016111031017515023306 0ustar twernertwerner Frequently Asked Questions about JBoss Cache Release 2.2.0 Poblano July 2008 Manik Surtani manik@jboss.org Ben Wang ben.wang@jboss.com Bela Ban bela@jboss.com Scott Marlow smarlow@novell.com Galder Zamarreño galder.zamarreno@jboss.com This is a compilation of the most frequently asked questions about JBoss Cache. Please report any bugs, inconsistencies, or omissions you find in this FAQ on the JBoss Cache User Form . This FAQ is divided into specific sections, all pertaining to the core JBoss Cache library. PojoCache has a separate FAQ document pertaining to PojoCache specifics. 2005 2006 2007 2008 JBoss, a division of Red Hat Inc. General Information What is JBoss Cache? JBoss Cache is a replicated and transactional cache. It is replicated since multiple JBoss Cache instances can be distributed (either within the same JVM or across several JVMs whether they reside on the same machine or on different machines on a network) and data is replicated across the whole group. It is transactional because a user can configure a JTA compliant transaction manager and make any cache interaction transactional. Note that the cache can also be run without any replication; this is the local mode. JBoss Cache comes in two flavours: Core and Pojo versions. The core library (using the org.jboss.cache.Cache interface ) is the underlying library that organises data in a tree-like structure and handles all locking, passivation, eviction and replication characteristics of data in the cache. The pojo library (using the org.jboss.cache.pojo.PojoCache interface) is built atop the core library and allows introspection of objects in the cache providing transparent coherence by using JBoss AOP. Note that the Pojo version of JBoss Cache (referred to as PojoCache) comes with a separate set of documentation (user guide, FAQ, etc.) available on the JBoss Cache documentation site . JBoss Cache is made available in one of four different packages: jboss-cache-core contains the core Cache library for users who do not wish to use the additional functionality offered by PojoCache. jboss-cache-pojo contains the core Cache library as well as the PojoCache extensions and dependencies. jboss-cache-all contains all of the above, including unit tests and source code. jboss-cache-core-JDK140 contains a JDK 1.4 compatible version of the core Cache library. Note that PojoCache is only available for JDK 5.0. Who are the JBoss Cache developers? JBoss Cache has an active community of developers and contributors. The project was founded by Bela Ban and is currently led by Manik Surtani. Jason Greene is the lead for the PojoCache subsystem, and other contributors both past and present include Ben Wang, Harald Gliebe, Brian Stansberry, Vladimir Blagojevic, Mircea Markus, Jimmy Wilson, Galder Zamarreño and Elias Ross. What is the license for JBoss Cache? JBoss Cache is licensed under LGPL . Where can I download JBoss Cache? The JBoss Cache product download page has prebuilt binaries as well as source distributions. You can also grab snapshots from the JBoss CVS repository (see this wiki page ) - the module name is JBossCache How do I build JBoss Cache from CVS sources? To build, do sh build.sh jar . This will produce jboss-cache.jar and pojocache.jar in the dist/lib directory. Note that you will need to use JDK 5 to build the distribution. Which versions of the JDK are supported by JBoss Cache? JBoss Cache is baselined on Java 5.0 and this is the platform on which JBoss Cache is most thoroughly tested. If, for whatever reason you have to use Java 1.4, you could build a retroweaved version of the core cache library that is Java 1.4 compatible, using the simple instructions on this wiki page on building and running JBoss Cache on Java 1.4. . Note that Red Hat Inc. does not offer commercial support for retroweaved binaries at this stage. Java 6 should work as well, and we haven't heard of any specific problems of JBoss Cache run under Java 6. How do I know the version of JBoss Cache that I am using? java -jar jbosscache.jar will spit out version details. Can I run JBoss Cache outside of JBoss Application Server? Of course! Even though JBoss Cache comes integrated with JBoss Application Server as an MBean service, it can also be run standalone, in any Java EE server such as BEA WebLogic, IBM Websphere or Tomcat. It can also run in a standalone Java process, completely outside of an application server. See the user guide for more details. How can I migrate my application and configuration from using JBoss Cache 1.x to 2.x? Look at this wiki page for help. Where can I report bugs or problems? Please report any bugs or problems to JBoss Cache User Forum . JBoss Cache - Core How do I deploy JBoss Cache as a MBean service? To deploy JBoss Cache as an MBean inside JBoss, you can copy the configuration xml file over to the deploy directory (from all configuration whereby the necessary jars are present). Under the standalone package etc/META-INF directory , there are example configuration files for different cache modes that can be used to deploy JBoss Cache as well. How do I know if my JBoss Cache MBean has been deployed? To verify that your JBoss Cache MBean is deployed correctly, you can first check the log output under the command console. Next you can verify it from JBoss JMX console. Look for jboss.cache domain. How do I access the JBoss Cache MBean? Accessing the JBoss Cache MBean is just like accessing any JBoss MBean. Here is a code snippet: Can I run multiple JBoss Cache instances on the same VM? Yes. There are some scenarios where you may want to run multiple instances of JBoss Cache. For example, you want to run multiple local cache instances with each instance having its own configuration (e.g., different cache policy). In this case, you will need multiple xml configuration files. Can JBoss Cache run as a second level cache inside Hibernate? Yes. Since Hibernate 3.0 release, you can configure it to use JBoss Cache as a second level cache. For details, see Hibernate documentation, and also see http://wiki.jboss.org/wiki/Wiki.jsp?page=JBossCacheHibernate What about using Pojo Cache as a Hibernate cache? It is not necessary to use PojoCache for second level cache inside Hibernate because Hibernate manages fine-grained fields in Java objects. Using PojoCache won't provide any advantage. How can I configure JBoss Cache? You can configure the JBoss Cache through a configuration xml file or programmatically using a org.jboss.cache.config.Configuration object, passed in to the org.jboss.cache.CacheFactory instance. In the configuration xml file, there are tags such as class , MBean , etc. What are these? These are tags for deploying JBoss Cache as a JBoss MBean service. For consistency, we have kept them in the standalone package as well, specifically, the MBean tag. If you run in standalone mode, JBoss Cache will ignore these elements. What is the difference between the different cache modes? JBossCache has five different cache modes, i.e., LOCAL , REPL_SYNC , REPL_ASYNC , INVALIDATION_SYNC and INVALIDATION_ASYNC . If you want to run JBoss Cache as a single instance, then you should set the cache mode to LOCAL so that it won't attempt to replicate anything. If you want to have synchronous replication among different JBoss Cache instances, you set it to REPL_SYNC . For asynchronous replication, use AYSNC_REPL . If you do not wish to replicate cached data but simply inform other caches in a cluster that data under specific addresses are now stale and should be evicted from memory, use INVALIDATION_SYNC or INVALIDTAION_ASYNC . Synchronous and asynchronous behavior applies to invalidation as well as replication. Note that ASYNC_REPL and INVALIDATION_ASYNC are non-blocking. This can be useful when you want to have another JBoss Cache serving as a mirror or backup and you don't want to wait for confirmation that this mirror has received your messages. How does JBoss Cache's replication mechanism work? JBoss Cache leverages JGroups as a replication layer. A user can configure the cluster of JBoss Cache instances by sharing the same cluster name ( cluster name ). There is also an option of whether to populate the cache data upon starting a new instance in the ClusterConfig attribute. Note that once all instances join the same replication group, every replication change is propagated to all participating members. There is no mechanism for sub-partitioning where some replication can be done within only a subset of members, unless you use the Buddy Replication features. See the user guide for more details on this. I run a 2 node cluster. If the network dies, do the caches continue to run? Yes, both will continue to run, but depending on your replication mode, all transactions or operations may not complete. If REPL_SYNC is used, operations will fail while if REPL_ASYNC is used they will succeed. Even if they succeed though, caches will be out of sync. Can I plug in library X instead of JGroups to handle remote calls and group communications? At this stage the answer is no. We do have an abstraction layer between the communication suite and JBoss Cache in the pipelines, and this may appear as a feature at some stage in the future. Does the cache need to replicate to every other instance in the cluster? Isn't this slow if the cluster is large? Replication need not occur to every node in the cluster. This feature - called Buddy Replication - allows each node to pick one or more 'buddies' in the cluster and only replicate to its buddies. This allows a cluster to scale very easily with no extra impact on memory or network traffic with each node added. See the User Guide for more information on Buddy Replication, and how it can be used to achieve very high scalability. If I have the need for different configuration properties (e.g., CacheMode and IsolationLevel ), do I simply need to create multiple org.jboss.cache.Cache instances with the appropriate configuration? Yes. All the above mentioned properties are per cache instance. Therefore you will need separate org.jboss.cache.Cache instances. Isn't this expensive from a networking standpoint, i.e., needing to create sockets for each org.jboss.cache.Cache instance? Yes, it can be. For such cases it is recommended that you configure your cache using the JGroups Multiplexer, which allows several caches to share a single JGroups channel. Please see the User Guide for details on how to configure the JGroups Multiplexer. Does the ClusterName configuration element have any relation to the JBoss AS cluster PartitionName ? Yes. They are both JGroups group names. Besides the notion of a channel in JGroups, it also can partition the channel into different group names. When using multiple JGroups based components [cluster-service.xml, cache (multiple instances)], what is the correct/valid way to configure those components to make sure my multicast addresses don't conflict? There are two parameters to consider: multicast address (plus port) and the group name. At minimum, you will have to run components using a different group name. But whether to run them on the same channel depends upon whether the communication performance is critical for you or not. If it is, then it'd be best to run them on different channels. Does JBoss Cache support cache persistence storage? Yes. JBoss Cache has a cache loader interface that supports cache persistence. See below for more FAQs on cache loaders. Does JBoss Cache support cache passivation/ overflow to a data store? Yes. JBoss Cache uses the cache loader to support cache passivation/ overflow. See documentation on how to configure and use this feature. Is JBoss Cache thread safe? Yes, it is thread safe. Does JBoss Cache support XA (2PC) transactions now? No, although it is also on our to do list. Our internal implementation does use a procedure similar to 2PC to coordinate a transactions among different instances, but JBoss Cache is not an XA resource. Which transaction managers are supported by JBoss Cache? JBoss Cache supports any TransactionManager that is JTA compliant such as JBossTM or JBossTS. JBoss Cache ships with a dummy transaction manager ( org.jboss.cache.transaction.DummyTransactionManager ) for testing purposes only. But note that DummyTransactionManager is not thread safe .i.e., it does not support concurrent transactions. Instead, only one transaction is allowed at a time. How do I set up the cache to be transactional? You either use the default transaction manager that ships with JBoss AS or you have to implement the org.jboss.cache.transaction.TransactionManagerLookup interface, and return an instance of your javax.transaction.TransactionManager implementation. The configuration property TransactionManagerLookupClass defines the class to be used by the cache to fetch a reference to a transaction manager. It is trivial to implement this interface to support other transaction managers. Once this attribute is specified, the cache will look up the transaction context from this transaction manager. How do I control the cache locking level? JBoss Cache lets you control the cache locking level through the transaction isolation level. This is configured through the attribute IsolationLevel . The transaction isolation levels correspond to database isolation levels, namely, NONE , READ_UNCOMMITTED , READ_COMMITTED , REPEATABLE_READ , and SERIALIZABLE . Note that these isolation levels are ignored if optimistic locking is used. For details, please refer to the user manual. How does JBoss Cache lock data for concurrent access? By default JBoss Cache uses pessimistic locking to lock data nodes, based on the isolation level configured. We also offer optimistic locking to allow for greater concurrency at the cost of slight processing overhead and performance. See the documentation for a more detailed discussion on concurrency and locking in JBoss Cache. How do I enable Optimistic Locking in JBoss Cache? Use the XMl attribute NodeLockingScheme . Note that IsolationLevel is ignored if NodeLockingScheme is set to OPTIMISTIC . Also note that NodeLockingScheme defaults to PESSIMISTIC if omitted. How does the write lock apply to an Fqn node, say, "/org/jboss/test"? First of all, JBoss Cache has a notion of root that serves as a starting point for every navigational operation. The default is "/" (since the default separator is "/" for the fqn). The locking then is applied to the node under root, for example "/org" (no locking "/"). Furthermore, let's say when JBoss Cache needs to apply a write lock on node "/org/jboss/test", it will first try to obtain read lock from the parent nodes recursively (in this example, "/org", and "/org/jboss"). Only when it succeeds then it will try to obtain a write lock on "/org/jboss/test". Can I use the cache locking level even without a transaction context? Yes. JBoss Cache controls the individual node locking behavior through the isolation level semantics. This means even if you don't use a transaction, you can specify the lock level via isolation level. You can think of the node locking behavior outside of a transaction as if it is under transaction with auto_commit on. With replication (REPL_SYNC/REPL_ASYNC) or invalidation (INVALIDATION_SYNC/INVALIDATION_ASYNC), how often does the cache broadcast messages over the network? If the updates are under transaction, then the broadcasts happen only when the transaction is about to commit (actually during the prepare stage internally). That is, it will be a batch update. However, if the operations are not under transaction context, then each update will trigger replication. Note that this has performance implication if network transport is heavy (it usually is). How can I do a mass removal? If you do a cache.removeNode(Fqn.fromString("/myroot")), it will recursively remove all the entries under "/myroot". Can I monitor and manage the JBoss Cache? Yes, using a JMX console such as the one shipped with JBoss AS or Java 5's jconsole utility. See the chapter titled Management Information in the JBoss Cache user guide for more details. Can I disable JBoss Cache management attributes? Yes, you can. Set the UseInterceptorMbeans configuration attribute to false (this defaults to true ). See the chapter titled Management Information in the JBoss Cache user guide for more details. What happened to jboss-serialization.jar? As of JBoss Cache 2.0.0, the dependency on JBoss Serialization has been dropped since most of the benefits of JBoss Serialization are available in updated Java 5 VMs. Since JBoss Cache 2.0.0 is baselined on Java 5, there was no need to provide these benefits separately. Does JBoss Cache support partitioning? Not right now. JBoss Cache does not support partitioning that a user can configure to have different set of data residing on different cache instances while still participating as a replication group. Does JBoss Cache handle the concept of application classloading inside, say, a J2EE container? Application-specific classloading is used widely inside a Java EE container. For example, a web application may require a new classloader to scope a specific version of the user library. However, by default JBoss Cache is agnostic to the classloader. In general, this leads to two kinds of problems: Object instance is stored in cache1 and replicated to cache2. As a result, the instance in cache2 is created by the system classloader. The replication may fail if the system classloader on cache2 does not have access to the required class. Even if replication doesn't fail, a user thread in cache2 may not be able to access the object if the user thread is expecting a type defined by the application classloader. Object instance is created by thread 1 and will be accessed by thread 2 (with two different classloaders). JBoss Cache has no notion of the different classloaders involved. As a result, you will have a ClassCastException . This is a standard problem in passing an object from one application space to another; JBoss Cache just adds a level of indirection in passing the object. To solve the first kind of issue JBoss Cache uses a CacheMarshaller . Basically, this allows application code to register a classloader with a portion of the cache tree for use in handling objects replicated to that portion. See the CacheMarshaller section of the user guide for more details. To solve the second kind of issue, the only solution (that we know of) is to cache "serialized" byte code and only de-serialize it during every object get (and this will be expensive!). That is, during a put operation, the object instance will be serialized and therefore can be deserialized safely by a "foreign" classloader. However, the performance penalty of this approach is quite severe so in general another local in-vm version will need to be used as a "near-line" cache. Note also that each time the serialized bytes are deserialized, a new instance of the object is created. To help with this kind of handling, JBoss has a utility class called MarshalledValue that wraps around the serialized object. Here is a code snippet that illustrates how you can create a wrapper around JBoss Cache to handle the classloader issue: Does JBoss Cache currently support pre-event and post-event notification? Yes. A boolean is passed in to each notification callback identifying whether the callback is before or after the event. See the org.jboss.cache.CacheListener interface for details. How do I implement a custom listener to listen to cache events? Either implement org.jboss.cache.CacheListener or extend org.jboss.cache.AbstractCacheListener and override for the events you are interested in. You can then register the listener using the org.jboss.cache.Cache.addCacheListener() API. Can I use UseRegionBasedMarshalling attribute in JBoss Cache in order to get around ClassCastExceptions happening when accessing data in the cache that has just been redeployed? Yes, you can. Originally, cache Marshalling was designed as a workaround for those replicated caches that upon state transfer did not have access to the classloaders defining the objects in the cache. On each deployment, JBoss creates a new classloader per the top level deployment artifact, for example an EAR. You also have to bear in mind that a class in an application server is defined not only by the class name but also its classloader. So, assuming that the cache is not deployed as part of your deployment, you could deploy an application and put instances of classes belonging to this deployment inside the cache. If you did a redeployment and try to do a get operation of the data previously put, this would result on a ClassCastException. This is because even though the class names are the same, the class definitions are not. The current classloader is different to the one when the classes were originally put. By enabling marshalling, you can control the lifecycle of the data in the cache and if on undeployment, you deactivate the region and unregister the classloader that you'd have registered on deployment, you'd evict the data in the cache locally. That means that in the next deployment, the data won't be in the cache, therefore avoiding the problem. Obviously, using marshalling to get around this problem is only recommended when you have some kind of persistence backing where the data survives, for example using CacheLoaders, or when JBoss Cache is used as a second level cache in a persistence framework. To implement this feature, please follow the instructions indicated in the example located in the CacheMarshaller section of the user's guide. It's worth noting that instead of a ServletContextListener , you could add this code into an MBean that contained lifecycle methods, such as start() and stop() . The key would be for this MBean to depend on the target cache, so that it can operate as long as the cache is up and running. Eviction Policies Does JBoss Cache support eviction policies? Yes. JBoss Cache currently supports multiple eviction policies such as LRU, MRU, and FIFO. Users can also plug in their own eviction policy algorithms. See user manual for details. Does JBoss Cache's eviction policy operates in replication mode? Yes and no. :-) The eviction policy only operates in local mode. That is, nodes are only evicted locally. This may cause the cache contents not to be synchronized temporarily. But when a user tries to obtain the cached contents of an evicted node and finds out that is null (e.g., get returns null), it should get it from the other data source and re-populate the data in the cache. During this moment, the node content will be propagated and the cache content will be in sync. However, you still can run eviction policies with cache mode set to either REPL_SYNC or REPL_ASYNC . Depending on your use case, you can set multiple cache instances to have their own eviction policy (which are applied locally) or just have selected instances with eviction policies activated. Also note that, with cache loader option, a locally evicted node can also be persisted to the backend store and a user can retrieve it from the store later on. Does JBoss Cache support Region ? Yes. JBoss Cache has the notion of region where a user can configure the eviction policy parameters (e.g., maxNodes or timeToIdleSeconds ) A region in JBoss Cache denotes a portion of tree hierarchy, e.g., a fully qualified name ( org.jboss.cache.Fqn ). For example, a user can define /org/jboss and /org/foocom as two separate regions. But note that you can configure the region programmatically now, i.e., everything has to be configured through the xml file. What are the EvictionPolicyConfig tag parameters for org.jboss.cache.eviction.LRUPolicy ? They are:
Parameters eventQueueSize A fine-tuning parameter where you can configure the size of the eviction notification event queue. Defaults to 200,000. wakeUpIntervalInSeconds Interval where the clean up thread wakes to process the sitting queue and sweep away the old data. region A area where each eviction policy parameters are specified. Note that it needs a minimum of /_default region. maxNodes Max number of nodes allowed in the eviction queue. 0 means no limit. timeToLiveInSeconds Age (in seconds) for the node to be evicted in the queue. 0 denotes no limit.
I have turned on the eviction policy, why do I still get "out of memory" (OOM) exception? OOM can happen when the speed of cache access exceeds the speed of eviction policy handling timer. Eviction policy handler will wake up every wakeUpIntervalInSeconds seconds to process the eviction event queue. So when the queue size is full, it will create a backlog and cause out-of-memory exceptions to happen unless the eviction timer catches up. To address this problem, in addition to increase the VM heap size, you can also reduce the wakeUpIntervaleInSeconds so the timer thread processes the queue more frequently. The eviction queue size is configurable. Cache Loaders What is a cache loader? A cache loader is the connection of JBoss Cache to a (persistent) data store. The cache loader is called by JBoss Cache to fetch data from a store when that data is not in the cache, and when modifications are made to data in the cache the Cache Loader is called to store those modifications back to the store. In conjunction with eviction policies, JBoss Cache with a cache loader allows a user to maintain a bounded cache for a large backend datastore. Frequently used data is fetched from the datastore into the cache, and the least used data is evicted, in order to provide fast access to frequently accessed data. This is all configured through XML, and the programmer doesn't have to take care of loading and eviction. JBoss Cache currently ships with several cache loader implementations, including: org.jboss.cache.loader.FileCacheLoader : this implementation uses the file system to store and retrieve data. JBoss Cache nodes are mapped to directories, subnodes to subdirectories etc. Attributes of a node are mapped to a data file inside the directory. org.jboss.cache.loader.BdbjeCacheLoader : this implementation is based on the Oracle's Berkeley DB Java Edition database, a fast and efficient transactional database. It uses a single file for the entire store. Note that if you use the Berkeley DB cache loader with JBoss Cache and wish to ship your product, you will have to acquire a commercial license from Oracle . org.jboss.cache.loader.JDBCCacheLoader : this implementation uses the relational database as the persistent storage. And more. See the chapter on cache loaders in the User Guide for more details. Is the FileCacheLoader recommended for production use? No, it is not. The FileCacheLoader has some severe limitations which restrict it's use in a production environment, or if used in such an environment, it should be used with due care and sufficient understanding of these limitations. Due to the way the FileCacheLoader represents a tree structure on disk (directories and files) traversal is inefficient for deep trees. Usage on shared filesystems like NFS, Windows shares, etc. should be avoided as these do not implement proper file locking and can cause data corruption. Usage with an isolation level of NONE can cause corrupt writes as multiple threads attempt to write to the same file. File systems are inherently not transactional, so when attempting to use your cache in a transactional context, failures when writing to the file (which happens during the commit phase) cannot be recovered. As a rule of thumb, it is recommended that the FileCacheLoader not be used in a highly concurrent, transactional or stressful environment, and it's use is restricted to testing. Can writing to cache loaders be asynchronous? Yes. Set the async attrobute to true. See the JBoss Cache User Guide for a more detailed discussion. By default though, all cache loader writes are synchronous and will block. Can I write my own cache loader ? Yes. A cache loader is a class implementing org.jboss.cache.loader.CacheLoader or extending org.jboss.cache.loader.AbstractCacheLoader . It is configured via the XML file (see JBoss Cache User Guide). Does a cache loader have to use a persistent store ? No, a cache loader could for example fetch (and possibly store) its data from a webdav-capable webserver. Another example is a caching proxy server, which fetches contents from the web. Note that an implementation of CacheLoader may not implement the 'store' functionality in this case, but just the 'load' functionality. Do I have to pay to use Oracle's Berkeley DB CacheLoader? Not if you use it only for personal use. As soon as you distribute your product with BdbjeCacheLoader, you have to purchase a commercial license from Oracle. See details at http://www.sleepycat.com/jeforjbosscache . Are there any tools available to monitor the Berkeley DB instance? Yes. Oracle ships a JMX-based monitoring tool, called JEMonitor which can be downloaded from the Oracle website. When tuning my Berkeley DB instance, where should I put my je.properties file? je.properties should reside in your Berkeley DB home directory. This is the directory you pass in to the BDBJECacheLoader's location configuration property. Can I use more than one cache loader? Yes. Within the CacheLoaderConfiguration XML element (see user guide chapter on cache loaders) you can describe several cache loaders. The impact is that the cache will look at all of the cache loaders in the order they've been configured, until it finds a valid, non-null element of data. When performing writes, all cache loaders are written to (except if the ignoreModifications element has been set to true for a specific cache loader. Can I migrate a JDBCacheLoader or FileCacheLoader based cache store containing data formatted with JBoss Cache 1.x.x to JBoss Cache 2.0 format? Yes. See "Transforming Cache Loaders" section within the "Cache Loaders" section located in the JBoss Cache users guide. Is the TCPDelegatingCacheLoader resilient to TCPCacheServer restarts? As of JBoss Cache 2.1.0, the answer is yes. See the User Guide for details on how to configure and tune your retries and wait period for reestablishing the TCP connection. Prior to that, restarting the TCPCacheServer would also mean restarting your application that uses the cache. Troubleshooting I am having problems getting JBoss Cache to work, where can I get information on troubleshooting? Troubleshooting section can be found in the following wiki link . jbosscache-core-2.2.2.GA/src/main/docbook/userguide/0000755000175000017500000000000011376174000022121 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/main/docbook/userguide/en/0000755000175000017500000000000011376174000022523 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/main/docbook/userguide/en/master.xml0000644000175000017500000000765011031017515024543 0ustar twernertwerner ]> JBoss Cache User Guide A clustered, transactional cache Release 2.2.0 Poblano July 2008 Manik Surtani manik@jboss.org Bela Ban bela@jboss.com Ben Wang ben.wang@jboss.com Brian Stansberry brian.stansberry@jboss.com Galder Zamarreño galder.zamarreno@jboss.com Daniel Huang dhuang@jboss.org Mircea Markus mircea.markus@jboss.com 2004 2005 2006 2007 2008 JBoss, a division of Red Hat Inc. &preface; Introduction to JBoss Cache This section covers what developers would need to quickly start using JBoss Cache in their projects. It covers an overview of the concepts and API, configuration and deployment information. &introduction; &basic_api; &configuration; &deployment; &compatibility; JBoss Cache Architecture This section digs deeper into the JBoss Cache architecture, and is meant for developers wishing to extend or enhance JBoss Cache, write plugins or are just looking for detailed knowledge of how things work under the hood. &architecture; &replication; &cache_loaders; &eviction_policies; &transactions; JBoss Cache Configuration References This section contains technical references for easy looking up. &configuration_reference; &jmx_reference; jbosscache-core-2.2.2.GA/src/main/docbook/userguide/en/modules/0000755000175000017500000000000011376174000024173 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/main/docbook/userguide/en/modules/compatibility.xml0000644000175000017500000000340010660354341027566 0ustar twernertwerner Version Compatibility and Interoperability Within a major version, releases of JBoss Cache are meant to be compatible and interoperable. Compatible in the sense that it should be possible to upgrade an application from one version to another by simply replacing the jars. Interoperable in the sense that if two different versions of JBoss Cache are used in the same cluster, they should be able to exchange replication and state transfer messages. Note however that interoperability requires use of the same JGroups version in all nodes in the cluster. In most cases, the version of JGroups used by a version of JBoss Cache can be upgraded. As such, JBoss Cache 2.x.x is not API or binary compatible with prior 1.x.x versions. However, JBoss Cache 2.1.x will be API and binary compatible with 2.0.x. A configuration attribute, ReplicationVersion, is available and is used to control the wire format of inter-cache communications. They can be wound back from more efficient and newer protocols to "compatible" versions when talking to older releases. This mechanism allows us to improve JBoss Cache by using more efficient wire formats while still providing a means to preserve interoperability.
Compatibility Matrix A compatibility matrix is maintained on the JBoss Cache website, which contains information on different versions of JBoss Cache, JGroups and JBoss AS.
jbosscache-core-2.2.2.GA/src/main/docbook/userguide/en/modules/cache_loaders.xml0000644000175000017500000020044711014576616027510 0ustar twernertwerner Cache Loaders JBoss Cache can use a CacheLoader to back up the in-memory cache to a backend datastore. If JBoss Cache is configured with a cache loader, then the following features are provided: Whenever a cache element is accessed, and that element is not in the cache (e.g. due to eviction or due to server restart), then the cache loader transparently loads the element into the cache if found in the backend store. Whenever an element is modified, added or removed, then that modification is persisted in the backend store via the cache loader. If transactions are used, all modifications created within a transaction are persisted. To this end, the CacheLoader takes part in the two phase commit protocol run by the transaction manager, although it does not do so explicitly.
The CacheLoader Interface and Lifecycle
The CacheLoader interface
The interaction between JBoss Cache and a CacheLoader implementation is as follows. When CacheLoaderConfiguration (see below) is non-null, an instance of each configured CacheLoader is created when the cache is created, and started when the cache is started. CacheLoader.create() and CacheLoader.start() are called when the cache is started. Correspondingly, stop() and destroy() are called when the cache is stopped. Next, setConfig() and setCache() are called. The latter can be used to store a reference to the cache, the former is used to configure this instance of the CacheLoader . For example, here a database cache loader could establish a connection to the database. The CacheLoader interface has a set of methods that are called when no transactions are used: get() , put() , remove() and removeData() : they get/set/remove the value immediately. These methods are described as javadoc comments in the interface. Then there are three methods that are used with transactions: prepare() , commit() and rollback() . The prepare() method is called when a transaction is to be committed. It has a transaction object and a list of modfications as argument. The transaction object can be used as a key into a hashmap of transactions, where the values are the lists of modifications. Each modification list has a number of Modification elements, which represent the changes made to a cache for a given transaction. When prepare() returns successfully, then the cache loader must be able to commit (or rollback) the transaction successfully. JBoss Cache takes care of calling prepare(), commit() and rollback() on the cache loaders at the right time. The commit() method tells the cache loader to commit the transaction, and the rollback() method tells the cache loader to discard the changes associated with that transaction. See the javadocs on this interface for a detailed explanation on each method and the contract implementations would need to fulfil.
Configuration Cache loaders are configured as follows in the JBoss Cache XML file. Note that you can define several cache loaders, in a chain. The impact is that the cache will look at all of the cache loaders in the order they've been configured, until it finds a valid, non-null element of data. When performing writes, all cache loaders are written to (except if the ignoreModifications element has been set to true for a specific cache loader. See the configuration section below for details. false / false org.jboss.cache.loader.JDBCCacheLoader cache.jdbc.driver=com.mysql.jdbc.Driver cache.jdbc.url=jdbc:mysql://localhost:3306/jbossdb cache.jdbc.user=root cache.jdbc.password= cache.jdbc.sql-concat=concat(1,2) false true false false false org.jboss.cache.loader.SingletonStoreCacheLoader pushStateWhenCoordinator=true pushStateWhenCoordinatorTimeout=20000 ]]> The class element defines the class of the cache loader implementation. (Note that, because of a bug in the properties editor in JBoss AS, backslashes in variables for Windows filenames might not get expanded correctly, so replace="false" may be necessary). Note that an implementation of cache loader has to have an empty constructor. The properties element defines a configuration specific to the given implementation. The filesystem-based implementation for example defines the root directory to be used, whereas a database implementation might define the database URL, name and password to establish a database connection. This configuration is passed to the cache loader implementation via CacheLoader.setConfig(Properties) . Note that backspaces may have to be escaped. preload allows us to define a list of nodes, or even entire subtrees, that are visited by the cache on startup, in order to preload the data associated with those nodes. The default ("/") loads the entire data available in the backend store into the cache, which is probably not a good idea given that the data in the backend store might be large. As an example, /a, /product/catalogue loads the subtrees /a and /product/catalogue into the cache, but nothing else. Anything else is loaded lazily when accessed. Preloading makes sense when one anticipates using elements under a given subtree frequently. . fetchPersistentState determines whether or not to fetch the persistent state of a cache when joining a cluster. Only one configured cache loader may set this property to true; if more than one cache loader does so, a configuration exception will be thrown when starting your cache service. async determines whether writes to the cache loader block until completed, or are run on a separate thread so writes return immediately. If this is set to true, an instance of org.jboss.cache.loader.AsyncCacheLoader is constructed with an instance of the actual cache loader to be used. The AsyncCacheLoader then delegates all requests to the underlying cache loader, using a separate thread if necessary. See the Javadocs on AsyncCacheLoader for more details. If unspecified, the async element defaults to false . Note on using the async element: there is always the possibility of dirty reads since all writes are performed asynchronously, and it is thus impossible to guarantee when (and even if) a write succeeds. This needs to be kept in mind when setting the async element to true. ignoreModifications determines whether write methods are pushed down to the specific cache loader. Situations may arise where transient application data should only reside in a file based cache loader on the same server as the in-memory cache, for example, with a further shared JDBCCacheLoader used by all servers in the network. This feature allows you to write to the 'local' file cache loader but not the shared JDBCCacheLoader . This property defaults to false , so writes are propagated to all cache loaders configured. purgeOnStatup empties the specified cache loader (if ignoreModifications is false ) when the cache loader starts up. shared indicates that the cache loader is shared among different cache instances, for example where all instances in a cluster use the same JDBC settings t talk to the same remote, shared database. Setting this to true prevents repeated and unnecessary writes of the same data to the cache loader by different cache instances. Default value is false .
Singleton Store Configuration singletonStore element enables modifications to be stored by only one node in the cluster, the coordinator. Essentially, whenever any data comes in to some node it is always replicated so as to keep the caches' in-memory states in sync; the coordinator, though, has the sole responsibility of pushing that state to disk. This functionality can be activated setting the enabled subelement to true in all nodes, but again only the coordinator of the cluster will store the modifications in the underlying cache loader as defined in cacheloader element. You cannot define a cache loader as shared and with singletonStore enabled at the same time. Default value for enabled is false . Optionally, within the singletonStore element, you can define a class element that specifies the implementation class that provides the singleton store functionality. This class must extend org.jboss.cache.loader.AbstractDelegatingCacheLoader , and if absent, it defaults to org.jboss.cache.loader.SingletonStoreCacheLoader . The properties subelement defines properties that allow changing the behaivour of the class providing the singleton store functionality. By default, pushStateWhenCoordinator and pushStateWhenCoordinatorTimeout properties have been defined, but more could be added as required by the user-defined class providing singleton store functionality. pushStateWhenCoordinator allows the in-memory state to be pushed to the cache store when a node becomes the coordinator, as a result of the new election of coordinator due to a cluster topology change. This can be very useful in situations where the coordinator crashes and there's a gap in time until the new coordinator is elected. During this time, if this property was set to false and the cache was updated, these changes would never be persisted. Setting this property to true would ensure that any changes during this process also get stored in the cache loader. You would also want to set this property to true if each node's cache loader is configured with a different location. Default value is true . pushStateWhenCoordinatorTimeout is only relevant if pushStateWhenCoordinator is true in which case, sets the maximum number of milliseconds that the process of pushing the in-memory state to the underlying cache loader should take, reporting a PushStateException if exceeded. Default value is 20000. Note on using the singletonStore element: setting up a cache loader as a singleton and using cache passivation (via evictions) can lead to undesired effects. If a node is to be passivated as a result of an eviction, while the cluster is in the process of electing a new coordinator, the data will be lost. This is because no coordinator is active at that time and therefore, none of the nodes in the cluster will store the passivated node. A new coordinator is elected in the cluster when either, the coordinator leaves the cluster, the coordinator crashes or stops responding.
Shipped Implementations The currently available implementations shipped with JBoss Cache are as follows.
File system based cache loaders JBoss Cache ships with several cache loaders that utilise the file system as a data store. They all require that the ]]> configuration element contains a location property, which maps to a directory to be used as a persistent store. (e.g., location=/tmp/myDataStore ). Used mainly for testing and not recommended for production use. FileCacheLoader , which is a simple filesystem-based implementation. By default, this cache loader checks for any potential character portability issues in the location or tree node names, for example invalid characters, producing warning messages. These checks can be disabled adding check.character.portability property to the ]]> element and setting it to false (e.g., check.character.portability=false ). The FileCacheLoader has some severe limitations which restrict it's use in a production environment, or if used in such an environment, it should be used with due care and sufficient understanding of these limitations. Due to the way the FileCacheLoader represents a tree structure on disk (directories and files) traversal is inefficient for deep trees. Usage on shared filesystems like NFS, Windows shares, etc. should be avoided as these do not implement proper file locking and can cause data corruption. Usage with an isolation level of NONE can cause corrupt writes as multiple threads attempt to write to the same file. File systems are inherently not transactional, so when attempting to use your cache in a transactional context, failures when writing to the file (which happens during the commit phase) cannot be recovered. As a rule of thumb, it is recommended that the FileCacheLoader not be used in a highly concurrent, transactional or stressful environment, and it's use is restricted to testing. BdbjeCacheLoader , which is a cache loader implementation based on the Oracle/Sleepycat's BerkeleyDB Java Edition . JdbmCacheLoader , which is a cache loader implementation based on the JDBM engine , a fast and free alternative to BerkeleyDB. Note that the BerkeleyDB implementation is much more efficient than the filesystem-based implementation, and provides transactional guarantees, but requires a commercial license if distributed with an application (see http://www.oracle.com/database/berkeley-db/index.html for details).
Cache loaders that delegate to other caches LocalDelegatingCacheLoader , which enables loading from and storing to another local (same JVM) cache. ClusteredCacheLoader , which allows querying of other caches in the same cluster for in-memory data via the same clustering protocols used to replicate data. Writes are not 'stored' though, as replication would take care of any updates needed. You need to specify a property called timeout , a long value telling the cache loader how many milliseconds to wait for responses from the cluster before assuming a null value. For example, timeout = 3000 would use a timeout value of 3 seconds.
JDBCCacheLoader JBossCache is distributed with a JDBC-based cache loader implementation that stores/loads nodes' state into a relational database. The implementing class is org.jboss.cache.loader.JDBCCacheLoader . The current implementation uses just one table. Each row in the table represents one node and contains three columns: column for Fqn (which is also a primary key column) column for node contents (attribute/value pairs) column for parent Fqn Fqn 's are stored as strings. Node content is stored as a BLOB. WARNING: JBoss Cache does not impose any limitations on the types of objects used in Fqn but this implementation of cache loader requires Fqn to contain only objects of type java.lang.String . Another limitation for Fqn is its length. Since Fqn is a primary key, its default column type is VARCHAR which can store text values up to some maximum length determined by the database in use. See http://wiki.jboss.org/wiki/Wiki.jsp?page=JDBCCacheLoader for configuration tips with specific database systems.
JDBCCacheLoader configuration
Table configuration Table and column names as well as column types are configurable with the following properties. cache.jdbc.table.name - the name of the table. Can be prepended with schema name for the given table: <schema_name>.<table_name>. The default value is 'jbosscache'. cache.jdbc.table.primarykey - the name of the primary key for the table. The default value is 'jbosscache_pk'. cache.jdbc.table.create - can be true or false. Indicates whether to create the table during startup. If true, the table is created if it doesn't already exist. The default value is true. cache.jdbc.table.drop - can be true or false. Indicates whether to drop the table during shutdown. The default value is true. cache.jdbc.fqn.column - FQN column name. The default value is 'fqn'. cache.jdbc.fqn.type - FQN column type. The default value is 'varchar(255)'. cache.jdbc.node.column - node contents column name. The default value is 'node'. cache.jdbc.node.type - node contents column type. The default value is 'blob'. This type must specify a valid binary data type for the database being used.
DataSource If you are using JBossCache in a managed environment (e.g., an application server) you can specify the JNDI name of the DataSource you want to use. cache.jdbc.datasource - JNDI name of the DataSource. The default value is java:/DefaultDS .
JDBC driver If you are not using DataSource you have the following properties to configure database access using a JDBC driver. cache.jdbc.driver - fully qualified JDBC driver name. cache.jdbc.url - URL to connect to the database. cache.jdbc.user - user name to connect to the database. cache.jdbc.password - password to connect to the database.
c3p0 connection pooling JBoss Cache implements JDBC connection pooling when running outside of an application server standalone using the c3p0:JDBC DataSources/Resource Pools library. In order to enable it, just edit the following property: cache.jdbc.connection.factory - Connection factory class name. If not set, it defaults to standard non-pooled implementation. To enable c3p0 pooling, just set the connection factory class for c3p0. See example below. You can also set any c3p0 parameters in the same cache loader properties section but don't forget to start the property name with 'c3p0.'. To find a list of available properties, please check the c3p0 documentation for the c3p0 library version distributed in c3p0:JDBC DataSources/Resource Pools . Also, in order to provide quick and easy way to try out different pooling parameters, any of these properties can be set via a System property overriding any values these properties might have in the JBoss Cache XML configuration file, for example: -Dc3p0.maxPoolSize=20 . If a c3p0 property is not defined in either the configuration file or as a System property, default value, as indicated in the c3p0 documentation, will apply.
Configuration example Below is an example of a JDBCCacheLoader using Oracle as database. The CacheLoaderConfiguration XML element contains an arbitrary set of properties which define the database-related configuration. false /some/stuff org.jboss.cache.loader.JDBCCacheLoader cache.jdbc.table.name=jbosscache cache.jdbc.table.create=true cache.jdbc.table.drop=true cache.jdbc.table.primarykey=jbosscache_pk cache.jdbc.fqn.column=fqn cache.jdbc.fqn.type=varchar(255) cache.jdbc.node.column=node cache.jdbc.node.type=blob cache.jdbc.parent.column=parent cache.jdbc.driver=oracle.jdbc.OracleDriver cache.jdbc.url=jdbc:oracle:thin:@localhost:1521:JBOSSDB cache.jdbc.user=SCOTT cache.jdbc.password=TIGER cache.jdbc.sql-concat=concat(1,2) false true false false ]]> As an alternative to configuring the entire JDBC connection, the name of an existing data source can be given: false /some/stuff org.jboss.cache.loader.JDBCCacheLoader cache.jdbc.datasource=java:/DefaultDS false true false false ]]> Cconfiguration example for a cache loader using c3p0 JDBC connection pooling: false /some/stuff org.jboss.cache.loader.JDBCCacheLoader cache.jdbc.table.name=jbosscache cache.jdbc.table.create=true cache.jdbc.table.drop=true cache.jdbc.table.primarykey=jbosscache_pk cache.jdbc.fqn.column=fqn cache.jdbc.fqn.type=varchar(255) cache.jdbc.node.column=node cache.jdbc.node.type=blob cache.jdbc.parent.column=parent cache.jdbc.driver=oracle.jdbc.OracleDriver cache.jdbc.url=jdbc:oracle:thin:@localhost:1521:JBOSSDB cache.jdbc.user=SCOTT cache.jdbc.password=TIGER cache.jdbc.sql-concat=concat(1,2) cache.jdbc.connection.factory=org.jboss.cache.loader.C3p0ConnectionFactory c3p0.maxPoolSize=20 c3p0.checkoutTimeout=5000 false true false false ]]>
S3CacheLoader The S3CacheLoader uses the Amazon S3 (Simple Storage Solution) for storing cache data. Since Amazon S3 is remote network storage and has fairly high latency, it is really best for caches that store large pieces of data, such as media or files. But consider this cache loader over the JDBC or file system based cache loaders if you want remotely managed, highly reliable storage. Or, use it for applications running on Amazon's EC2 (Elastic Compute Cloud). If you're planning to use Amazon S3 for storage, consider using it with JBoss Cache. JBoss Cache itself provides in-memory caching for your data to minimize the amount of remote access calls, thus reducing the latency and cost of fetching your Amazon S3 data. With cache replication, you are also able to load data from your local cluster without having to remotely access it every time. Note that Amazon S3 does not support transactions. If transactions are used in your application then there is some possibility of state inconsistency when using this cache loader. However, writes are atomic, in that if a write fails nothing is considered written and data is never corrupted. Data is stored in keys based on the Fqn of the Node and Node data is serialized as a java.util.Map using the CacheSPI.getMarshaller() instance. Read the javadoc on how data is structured and stored. Data is stored using Java serialization. Be aware this means data is not readily accessible over HTTP to non-JBoss Cache clients. Your feedback and help would be appreciated to extend this cache loader for that purpose. With this cache loader, single-key operations such as Node.remove(Object) and Node.put(Object, Object) are the slowest as data is stored in a single Map instance. Use bulk operations such as Node.replaceAll(Map) and Node.clearData() for more efficiency. Try the cache.s3.optimize option as well.
Amazon S3 Library The S3 cache loader is provided with the default distribution but requires a library to access the service at runtime. This runtime library may be obtained through a Sourceforge Maven Repository. Include the following sections in your pom.xml file: e-xml.sourceforge.net http://e-xml.sourceforge.net/maven2/repository ... net.noderunner amazon-s3 1.0.0.0 runtime ]]> If you do not use Maven, you can still download the amazon-s3 library by navigating the repository or through this URL.
Configuration At a minimum, you must configure your Amazon S3 access key and secret access key. The following configuration keys are listed in general order of utility. cache.s3.accessKeyId - Amazon S3 Access Key, available from your account profile. cache.s3.secretAccessKey - Amazon S3 Secret Access Key, available from your account profile. As this is a password, be careful not to distribute it or include this secret key in built software. cache.s3.secure - The default isfalse: Traffic is sent unencrypted over the public Internet. Set to true to use HTTPS. Note that unencrypted uploads and downloads use less CPU. cache.s3.bucket - Name of the bucket to store data. For different caches using the same access key, use a different bucket name. Read the S3 documentation on the definition of a bucket. The default value is jboss-cache. cache.s3.callingFormat - One of PATH, SUBDOMAIN, or VANITY. Read the S3 documentation on the use of calling domains. The default value is SUBDOMAIN. cache.s3.optimize - The default is false. If true, put(Map) operations replace the data stored at an Fqn rather than attempt to fetch and merge. (This option is fairly experimental at the moment.) cache.s3.parentCache - The default is true. Set this value to false if you are using multiple caches sharing the same S3 bucket, that remove parent nodes of nodes being created in other caches. (This is not a common use case.) JBoss Cache stores nodes in a tree format and automatically creates intermediate parent nodes as necessary. The S3 cache loader must also create these parent nodes as well to allow for operations such as getChildrenNames to work properly. Checking if all parent nodes exists for every put operation is fairly expensive, so by default the cache loader caches the existence of these parent nodes. cache.s3.location - This choses a primary storage location for your data to reduce loading and retrevial latency. Set to EU to store data in Europe. The default is null, to store data in the United States.
TcpDelegatingCacheLoader This cache loader allows to delegate loads and stores to another instance of JBoss Cache, which could reside (a) in the same address space, (b) in a different process on the same host, or (c) in a different process on a different host. A TcpDelegatingCacheLoader talks to a remote org.jboss.cache.loader.tcp.TcpCacheServer , which can be a standalone process started on the command line, or embedded as an MBean inside JBoss AS. The TcpCacheServer has a reference to another JBoss Cache instance, which it can create itself, or which is given to it (e.g. by JBoss, using dependency injection). As of JBoss Cache 2.1.0, the TcpDelegatingCacheLoader transparently handles reconnects if the connection to the TcpCacheServer is lost. The TcpDelegatingCacheLoader is configured with the host and port of the remote TcpCacheServer, and uses this to communicate to it. In addition, 2 new optional parameters are used to control transparent reconnecting to the TcpCacheServer. The timeout property (defaults to 5000) specifies the length of time the cache loader must continue retrying to connect to the TcpCacheServer before giving up and throwing an exception. The reconnectWaitTime (defaults to 500) is how long the cache loader should wait before attempting a reconnect if it detects a communication failure. The last two parameters can be used to add a level of fault tolerance to the cache loader, do deal with TcpCacheServer restarts. The configuration looks as follows: org.jboss.cache.loader.TcpDelegatingCacheLoader host=myRemoteServer port=7500 timeout=10000 reconnectWaitTime=250 ]]> This means this instance of JBoss Cache will delegate all load and store requests to the remote TcpCacheServer running on myRemoteServer:7500 . A typical use case could be multiple replicated instances of JBoss Cache in the same cluster, all delegating to the same TcpCacheServer instance. The TcpCacheServer might itself delegate to a database via JDBCCacheLoader, but the point here is that - if we have 5 nodes all accessing the same dataset - they will load the data from the TcpCacheServer, which has do execute one SQL statement per unloaded data set. If the nodes went directly to the database, then we'd have the same SQL executed multiple times. So TcpCacheServer serves as a natural cache in front of the DB (assuming that a network round trip is faster than a DB access (which usually also include a network round trip)). To alleviate single point of failure, we could configure several cache loaders. The first cache loader is a ClusteredCacheLoader, the second a TcpDelegatingCacheLoader, and the last a JDBCacheLoader, effectively defining our cost of access to a cache in increasing order.
Transforming Cache Loaders The way cached data is written to FileCacheLoader and JDBCCacheLoader based cache stores has changed in JBoss Cache 2.0 in such way that these cache loaders now write and read data using the same marhalling framework used to replicate data accross the network. Such change is trivial for replication purpouses as it just requires the rest of the nodes to understand this format. However, changing the format of the data in cache stores brings up a new problem: how do users, which have their data stored in JBoss Cache 1.x.x format, migrate their stores to JBoss Cache 2.0 format? With this in mind, JBoss Cache 2.0 comes with two cache loader implementations called org.jboss.cache.loader.TransformingFileCacheLoader and org.jboss.cache.loader.TransformingJDBCCacheLoader located within the optional jbosscache-cacheloader-migration.jar file. These are one-off cache loaders that read data from the cache store in JBoss Cache 1.x.x format and write data to cache stores in JBoss Cache 2.0 format. The idea is for users to modify their existing cache configuration file(s) momentarily to use these cache loaders and for them to create a small Java application that creates an instance of this cache, recursively reads the entire cache and writes the data read back into the cache. Once the data is transformed, users can revert back to their original cache configuration file(s). In order to help the users with this task, a cache loader migration example has been constructed which can be located under the examples/cacheloader-migration directory within the JBoss Cache distribution. This example, called examples.TransformStore , is independent of the actual data stored in the cache as it writes back whatever it was read recursively. It is highly recommended that anyone interested in porting their data run this example first, which contains a readme.txt file with detailed information about the example itself, and also use it as base for their own application.
Cache Passivation A cache loader can be used to enforce node passivation and activation on eviction in a cache. Cache Passivation is the process of removing an object from in-memory cache and writing it to a secondary data store (e.g., file system, database) on eviction. Cache Activation is the process of restoring an object from the data store into the in-memory cache when it's needed to be used. In both cases, the configured cache loader will be used to read from the data store and write to the data store. When an eviction policy in effect evicts a node from the cache, if passivation is enabled, a notification that the node is being passivated will be emitted to the cache listeners and the node and its children will be stored in the cache loader store. When a user attempts to retrieve a node that was evicted earlier, the node is loaded (lazy loaded) from the cache loader store into memory. When the node and its children have been loaded, they're removed from the cache loader and a notification is emitted to the cache listeners that the node has been activated. To enable cache passivation/activation, you can set passivation to true. The default is false . When passivation is used, only the first cache loader configured is used and all others are ignored.
Cache Loader Behavior with Passivation Disabled vs. Enabled When passivation is disabled, whenever an element is modified, added or removed, then that modification is persisted in the backend store via the cache loader. There is no direct relationship between eviction and cache loading. If you don't use eviction, what's in the persistent store is basically a copy of what's in memory. If you do use eviction, what's in the persistent store is basically a superset of what's in memory (i.e. it includes nodes that have been evicted from memory). When passivation is enabled, there is a direct relationship between eviction and the cache loader. Writes to the persistent store via the cache loader only occur as part of the eviction process. Data is deleted from the persistent store when the application reads it back into memory. In this case, what's in memory and what's in the persistent store are two subsets of the total information set, with no intersection between the subsets. Following is a simple example, showing what state is in RAM and in the persistent store after each step of a 6 step process: Insert /A Insert /B Eviction thread runs, evicts /A Read /A Eviction thread runs, evicts /B Remove /B When passivation is disabled: 1) RAM: /A Disk: /A 2) RAM: /A, /B Disk: /A, /B 3) RAM: /B Disk: /A, /B 4) RAM: /A, /B Disk: /A, /B 5) RAM: /A Disk: /A, /B 6) RAM: /A Disk: /A When passivation is enabled: 1) RAM: /A Disk: 2) RAM: /A, /B Disk: 3) RAM: /B Disk: /A 4) RAM: /A, /B Disk: 5) RAM: /A Disk: /B 6) RAM: /A Disk:
Strategies This section discusses different patterns of combining different cache loader types and configuration options to achieve specific outcomes.
Local Cache With Store This is the simplest case. We have a JBoss Cache instance, whose cache mode is LOCAL , therefore no replication is going on. The cache loader simply loads non-existing elements from the store and stores modifications back to the store. When the cache is started, depending on the preload element, certain data can be preloaded, so that the cache is partly warmed up.
Replicated Caches With All Caches Sharing The Same Store The following figure shows 2 JBoss Cache instances sharing the same backend store:
2 nodes sharing a backend store
Both nodes have a cache loader that accesses a common shared backend store. This could for example be a shared filesystem (using the FileCacheLoader), or a shared database. Because both nodes access the same store, they don't necessarily need state transfer on startup. Of course they can enable state transfer, if they want to have a warm or hot cache after startup. Rather, the FetchInMemoryState attribute could be set to false, resulting in a 'cold' cache, that gradually warms up as elements are accessed and loaded for the first time. This would mean that individual caches in a cluster might have different in-memory state at any given time (largely depending on their preloading and eviction strategies). When storing a value, the writer takes care of storing the change in the backend store. For example, if node1 made change C1 and node2 C2, then node1 would tell its cache loader to store C1, and node2 would tell its cache loader to store C2.
Replicated Caches With Only One Cache Having A Store
2 nodes but only one accesses the backend store
This is a similar case to the previous one, but here only one node in the cluster interacts with a backend store via its cache loader. All other nodes perform in-memory replication. The idea here is all application state is kept in memory in each node, with the existence of multiple caches making the data highly available. (This assumes that a client that needs the data is able to somehow fail over from one cache to another.) The single persistent backend store then provides a backup copy of the data in case all caches in the cluster fail or need to be restarted. Note that here it may make sense for the cache loader to store changes asynchronously, that is not on the caller's thread, in order not to slow down the cluster by accessing (for example) a database. This is a non-issue when using asynchronous replication. A weakness with this architecture is that the cache with access to the cache loader becomes a single point of failure. Furthermore, if the cluster is restarted, the cache with the cache loader must be started first (easy to forget). A solution to the first problem is to configure a cache loader on each node, but set the singletonStore configuration to true. With this kind of setup, one but only one node will always be writing to a persistent store. However, this complicates the restart problem, as before restarting you need to determine which cache was writing before the shutdown/failure and then start that cache first.
Replicated Caches With Each Cache Having Its Own Store
2 nodes each having its own backend store
Here, each node has its own datastore. Modifications to the cache are (a) replicated across the cluster and (b) persisted using the cache loader. This means that all datastores have exactly the same state. When replicating changes synchronously and in a transaction, the two phase commit protocol takes care that all modifications are replicated and persisted in each datastore, or none is replicated and persisted (atomic updates). Note that JBoss Cache is not an XA Resource, that means it doesn't implement recovery. When used with a transaction manager that supports recovery, this functionality is not available. The challenge here is state transfer: when a new node starts it needs to do the following: Tell the coordinator (oldest node in a cluster) to send it the state. This is always a full state transfer, overwriting any state that may already be present. The coordinator then needs to wait until all in-flight transactions have completed. During this time, it will not allow for new transactions to be started. Then the coordinator asks its cache loader for the entire state using loadEntireState() . It then sends back that state to the new node. The new node then tells its cache loader to store that state in its store, overwriting the old state. This is the CacheLoader.storeEntireState() method As an option, the transient (in-memory) state can be transferred as well during the state transfer. The new node now has the same state in its backend store as everyone else in the cluster, and modifications received from other nodes will now be persisted using the local cache loader.
Hierarchical Caches If you need to set up a hierarchy within a single JVM, you can use the LocalDelegatingCacheLoader . This type of hierarchy can currently only be set up programmatically. Hierarchical caches could also be set up spanning more than one JVM or server, using the TcpDelegatingCacheLoader .
TCP delegating cache loader
Multiple Cache Loaders You can set up more than one cache loader in a chain. Internally, a delegating ChainingCacheLoader is used, with references to each cache loader you have configured. Use cases vary depending on the type of cache loaders used in the chain. One example is using a filesystem based cache loader, colocated on the same host as the JVM, used as an overflow for memory. This ensures data is available relatively easily and with low cost. An additional remote cache loader, such as a TcpDelegatingCacheLoader provides resilience between server restarts.
Multiple cache loaders in a chain
jbosscache-core-2.2.2.GA/src/main/docbook/userguide/en/modules/introduction.xml0000644000175000017500000001663710660354341027456 0ustar twernertwerner Overview
What is JBoss Cache? JBoss Cache is a tree-structured, clustered, transactional cache. It is the backbone for many fundamental JBoss Application Server clustering services, including - in certain versions - clustering JNDI, HTTP and EJB sessions. JBoss Cache can also be used as a standalone transactional and clustered caching library or even an object oriented data store. It can even be embedded in other enterprise Java frameworks and application servers such as BEA WebLogic or IBM WebSphere, Tomcat, Spring, Hibernate, and many others. It is also very commonly used directly by standalone Java applications that do not run from within an application server, to maintain clustered state.
And what is Pojo Cache? Pojo Cache is an extension of the core JBoss Cache API. Pojo Cache offers additional functionality such as: maintaining object references even after replication or persistence. fine grained replication, where only modified object fields are replicated. "API-less" clustering model where pojos are simply annotated as being clustered. Pojo Cache has a complete and separate set of documentation, including a user guide, FAQ and tutorial and as such, Pojo Cache is not discussed further in this book.
Summary of Features JBoss Cache offers a simple and straightforward API, where data (simple Java objects) can be placed in the cache and, based on configuration options selected, this data may be one or all of: replicated to some or all cache instances in a cluster. persisted to disk and/or a remote cluster ("far-cache"). garbage collected from memory when memory runs low, and passivated to disk so state isn't lost. In addition, JBoss Cache offers a rich set of enterprise-class features: being able to participate in JTA transactions (works with Java EE compliant TransactionManagers). attach to JMX servers and provide runtime statistics on the state of the cache. allow client code to attach listeners and receive notifications on cache events. A cache is organised as a tree, with a single root. Each node in the tree essentially contains a Map, which acts as a store for key/value pairs. The only requirement placed on objects that are cached is that they implement java.io.Serializable . Note that this requirement does not exist for Pojo Cache. JBoss Cache can be either local or replicated. Local trees exist only inside the JVM in which they are created, whereas replicated trees propagate any changes to some or all other trees in the same cluster. A cluster may span different hosts on a network or just different JVMs on a single host. When a change is made to an object in the cache and that change is done in the context of a transaction, the replication of changes is deferred until the transaction commits successfully. All modifications are kept in a list associated with the transaction for the caller. When the transaction commits, we replicate the changes. Otherwise, (on a rollback) we simply undo the changes locally resulting in zero network traffic and overhead. For example, if a caller makes 100 modifications and then rolls back the transaction, we will not replicate anything, resulting in no network traffic. If a caller has no transaction associated with it (and isolation level is not NONE - more about this later), we will replicate right after each modification, e.g. in the above case we would send 100 messages, plus an additional message for the rollback. In this sense, running without a transaction can be thought of as analogous as running with auto-commit switched on in JDBC terminology, where each operation is committed automatically. JBoss Cache works out of the box with most popular transaction managers, and even provides an API where custom transaction manager lookups can be written. The cache is also completely thread-safe. It uses a pessimistic locking scheme for nodes in the tree by default, with an optimistic locking scheme as a configurable option. With pessimistic locking, the degree of concurrency can be tuned using a number of isolation levels, corresponding to database-style transaction isolation levels, i.e., SERIALIZABLE, REPEATABLE_READ, READ_COMMITTED, READ_UNCOMMITTED and NONE. Concurrency, locking and isolation levels will be discussed later.
Requirements JBoss Cache requires Java 5.0 (or newer). However, there is a way to build JBoss Cache as a Java 1.4.x compatible binary using JBossRetro to retroweave the Java 5.0 binaries. However, Red Hat Inc. does not offer professional support around the retroweaved binary at this time and the Java 1.4.x compatible binary is not in the binary distribution. See this wiki page for details on building the retroweaved binary for yourself. In addition to Java 5.0, at a minimum, JBoss Cache has dependencies on JGroups , and Apache's commons-logging . JBoss Cache ships with all dependent libraries necessary to run out of the box.
License JBoss Cache is an open source product, using the business and OEM-friendly OSI-approved LGPL license. Commercial development support, production support and training for JBoss Cache is available through JBoss, a division of Red Hat Inc. JBoss Cache is a part of JBoss Professional Open Source JEMS (JBoss Enterprise Middleware Suite).
jbosscache-core-2.2.2.GA/src/main/docbook/userguide/en/modules/configuration_reference.xml0000644000175000017500000007432010765056217031622 0ustar twernertwerner Configuration References
Sample XML Configuration File This is what a typical XML configuration file looks like. It is recommended that you use one of the configurations shipped with the JBoss Cache distribution and tweak according to your needs rather than write one from scratch. jboss:service=Naming jboss:service=TransactionManager org.jboss.cache.transaction.GenericTransactionManagerLookup REPEATABLE_READ true REPL_ASYNC JBossCache-Cluster 20000 20000 15000 DEFAULT true 5 200000 org.jboss.cache.eviction.LRUPolicy 5000 1000 5000 1000 5 4 10000 4 10000 8 10 ]]>
Reference table of XML attributes A list of definitions of each of the XML attributes used above. If the description of an attribute states that it is dynamic , that means it can be changed after the cache is created and started. Name Description BuddyReplicationConfig An XML element that contains detailed buddy replication configuration. See section on Buddy Replication for details. CacheLoaderConfig An XML element that contains detailed cache loader configuration. See chapter on Cache Loaders for details. CacheLoaderConfiguration Deprecated . Use CacheLoaderConfig . CacheMode LOCAL, REPL_SYNC, REPL_ASYNC, INVALIDATION_SYNC or INVALIDATION_ASYNC. Defaults to LOCAL. See the chapter on Clustering for details. ClusterConfig The configuration of the underlying JGroups stack. Ignored if MultiplexerService and MultiplexerStack are used. See the various *-service.xml files in the source distribution etc/META-INF folder for examples. See the JGroups documentation or the JGroups wiki page for more information. ClusterName Name of cluster. Needs to be the same for all nodes in a cluster in order for them to communicate with each other. EvictionPolicyConfig Configuration parameter for the specified eviction policy. See chapter on eviction policies for details. This property is dynamic . ExposeManagementStatistics Specifies whether interceptors that provide statistics should have statistics gathering enabled at startup. Also controls whether a CacheMgmtInterceptor (whose sole purpose is gathering statistics) should be added to the interceptor chain. Default value is true . See the JBoss Cache Statistics section section for more details. FetchInMemoryState Whether or not to acquire the initial in-memory state from existing members. Allows for hot caches when enabled. Also see the fetchPersistentState element in CacheLoaderConfig . Defaults to true . This property is dynamic . InactiveOnStartup Whether or not the entire tree is inactive upon startup, only responding to replication messages after activateRegion() is called to activate one or more parts of the tree. If true, property FetchInMemoryState is ignored. This property should only be set to true if UseRegionBasedMarshalling is also true . StateRetrievalTimeout Time in milliseconds to wait for state retrieval. This should be longer than LockAcquisitionTimeout as the node providing state may need to wait that long to acquire necessary read locks on the cache. This property is dynamic . IsolationLevel Node locking isolation level : SERIALIZABLE, REPEATABLE_READ (default), READ_COMMITTED, READ_UNCOMMITTED, and NONE. Note that this is ignored if NodeLockingScheme is OPTIMISTIC. Case doesn't matter. See documentation on Transactions and Concurrency for more details. LockAcquisitionTimeout Time in milliseconds to wait for a lock to be acquired. If a lock cannot be acquired an exception will be thrown. This property is dynamic . LockParentForChildInsertRemove Controls whether inserting or removing a node requires a write lock on the node's parent (when pessimistic locking is used) or whether it results in an update of the parent node's version (when optimistic locking is used). The default value is false . MarshallerClass An instance of org.jboss.cache.marshall.Marshaller used to serialize data to byte streams. Defaults to org.jboss.cache.marshall.VersionAwareMarshaller if not specified. MultiplexerService The JMX object name of the service that defines the JGroups multiplexer. In JBoss AS 5.0 this service is normally defined in the jgroups-multiplexer.sar. This XML attribute can only be handled by the JBoss AS MBean deployment services; if it is included in a file passed to a CacheFactory the factory's creation of the cache will fail. Inside JBoss AS, the attribute should be specified using the "depends optional-attribute-name" syntax shown in the example above. Inside the AS if this attribute is defined, an instance of org.jgroups.jmx.JChannelFactoryMBean will be injected into the CacheJmxWrapper which will use it to obtain a multiplexed JGroups channel. The configuration of the channel will be that associated with MultiplexerStack . The ClusterConfig attribute will be ignored. MultiplexerStack The name of the JGroups stack to be used with the cache cluster. Stacks are defined in the configuration of the external MultiplexerService discussed above. In JBoss AS 5 this is normally done in the jgroups-multiplexer.sar/META-INF/multiplexer-stacks.xml file. The default stack is udp . This attribute is used in conjunction with MultiplexerService . NodeLockingScheme May be PESSIMISTIC (default) or OPTIMISTIC. ReplicationVersion Tells the cache to serialize cluster traffic in a format consistent with that used by the given release of JBoss Cache. Different JBoss Cache versions use different wire formats; setting this attribute tells a cache from a later release to serialize data using the format from an earlier release. This allows caches from different releases to interoperate. For example, a 2.1.0 cache could have this value set to "2.0.0", allowing it to interoperate with a 2.0.0 cache. Valid values are a dot-separated release number, with any final qualifer also separated by a dot, e.g. "2.0.0" or "2.0.0.GA". Values that indicate a 1.x release are not supported in the 2.x series. ReplQueueInterval Time in milliseconds for elements from the replication queue to be replicated. Only used if UseReplQueue is enabled. This property is dynamic . ReplQueueMaxElements Max number of elements in the replication queue until replication kicks in. Only used if UseReplQueue is enabled. This property is dynamic . SyncCommitPhase This option is used to control the behaviour of the commit part of a 2-phase commit protocol, when using REPL_SYNC (does not apply to other cache modes). By default this is set to false . There is a performance penalty to enabling this, especially when running in a large cluster, but the upsides are greater cluster-wide data integrity. See the chapter on clustered caches for more information on this. This property is dynamic . SyncReplTimeout For synchronous replication: time in milliseconds to wait until replication acks have been received from all nodes in the cluster. It is usually best that this is greater than LockAcquisitionTimeout . This property is dynamic . SyncRollbackPhase This option is used to control the behaviour of the rollback part of a 2-phase commit protocol, when using REPL_SYNC (does not apply to other cache modes). By default this is set to false . There is a performance penalty to enabling this, especially when running in a large cluster, but the upsides are greater cluster-wide data integrity. See the chapter on clustered caches for more information on this. This property is dynamic . TransactionManagerLookupClass The fully qualified name of a class implementing TransactionManagerLookup. Default is JBossTransactionManagerLookup. There is also an option of GenericTransactionManagerLookup for example. UseInterceptorMbeans Deprecated . Use ExposeManagementStatistics . UseRegionBasedMarshalling When unmarshalling replicated data, this option specifies whether or not to support use of different classloaders for different cache regions. This defaults to false if unspecified.

DEPRECATED. This option will disappear in JBoss Cache 3.x. See UseLazyDeserialization instead. UseReplQueue For asynchronous replication: whether or not to use a replication queue. Defaults to false . ShutdownHookBehavior An optional parameter that controls whether JBoss Cache registers a shutdown hook with the JVM runtime. Allowed values areDEFAULT, REGISTER and DONT_REGISTER. REGISTER and DONT_REGISTER forces or suppresses the registration of a shutdown hook, respectively, and DEFAULT registers one if an MBean server (other than the JDK default) cannot be found and it is assumed that the cache is running in a managed environment. The default if unspecified is, as expected, DEFAULT. UseLazyDeserialization An optional parameter that can be used to enable or disable the use of lazy deserialization for cached objects. Defaults tofalse, since it adds a small processing overhead. If lazy deserialization is disabled, support for implicitly using context class loaders registered with the calling thread goes away. ObjectInputStreamPoolSize and ObjectOutputStreamPoolSize Since JBoss Cache 2.1.0, object input and output streams - used to serialize and deserialize RPC calls in a cluster - are pooled to reduce the overhead of constructing such streams. They are reused by making use of special resettable stream implementations. by default, these stream pools are set at 50 objects each. You could increase or decrease the pool size if, while profiling, you see a lot of threads blocking on ObjectStreamPool.getInputStream() orObjectStreamPool.getOutputStream(). In general, having more streams is better than having fewer than needed. Based on your application, make sure you have more streams available than number of threads you expect to concurrently write to the cache.

jbosscache-core-2.2.2.GA/src/main/docbook/userguide/en/modules/configuration.xml0000644000175000017500000004003411000744014027555 0ustar twernertwerner Configuration
Configuration Overview The org.jboss.cache.config.Configuration class (along with its component parts ) is a Java Bean that encapsulates the configuration of the Cache and all of its architectural elements (cache loaders, evictions policies, etc.) The Configuration exposes numerous properties which are summarized in the configuration reference section of this book and many of which are discussed in later chapters. Any time you see a configuration option discussed in this book, you can assume that the Configuration class or one of its component parts exposes a simple property setter/getter for that configuration option.
Creating a <literal>Configuration</literal> As discussed in the User API section , before a Cache can be created, the CacheFactory must be provided with a Configuration object or with a file name or input stream to use to parse a Configuration from XML. The following sections describe how to accomplish this.
Parsing an XML-based Configuration File The most convenient way to configure JBoss Cache is via an XML file. The JBoss Cache distribution ships with a number of configuration files for common use cases. It is recommended that these files be used as a starting point, and tweaked to meet specific needs. Here is a simple example configuration file: org.jboss.cache.transaction.GenericTransactionManagerLookup READ_COMMITTED true LOCAL 15000 5 org.jboss.cache.eviction.LRUPolicy 5000 1000 ]]> Another, more complete, sample XML file is included in the configuration reference section of this book, along with a handy look-up table explaining the various options. For historical reasons, the format of the JBoss Cache configuraton file follows that of a JBoss AS Service Archive (SAR) deployment descriptor (and still can be used as such inside JBoss AS ). Because of this dual usage, you may see elements in some configuration files (such as depends or classpath ) that are not relevant outside JBoss AS. These can safely be ignored. Here's how you tell the CacheFactory to create and start a cache by finding and parsing a configuration file on the classpath:
Programmatic Configuration In addition to the XML-based configuration above, the Configuration can be built up programatically, using the simple property mutators exposed by Configuration and its components. When constructed, the Configuration object is preset with JBoss Cache defaults and can even be used as-is for a quick start. Following is an example of programatically creating a Configuration configured to match the one produced by the XML example above, and then using it to create a Cache : ercs = new ArrayList(); ercs.add(erc); ec.setEvictionRegionConfigs(erc); config.setEvictionConfig(ec); CacheFactory factory = new DefaultCacheFactory(); Cache cache = factory.createCache(config); ]]> Even the above fairly simple configuration is pretty tedious programming; hence the preferred use of XML-based configuration. However, if your application requires it, there is no reason not to use XML-based configuration for most of the attributes, and then access the Configuration object to programatically change a few items from the defaults, add an eviction region, etc. Note that configuration values may not be changed programmatically when a cache is running, except those annotated as @Dynamic . Dynamic properties are also marked as such in the configuration reference table. Attempting to change a non-dynamic property will result in a ConfigurationException .
Using an IOC Framework The Configuration class and its component parts are all Java Beans that expose all config elements via simple setters and getters. Therefore, any good IOC framework should be able to build up a Configuration from an XML file in the framework's own format. See the deployment via the JBoss micrcontainer section for an example of this.
Composition of a <literal>Configuration</literal> Object A Configuration is composed of a number of subobjects: Following is a brief overview of the components of a Configuration . See the javadoc and the linked chapters in this book for a more complete explanation of the configurations associated with each component. Configuration : top level object in the hierarchy; exposes the configuration properties listed in the configuration reference section of this book. BuddyReplicationConfig : only relevant if buddy replication is used. General buddy replication configuration options. Must include a: BuddyLocatorConfig : implementation-specific configuration object for the BuddyLocator implementation being used. What configuration elements are exposed depends on the needs of the BuddyLocator implementation. EvictionConfig : only relevant if eviction is used. General eviction configuration options. Must include at least one: EvictionRegionConfig : one for each eviction region; names the region, etc. Must include a: EvictionPolicyConfig : implementation-specific configuration object for the EvictionPolicy implementation being used. What configuration elements are exposed depends on the needs of the EvictionPolicy implementation. CacheLoaderConfig : only relevant if a cache loader is used. General cache loader configuration options. Must include at least one: IndividualCacheLoaderConfig : implementation-specific configuration object for the CacheLoader implementation being used. What configuration elements are exposed depends on the needs of the CacheLoader implementation. RuntimeConfig : exposes to cache clients certain information about the cache's runtime environment (e.g. membership in buddy replication groups if buddy replication is used.) Also allows direct injection into the cache of needed external services like a JTA TransactionManager or a JGroups ChannelFactory .
Dynamic Reconfiguration Dynamically changing the configuration of some options while the cache is running is supported, by programmatically obtaining the Configuration object from the running cache and changing values. E.g., A complete listing of which options may be changed dynamically is in the configuration reference section. An org.jboss.cache.config.ConfigurationException will be thrown if you attempt to change a setting that is not dynamic.
Overriding the Configuration Via the Option API The Option API allows you to override certain behaviours of the cache on a per invocation basis. This involves creating an instance of org.jboss.cache.config.Option , setting the options you wish to override on the Option object and passing it in the InvocationContext before invoking your method on the cache. E.g., to override the default node versioning used with optimistic locking: E.g., to suppress replication of a put call in a REPL_SYNC cache: See the javadocs on the Option class for details on the options available.
jbosscache-core-2.2.2.GA/src/main/docbook/userguide/en/modules/eviction_policies.xml0000644000175000017500000006325110765056217030445 0ustar twernertwerner Eviction Policies Eviction policies control JBoss Cache's memory management by managing how many nodes are allowed to be stored in memory and their life spans. Memory constraints on servers mean cache cannot grow indefinitely, so policies need to be in place to restrict the size of the cache. Eviction policies are most often used alongside cache loaders cache loaders .
Configuring Eviction Policies
Basic Configuration The basic eviction policy configuration element looks like: 3 100000 org.jboss.cache.eviction.LRUPolicy 100 250 10 60000 ... ]]> wakeUpIntervalSeconds - this required parameter defines how often the eviction thread runs eventQueueSize - this optional parameter defines the size of the queue which holds eviction events. If your eviction thread does not run often enough, you may need to increase this. This can be overridden on a per-region basis. policyClass - this is required, unless you set individual policyClass attributes on each and every region. This defines the eviction policy to use if one is not defined for a region.
Eviction Regions The concept of regions and the Region class were visited earlier when talking about marshalling. Regions also have another use, in that they are used to define the eviction policy used within the region. In addition to using a region-specific configuration, you can also configure a default, cache-wide eviction policy for nodes that do not fall into predefined regions or if you do not wish to define specific regions. It is important to note that when defining regions using the configuration XML file, all elements of the Fqn that defines the region are java.lang.String objects. Looking at the eviction configuration snippet above, we see that a default region, _default_ , holds attributes which apply to nodes that do not fall into any of the other regions defined. For each region, you can define parameters which affect how the policy which applies to the region chooses to evict nodes. In the example above, the LRUPolicy allows a maxNodes parameter which defines how many nodes can exist in the region before it chooses to start evicting nodes. See the javadocs for each policy for a list of allowed parameters. It also defines a minTimeToLiveSeconds parameter, which defines a minimum time a node must exist in memory before being considered for eviction.
Overlapping Eviction Regions It's possible to define regions that overlap. In other words, one region can be defined for /a/b/c , and another defined for /a/b/c/d (which is just the d subtree of the /a/b/c sub-tree). The algorithm, in order to handle scenarios like this consistently, will always choose the first region it encounters. In this way, if the algorithm needed to decide how to handle /a/b/c/d/e , it would start from there and work its way up the tree until it hits the first defined region - in this case /a/b/c/d .
Resident Nodes Nodes marked as resident (using Node.setResident() API) will be ignored by the eviction policies both when checking whether to trigger the eviction and when proceeding with the actual eviction of nodes. E.g. if a region is configured to have a maximum of 10 nodes, resident nodes won't be counted when deciding whether to evict nodes in that region. In addition, resident nodes will not be considered for eviction when the region's eviction threshold is reached. In order to mark a node as resident the Node.setResident() API should be used. By default, the newly created nodes are not resident. The resident attribute of a node is neither replicated, persisted nor transaction-aware. A sample use case for resident nodes would be ensuring "path" nodes don't add "noise" to an eviction policy. E.g.,: In this example, the nodes /a and /a/b are paths which exist solely to support the existence of node /a/b/c and don't hold any data themselves. As such, they are good candidates for being marked as resident. This would lead to better memory management as no eviction events would be generated when accessing /a and/a/b. N.B. when adding attributes to a resident node, e.g. cache.put("/a", "k", "v") in the above example, it would make sense to mark the nodes as non-resident again and let them be considered for eviction..
Programmatic Configuration Configuring eviction using the Configuration object entails the use of the org.jboss.cache.config.EvictionConfig bean, which is passed into Configuration.setEvictionConfig() . See the chapter on Configuration for more on building a Configuration programatically. The use of simple POJO beans to represent all elements in a cache's configuration also makes it fairly easy to programatically add eviction regions after the cache is started . For example, assume we had an existing cache configured via XML with the EvictionConfig element shown above. Now at runtime we wished to add a new eviction region named "/org/jboss/fifo", using LRUPolicy but a different number of maxNodes :
Shipped Eviction Policies
LRUPolicy - Least Recently Used org.jboss.cache.eviction.LRUPolicy controls both the node lifetime and age. This policy guarantees a constant order ( O (1) ) for adds, removals and lookups (visits). It has the following configuration parameters: maxNodes - This is the maximum number of nodes allowed in this region. 0 denotes no limit. timeToLiveSeconds - The amount of time a node is not written to or read (in seconds) before the node is swept away. 0 denotes no limit. maxAgeSeconds - Lifespan of a node (in seconds) regardless of idle time before the node is swept away. 0 denotes no limit. minTimeToLiveSeconds - the minimum amount of time a node must be allowed to live after being accessed before it is allowed to be considered for eviction. 0 denotes that this feature is disabled, which is the default value.
FIFOPolicy - First In, First Out org.jboss.cache.eviction.FIFOPolicy controls the eviction in a proper first in first out order. This policy guarantees a constant order ( O (1) ) for adds, removals and lookups (visits). It has the following configuration parameters: maxNodes - This is the maximum number of nodes allowed in this region. 0 denotes no limit. minTimeToLiveSeconds - the minimum amount of time a node must be allowed to live after being accessed before it is allowed to be considered for eviction. 0 denotes that this feature is disabled, which is the default value.
MRUPolicy - Most Recently Used org.jboss.cache.eviction.MRUPolicy controls the eviction in based on most recently used algorithm. The most recently used nodes will be the first to evict with this policy. This policy guarantees a constant order ( O (1) ) for adds, removals and lookups (visits). It has the following configuration parameters: maxNodes - This is the maximum number of nodes allowed in this region. 0 denotes no limit. minTimeToLiveSeconds - the minimum amount of time a node must be allowed to live after being accessed before it is allowed to be considered for eviction. 0 denotes that this feature is disabled, which is the default value.
LFUPolicy - Least Frequently Used org.jboss.cache.eviction.LFUPolicy controls the eviction in based on least frequently used algorithm. The least frequently used nodes will be the first to evict with this policy. Node usage starts at 1 when a node is first added. Each time it is visted, the node usage counter increments by 1. This number is used to determine which nodes are least frequently used. LFU is also a sorted eviction algorithm. The underlying EvictionQueue implementation and algorithm is sorted in ascending order of the node visits counter. This class guarantees a constant order ( O (1) ) for adds, removal and searches. However, when any number of nodes are added/visited to the queue for a given processing pass, a single quasilinear ( O (n * log n) ) operation is used to resort the queue in proper LFU order. Similarly if any nodes are removed or evicted, a single linear ( O (n) ) pruning operation is necessary to clean up the EvictionQueue. LFU has the following configuration parameters: maxNodes - This is the maximum number of nodes allowed in this region. 0 denotes no limit. minNodes - This is the minimum number of nodes allowed in this region. This value determines what the eviction queue should prune down to per pass. e.g. If minNodes is 10 and the cache grows to 100 nodes, the cache is pruned down to the 10 most frequently used nodes when the eviction timer makes a pass through the eviction algorithm. minTimeToLiveSeconds - the minimum amount of time a node must be allowed to live after being accessed before it is allowed to be considered for eviction. 0 denotes that this feature is disabled, which is the default value.
ExpirationPolicy org.jboss.cache.eviction.ExpirationPolicy is a policy that evicts nodes based on an absolute expiration time. The expiration time is indicated using the org.jboss.cache.Node.put() method, using a String key expiration and the absolute time as a java.lang.Long object, with a value indicated as milliseconds past midnight January 1st, 1970 UTC (the same relative time as provided by java.lang.System.currentTimeMillis() ). This policy guarantees a constant order ( O (1) ) for adds and removals. Internally, a sorted set (TreeSet) containing the expiration time and Fqn of the nodes is stored, which essentially functions as a heap. This policy has the following configuration parameters: expirationKeyName - This is the Node key name used in the eviction algorithm. The configuration default is expiration . maxNodes - This is the maximum number of nodes allowed in this region. 0 denotes no limit. The following listing shows how the expiration date is indicated and how the policy is applied: Note that the expiration time of nodes is only checked when the region manager wakes up every wakeUpIntervalSeconds , so eviction may happen a few seconds later than indicated.
ElementSizePolicy - Eviction based on number of key/value pairs in a node org.jboss.cache.eviction.ElementSizePolicy controls the eviction in based on the number of key/value pairs in the node. Nodes The most recently used nodes will be the first to evict with this policy. It has the following configuration parameters: maxNodes - This is the maximum number of nodes allowed in this region. 0 denotes no limit. maxElementsPerNode - This is the trigger number of attributes per node for the node to be selected for eviction. 0 denotes no limit. minTimeToLiveSeconds - the minimum amount of time a node must be allowed to live after being accessed before it is allowed to be considered for eviction. 0 denotes that this feature is disabled, which is the default value.
Writing Your Own Eviction Policies
Eviction Policy Plugin Design The design of the JBoss Cache eviction policy framework is based on an EvictionInterceptor to handle cache events and relay them back to the eviction policies. During the cache start up, an EvictionInterceptor will be added to the cache interceptor stack if the eviction policy is specified. Then whenever a node is added, removed, evicted, or visited, the EvictionInterceptor will maintain state statistics and information will be relayed to each individual eviction region. There is a single eviction thread (timer) that will run at a configured interval. This thread will make calls into each of the policy providers and inform it of any aggregated adds, removes and visits (gets) events to the cache during the configured interval. The eviction thread is responsible for kicking off the eviction policy processing (a single pass) for each configured eviction cache region.
Interfaces to implement In order to implement an eviction policy, the following interfaces must be implemented: org.jboss.cache.eviction.EvictionPolicy org.jboss.cache.eviction.EvictionAlgorithm org.jboss.cache.eviction.EvictionQueue org.jboss.cache.config.EvictionPolicyConfig When compounded together, each of these interface implementations define all the underlying mechanics necessary for a complete eviction policy implementation. Note that: The EvictionPolicyConfig implementation should maintain getter and setter methods for whatever configuration properties the policy supports (e.g. for LRUConfiguration among others there is a int getMaxNodes() and a setMaxNodes(int) ). When the "EvictionConfig" section of an XML configuration is parsed, these properties will be set by reflection. Alternatively, the implementation of a new eviction policy provider can be simplified by extending BaseEvictionPolicy and BaseEvictionAlgorithm . Or for properly sorted EvictionAlgorithms (sorted in eviction order - see LFUAlgorithm ) extending BaseSortedEvictionAlgorithm and implementing SortedEvictionQueue takes care of most of the common functionality available in a set of eviction policy provider classes Note that: The BaseEvictionAlgorithm class maintains a processing structure. It will process the ADD, REMOVE, and VISIT events queued by the region first. It also maintains an collection of items that were not properly evicted during the last go around because of held locks. That list is pruned. Finally, the EvictionQueue itself is pruned for entries that should be evicted based upon the configured eviction rules for the region. The BaseSortedEvictionAlgorithm class will maintain a boolean through the algorithm processing that will determine if any new nodes were added or visited. This allows the Algorithm to determine whether to resort the eviction queue items (in first to evict order) or to skip the potentially expensive sorting if there have been no changes to the cache in this region. The SortedEvictionQueue interface defines the contract used by the BaseSortedEvictionAlgorithm abstract class that is used to resort the underlying queue. Again, the queue sorting should be sorted in first to evict order. The first entry in the list should evict before the last entry in the queue. The last entry in the queue should be the last entry that will require eviction.
jbosscache-core-2.2.2.GA/src/main/docbook/userguide/en/modules/transactions.xml0000644000175000017500000004335110765056217027445 0ustar twernertwerner Transactions and Concurrency
Concurrent Access JBoss Cache is a thread safe caching API, and uses its own efficient mechanisms of controlling concurrent access. It uses a pessimistic locking scheme by default for this purpose. Optimistic locking may alternatively be used, and is discussed later.
Locks Locking is done internally, on a node-level. For example when we want to access "/a/b/c", a lock will be acquired for nodes "a", "b" and "c". When the same transaction wants to access "/a/b/c/d", since we already hold locks for "a", "b" and "c", we only need to acquire a lock for "d". Lock owners are either transactions (call is made within the scope of an existing transaction) or threads (no transaction associated with the call). Regardless, a transaction or a thread is internally transformed into an instance of GlobalTransaction , which is used as a globally unique identifier for modifications across a cluster. E.g. when we run a two-phase commit protocol across the cluster, the GlobalTransaction uniquely identifies a unit of work across a cluster. Locks can be read or write locks. Write locks serialize read and write access, whereas read-only locks only serialize read access. When a write lock is held, no other write or read locks can be acquired. When a read lock is held, others can acquire read locks. However, to acquire write locks, one has to wait until all read locks have been released. When scheduled concurrently, write locks always have precedence over read locks. Note that (if enabled) read locks can be upgraded to write locks. Using read-write locks helps in the following scenario: consider a tree with entries "/a/b/n1" and "/a/b/n2". With write-locks, when Tx1 accesses "/a/b/n1", Tx2 cannot access "/a/b/n2" until Tx1 has completed and released its locks. However, with read-write locks this is possible, because Tx1 acquires read-locks for "/a/b" and a read-write lock for "/a/b/n1". Tx2 is then able to acquire read-locks for "/a/b" as well, plus a read-write lock for "/a/b/n2". This allows for more concurrency in accessing the cache.
Pessimistic locking By default, JBoss Cache uses pessimistic locking. Locking is not exposed directly to user. Instead, a transaction isolation level which provides different locking behaviour is configurable.
Isolation levels JBoss Cache supports the following transaction isolation levels, analogous to database ACID isolation levels. A user can configure an instance-wide isolation level of NONE, READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, or SERIALIZABLE. REPEATABLE_READ is the default isolation level used. NONE. No transaction support is needed. There is no locking at this level, e.g., users will have to manage the data integrity. Implementations use no locks. READ_UNCOMMITTED. Data can be read anytime while write operations are exclusive. Note that this level doesn't prevent the so-called "dirty read" where data modified in Tx1 can be read in Tx2 before Tx1 commits. In other words, if you have the following sequence, using this isolation level will not prevent Tx2 read operation. Implementations typically use an exclusive lock for writes while reads don't need to acquire a lock. READ_COMMITTED. Data can be read any time as long as there is no write. This level prevents the dirty read. But it doesn’t prevent the so-called ‘non-repeatable read’ where one thread reads the data twice can produce different results. For example, if you have the following sequence, where the second read in Tx1 thread will produce different result. Implementations usually use a read-write lock; reads succeed acquiring the lock when there are only reads, writes have to wait until there are no more readers holding the lock, and readers are blocked acquiring the lock until there are no more writers holding the lock. Reads typically release the read-lock when done, so that a subsequent read to the same data has to re-acquire a read-lock; this leads to nonrepeatable reads, where 2 reads of the same data might return different values. Note that, the write only applies regardless of transaction state (whether it has been committed or not). REPEATABLE_READ. Data can be read while there is no write and vice versa. This level prevents "non-repeatable read" but it does not completely prevent the so-called "phantom read" where new data can be inserted into the tree from another transaction. Implementations typically use a read-write lock. This is the default isolation level used. SERIALIZABLE. Data access is synchronized with exclusive locks. Only 1 writer or reader can have the lock at any given time. Locks are released at the end of the transaction. Regarded as very poor for performance and thread/transaction concurrency.
Insertion and Removal of Nodes By default, before inserting a new node into the tree or removing an existing node from the tree, JBoss Cache will only attempt to acquire a read lock on the new node's parent node. This approach does not treat child nodes as an integral part of a parent node's state. This approach allows greater concurrency if nodes are frequently added or removed, but at a cost of lesser correctness. For use cases where greater correctness is necessary, JBoss Cache provides a configuration option LockParentForChildInsertRemove . If this is set to true , insertions and removals of child nodes require the acquisition of a write lock on the parent node. In addition to the above, in version 2.1.0 and above, JBoss Cache offers the ability to override this configuration on a per-node basis. See Node.setLockForChildInsertRemove() and it's corresponding javadocs for details.
Optimistic Locking The motivation for optimistic locking is to improve concurrency. When a lot of threads have a lot of contention for access to the data tree, it can be inefficient to lock portions of the tree - for reading or writing - for the entire duration of a transaction as we do in pessimistic locking. Optimistic locking allows for greater concurrency of threads and transactions by using a technique called data versioning, explained here. Note that isolation levels (if configured) are ignored if optimistic locking is enabled.
Architecture Optimistic locking treats all method calls as transactional Because of this requirement, you must always have a transaction manager configured when using optimistic locking. . Even if you do not invoke a call within the scope of an ongoing transaction, JBoss Cache creates an implicit transaction and commits this transaction when the invocation completes. Each transaction maintains a transaction workspace, which contains a copy of the data used within the transaction. For example, if a transaction calls cache.getRoot().getChild( Fqn.fromString("/a/b/c") ) , nodes a, b and c are copied from the main data tree and into the workspace. The data is versioned and all calls in the transaction work on the copy of the data rather than the actual data. When the transaction commits, its workspace is merged back into the underlying tree by matching versions. If there is a version mismatch - such as when the actual data tree has a higher version than the workspace, perhaps if another transaction were to access the same data, change it and commit before the first transaction can finish - the transaction throws a RollbackException when committing and the commit fails. Optimistic locking uses the same locks we speak of above, but the locks are only held for a very short duration - at the start of a transaction to build a workspace, and when the transaction commits and has to merge data back into the tree. So while optimistic locking may occasionally fail if version validations fail or may run slightly slower than pessimistic locking due to the inevitable overhead and extra processing of maintaining workspaces, versioned data and validating on commit, it does buy you a near-SERIALIZABLE degree of data integrity while maintaining a very high level of concurrency.
Data Versioning Optimistic locking makes use of the DataVersion interface (and an internal and default DefaultDataVersion implementation to keep a track of node versioning. In certain cases, where cached data is an in-memory representation of data from an external source such as a database, it makes sense to align the versions used in JBoss Cache with the versions used externally. As such, using the options API , it is possible to set the DataVersion you wish to use on a per-invocation basis, allowing you to implement the DataVersion interface to hold the versioning information obtained externally before putting your data into the cache.
Configuration Optimistic locking is enabled by using the NodeLockingScheme XML attribute, and setting it to "OPTIMISTIC": OPTIMISTIC ... ]]> It is generally advisable that if you have an eviction policy defined along with optimistic locking, you define the eviction policy's minTimeToLiveSeconds parameter to be slightly greater than the transaction timeout value set in your transaction manager. This ensures that data versions in the cache are not evicted while transactions are in progress See JBCACHE-1155 .
Transactional Support JBoss Cache can be configured to use and participate in JTA compliant transactions. Alternatively, if transaction support is disabled, it is equivalent to setting AutoCommit to on where modifications are potentially Depending on whether interval-based asynchronous replication is used replicated after every change (if replication is enabled). What JBoss Cache does on every incoming call is: Retrieve the current javax.transaction.Transaction associated with the thread If not already done, register a javax.transaction.Synchronization with the transaction manager to be notified when a transaction commits or is rolled back. In order to do this, the cache has to be provided with a reference to environment's javax.transaction.TransactionManager . This is usually done by configuring the cache with the class name of an implementation of the TransactionManagerLookup interface. When the cache starts, it will create an instance of this class and invoke its getTransactionManager() method, which returns a reference to the TransactionManager . JBoss Cache ships with JBossTransactionManagerLookup and GenericTransactionManagerLookup . The JBossTransactionManagerLookup is able to bind to a running JBoss AS instance and retrieve a TransactionManager while the GenericTransactionManagerLookup is able to bind to most popular Java EE application servers and provide the same functionality. A dummy implementation - DummyTransactionManagerLookup - is also provided, primarily for unit tests. Being a dummy, this is just for demo and testing purposes and is not recommended for production use. An alternative to configuring a TransactionManagerLookup is to programatically inject a reference to the TransactionManager into the Configuration object's RuntimeConfig element: Injecting the TransactionManager is the recommended approach when the Configuration is built by some sort of IOC container that already has a reference to the TM. When the transaction commits, we initiate either a one- two-phase commit protocol. See replicated caches and transactions for details.
jbosscache-core-2.2.2.GA/src/main/docbook/userguide/en/modules/architecture.xml0000644000175000017500000004617710660354341027421 0ustar twernertwerner Architecture
Data Structures Within The Cache A Cache consists of a collection of Node instances, organised in a tree structure. Each Node contains a Map which holds the data objects to be cached. It is important to note that the structure is a mathematical tree, and not a graph; each Node has one and only one parent, and the root node is denoted by the constant fully qualitied name, Fqn.ROOT . The reason for organising nodes as such is to improve concurrent access to data and make replication and persistence more fine-grained.
Data structured as a tree
In the diagram above, each box represents a JVM. You see 2 caches in separate JVMs, replicating data to each other. These VMs can be located on the same physical machine, or on 2 different machines connected by a network link. The underlying group communication between networked nodes is done using JGroups .
Any modifications (see API chapter ) in one cache instance will be replicated to the other cache. Naturally, you can have more than 2 caches in a cluster. Depending on the transactional settings, this replication will occur either after each modification or at the end of a transaction, at commit time. When a new cache is created, it can optionally acquire the contents from one of the existing caches on startup.
SPI Interfaces In addition to Cache and Node interfaces, JBoss Cache exposes more powerful CacheSPI and NodeSPI interfaces, which offer more control over the internals of JBoss Cache. These interfaces are not intended for general use, but are designed for people who wish to extend and enhance JBoss Cache, or write custom Interceptor or CacheLoader instances.
SPI Interfaces
The CacheSPI interface cannot be created, but is injected into Interceptor and CacheLoader implementations by the setCache(CacheSPI cache) methods on these interfaces. CacheSPI extends Cache so all the functionality of the basic API is made available. Similarly, a NodeSPI interface cannot be created. Instead, one is obtained by performing operations on CacheSPI , obtained as above. For example, Cache.getRoot() : Node is overridden as CacheSPI.getRoot() : NodeSPI . It is important to note that directly casting a Cache or Node to it's SPI counterpart is not recommended and is bad practice, since the inheritace of interfaces it is not a contract that is guaranteed to be upheld moving forward. The exposed public APIs, on the other hand, is guaranteed to be upheld.
Method Invocations On Nodes Since the cache is essentially a collection of nodes, aspects such as clustering, persistence, eviction, etc. need to be applied to these nodes when operations are invoked on the cache as a whole or on individual nodes. To achieve this in a clean, modular and extensible manner, an interceptor chain is used. The chain is built up of a series of interceptors, each one adding an aspect or particular functionality. The chain is built when the cache is created, based on the configuration used. It is important to note that the NodeSPI offers some methods (such as the xxxDirect() method family) that operate on a node directly without passing through the interceptor stack. Plugin authors should note that using such methods will affect the aspects of the cache that may need to be applied, such as locking, replication, etc. Basically, don't use such methods unless you really know what you're doing!
Interceptors An Interceptor is an abstract class, several of which comprise an interceptor chain. It exposes an invoke() method, which must be overridden by implementing classes to add behaviour to a call before passing the call down the chain by calling super.invoke() .
SPI Interfaces
JBoss Cache ships with several interceptors, representing different configuration options, some of which are: TxInterceptor - looks for ongoing transactions and registers with transaction managers to participate in synchronization events ReplicationInterceptor - replicates state across a cluster using a JGroups channel CacheLoaderInterceptor - loads data from a persistent store if the data requested is not available in memory The interceptor chain configured for your cache instance can be obtained and inspected by calling CacheSPI.getInterceptorChain() , which returns an ordered List of interceptors.
Writing Custom Interceptors Custom interceptors to add specific aspects or features can be written by extending Interceptor and overriding invoke() . The custom interceptor will need to be added to the interceptor chain by using the CacheSPI.addInterceptor() method. Adding custom interceptors via XML is not supported at this time.
MethodCalls org.jboss.cache.marshall.MethodCall is a class that encapsulates a java.lang.reflection.Method and an Object[] representing the method's arguments. It is an extension of the org.jgroups.blocks.MethodCall class, that adds a mechanism for identifying known methods using magic numbers and method ids, which makes marshalling and unmarshalling more efficient and performant. This is central to the Interceptor architecture, and is the only parameter passed in to Interceptor.invoke() .
InvocationContexts InvocationContext holds intermediate state for the duration of a single invocation, and is set up and destroyed by the InvocationContextInterceptor which sits at the start of the chain. InvocationContext , as its name implies, holds contextual information associated with a single cache method invocation. Contextual information includes associated javax.transaction.Transaction or org.jboss.cache.transaction.GlobalTransaction , method invocation origin ( InvocationContext.isOriginLocal() ) as well as Option overrides . The InvocationContext can be obtained by calling Cache.getInvocationContext() .
Managers For Subsystems Some aspects and functionality is shared by more than a single interceptor. Some of these have been encapsulated into managers, for use by various interceptors, and are made available by the CacheSPI interface.
RpcManager This class is responsible for calls made via the JGroups channel for all RPC calls to remote caches, and encapsulates the JGroups channel used.
BuddyManager This class manages buddy groups and invokes group organisation remote calls to organise a cluster of caches into smaller sub-groups.
CacheLoaderManager Sets up and configures cache loaders. This class wraps individual CacheLoader instances in delegating classes, such as SingletonStoreCacheLoader or AsyncCacheLoader , or may add the CacheLoader to a chain using the ChainingCacheLoader .
Marshalling And Wire Formats Early versions of JBoss Cache simply wrote cached data to the network by writing to an ObjectOutputStream during replication. Over various releases in the JBoss Cache 1.x.x series this approach was gradually deprecated in favour of a more mature marshalling framework. In the JBoss Cache 2.x.x series, this is the only officially supported and recommended mechanism for writing objects to datastreams.
The Marshaller interface
The Marshaller Interface The Marshaller interface extends RpcDispatcher.Marshaller from JGroups. This interface has two main implementations - a delegating VersionAwareMarshaller and a concrete CacheMarshaller200 . The marshaller can be obtained by calling CacheSPI.getMarshaller() , and defaults to the VersionAwareMarshaller . Users may also write their own marshallers by implementing the Marshaller interface and adding it to their configuration, by using the MarshallerClass configuration attribute.
VersionAwareMarshaller As the name suggests, this marshaller adds a version short to the start of any stream when writing, enabling similar VersionAwareMarshaller instances to read the version short and know which specific marshaller implementation to delegate the call to. For example, CacheMarshaller200 , is the marshaller for JBoss Cache 2.0.x. JBoss Cache 2.1.x, say, may ship with CacheMarshaller210 with an improved wire protocol. Using a VersionAwareMarshaller helps achieve wire protocol compatibility between minor releases but still affords us the flexibility to tweak and improve the wire protocol between minor or micro releases.
CacheLoaders Some of the existing cache loaders, such as the JDBCCacheLoader and the FileCacheLoader relied on persisting data using ObjectOutputStream as well, but now, they are using the VersionAwareMarshaller to marshall the persisted data to their cache stores.
CacheMarshaller200 This marshaller treats well-known objects that need marshalling - such as MethodCall , Fqn , DataVersion , and even some JDK objects such as String , List , Boolean and others as types that do not need complete class definitions. Instead, each of these well-known types are represented by a short , which is a lot more efficient. In addition, reference counting is done to reduce duplication of writing certain objects multiple times, to help keep the streams small and efficient. Also, if UseRegionBasedMarshalling is enabled (disabled by default) the marshaller adds region information to the stream before writing any data. This region information is in the form of a String representation of an Fqn . When unmarshalling, the RegionManager can be used to find the relevant Region , and use a region-specific ClassLoader to unmarshall the stream. This is specifically useful when used to cluster state for application servers, where each deployment has it's own ClassLoader . See the section below on regions for more information.
Class Loading and Regions When used to cluster state of application servers, applications deployed in the application tend to put instances of objects specific to their application in the cache (or in an HttpSession object) which would require replication. It is common for application servers to assign separate ClassLoader instances to each application deployed, but have JBoss Cache libraries referenced by the application server's ClassLoader . To enable us to successfully marshall and unmarshall objects from such class loaders, we use a concept called regions. A region is a portion of the cache which share a common class loader (a region also has other uses - see eviction policies ). A region is created by using the Cache.getRegion(Fqn fqn, boolean createIfNotExists) method, and returns an implementation of the Region interface. Once a region is obtained, a class loader for the region can be set or unset, and the region can be activated/deactivated. By default, regions are active unless the InactiveOnStartup configuration attribute is set to true .
jbosscache-core-2.2.2.GA/src/main/docbook/userguide/en/modules/jmx_reference.xml0000644000175000017500000003052410660354341027540 0ustar twernertwerner JMX References
JBoss Cache Statistics The following table describes the statistics currently available and may be collected via JMX. JBoss Cache Management Statistics MBean Name Attribute Type Description ActivationInterceptor Activations long Number of passivated nodes that have been activated. CacheLoaderInterceptor CacheLoaderLoads long Number of nodes loaded through a cache loader. CacheLoaderInterceptor CacheLoaderMisses long Number of unsuccessful attempts to load a node through a cache loader. CacheMgmtInterceptor Hits long Number of successful attribute retrievals. CacheMgmtInterceptor Misses long Number of unsuccessful attribute retrievals. CacheMgmtInterceptor Stores long Number of attribute store operations. CacheMgmtInterceptor Evictions long Number of node evictions. CacheMgmtInterceptor NumberOfAttributes int Number of attributes currently cached. CacheMgmtInterceptor NumberOfNodes int Number of nodes currently cached. CacheMgmtInterceptor ElapsedTime long Number of seconds that the cache has been running. CacheMgmtInterceptor TimeSinceReset long Number of seconds since the cache statistics have been reset. CacheMgmtInterceptor AverageReadTime long Average time in milliseconds to retrieve a cache attribute, including unsuccessful attribute retrievals. CacheMgmtInterceptor AverageWriteTime long Average time in milliseconds to write a cache attribute. CacheMgmtInterceptor HitMissRatio double Ratio of hits to hits and misses. A hit is a get attribute operation that results in an object being returned to the client. The retrieval may be from a cache loader if the entry isn't in the local cache. CacheMgmtInterceptor ReadWriteRatio double Ratio of read operations to write operations. This is the ratio of cache hits and misses to cache stores. CacheStoreInterceptor CacheLoaderStores long Number of nodes written to the cache loader. InvalidationInterceptor Invalidations long Number of cached nodes that have been invalidated. PassivationInterceptor Passivations long Number of cached nodes that have been passivated. TxInterceptor Prepares long Number of transaction prepare operations performed by this interceptor. TxInterceptor Commits long Number of transaction commit operations performed by this interceptor. TxInterceptor Rollbacks long Number of transaction rollbacks operations performed by this interceptor.
JMX MBean Notifications The following table depicts the JMX notifications available for JBoss Cache as well as the cache events to which they correspond. These are the notifications that can be received through the CacheJmxWrapper MBean. Each notification represents a single event published by JBoss Cache and provides user data corresponding to the parameters of the event. JBoss Cache MBean Notifications Notification Type Notification Data CacheListener Event org.jboss.cache.CacheStarted String : cache service name cacheStarted org.jboss.cache.CacheStopped String : cache service name cacheStopped org.jboss.cache.NodeCreated String : fqn NodeCreated org.jboss.cache.NodeEvicted String : fqn NodeEvicted org.jboss.cache.NodeLoaded String : fqn NodeLoaded org.jboss.cache.NodeModifed String : fqn NodeModifed org.jboss.cache.NodeRemoved String : fqn NodeRemoved org.jboss.cache.NodeVisited String : fqn NodeVisited org.jboss.cache.ViewChange String : view ViewChange org.jboss.cache.NodeActivate Object[0]=String: fqn Object[1]=Boolean: pre NodeActivate org.jboss.cache.NodeEvict Object[0]=String: fqn Object[1]=Boolean: pre NodeEvict org.jboss.cache.NodeModify Object[0]=String: fqn Object[1]=Boolean: pre Object[2]=Boolean: isLocal NodeModify org.jboss.cache.NodePassivate Object[0]=String: fqn Object[1]=Boolean: pre NodePassivate org.jboss.cache.NodeRemove Object[0]=String: fqn Object[1]=Boolean: pre Object[2]=Boolean: isLocal NodeRemove
jbosscache-core-2.2.2.GA/src/main/docbook/userguide/en/modules/replication.xml0000644000175000017500000010200510765056217027236 0ustar twernertwerner Clustering This chapter talks about aspects around clustering JBoss Cache.
Cache Replication Modes JBoss Cache can be configured to be either local (standalone) or clustered. If in a cluster, the cache can be configured to replicate changes, or to invalidate changes. A detailed discussion on this follows.
Local Mode Local caches don't join a cluster and don't communicate with other caches in a cluster. Therefore their elements don't need to be serializable - however, we recommend making them serializable, enabling a user to change the cache mode at any time. The dependency on the JGroups library is still there, although a JGroups channel is not started.
Replicated Caches Replicated caches replicate all changes to some or all of the other cache instances in the cluster. Replication can either happen after each modification (no transactions), or at the end of a transaction (commit time). Replication can be synchronous or asynchronous . Use of either one of the options is application dependent. Synchronous replication blocks the caller (e.g. on a put() ) until the modifications have been replicated successfully to all nodes in a cluster. Asynchronous replication performs replication in the background (the put() returns immediately). JBoss Cache also offers a replication queue, where modifications are replicated periodically (i.e. interval-based), or when the queue size exceeds a number of elements, or a combination thereof. Asynchronous replication is faster (no caller blocking), because synchronous replication requires acknowledgments from all nodes in a cluster that they received and applied the modification successfully (round-trip time). However, when a synchronous replication returns successfully, the caller knows for sure that all modifications have been applied to all cache instances, whereas this is not be the case with asynchronous replication. With asynchronous replication, errors are simply written to a log. Even when using transactions, a transaction may succeed but replication may not succeed on all cache instances.
Replicated Caches and Transactions When using transactions, replication only occurs at the transaction boundary - i.e., when a transaction commits. This results in minimising replication traffic since a single modification is broadcast rather than a series of individual modifications, and can be a lot more efficient than not using transactions. Another effect of this is that if a transaction were to roll back, nothing is broadcast across a cluster. Depending on whether you are running your cluster in asynchronous or synchronous mode, JBoss Cache will use either a single phase or two phase commit protocol, respectively.
One Phase Commits Used when your cache mode is REPL_ASYNC. All modifications are replicated in a single call, which instructs remote caches to apply the changes to their local in-memory state and commit locally. Remote errors/rollbacks are never fed back to the originator of the transaction since the communication is asynchronous.
Two Phase Commits Used when your cache mode is REPL_SYNC. Upon committing your transaction, JBoss Cache broadcasts a prepare call, which carries all modifications relevant to the transaction. Remote caches then acquire local locks on their in-memory state and apply the modifications. Once all remote caches respond to the prepare call, the originator of the transaction broadcasts a commit. This instructs all remote caches to commit their data. If any of the caches fail to respond to the prepare phase, the originator broadcasts a rollback. Note that although the prepare phase is synchronous, the commit and rollback phases are asynchronous. This is because Sun's JTA specification does not specify how transactional resources should deal with failures at this stage of a transaction; and other resources participating in the transaction may have indeterminate state anyway. As such, we do away with the overhead of synchronous communication for this phase of the transaction. That said, they can be forced to be synchronous using the SyncCommitPhase and SyncRollbackPhase configuration attributes.
Buddy Replication Buddy Replication allows you to suppress replicating your data to all instances in a cluster. Instead, each instance picks one or more 'buddies' in the cluster, and only replicates to these specific buddies. This greatly helps scalability as there is no longer a memory and network traffic impact every time another instance is added to a cluster. One of the most common use cases of Buddy Replication is when a replicated cache is used by a servlet container to store HTTP session data. One of the pre-requisites to buddy replication working well and being a real benefit is the use of session affinity , more casually known as sticky sessions in HTTP session replication speak. What this means is that if certain data is frequently accessed, it is desirable that this is always accessed on one instance rather than in a round-robin fashion as this helps the cache cluster optimise how it chooses buddies, where it stores data, and minimises replication traffic. If this is not possible, Buddy Replication may prove to be more of an overhead than a benefit.
Selecting Buddies
BuddyLocator
Buddy Replication uses an instance of a BuddyLocator which contains the logic used to select buddies in a network. JBoss Cache currently ships with a single implementation, NextMemberBuddyLocator , which is used as a default if no implementation is provided. The NextMemberBuddyLocator selects the next member in the cluster, as the name suggests, and guarantees an even spread of buddies for each instance. The NextMemberBuddyLocator takes in 2 parameters, both optional. numBuddies - specifies how many buddies each instance should pick to back its data onto. This defaults to 1. ignoreColocatedBuddies - means that each instance will try to select a buddy on a different physical host. If not able to do so though, it will fall back to colocated instances. This defaults to true .
BuddyPools Also known as replication groups , a buddy pool is an optional construct where each instance in a cluster may be configured with a buddy pool name. Think of this as an 'exclusive club membership' where when selecting buddies, BuddyLocator s that support buddy pools would try and select buddies sharing the same buddy pool name. This allows system administrators a degree of flexibility and control over how buddies are selected. For example, a sysadmin may put two instances on two separate physical servers that may be on two separate physical racks in the same buddy pool. So rather than picking an instance on a different host on the same rack, BuddyLocator s would rather pick the instance in the same buddy pool, on a separate rack which may add a degree of redundancy.
Failover In the unfortunate event of an instance crashing, it is assumed that the client connecting to the cache (directly or indirectly, via some other service such as HTTP session replication) is able to redirect the request to any other random cache instance in the cluster. This is where a concept of Data Gravitation comes in. Data Gravitation is a concept where if a request is made on a cache in the cluster and the cache does not contain this information, it asks other instances in the cluster for the data. In other words, data is lazily transferred, migrating only when other nodes ask for it. This strategy prevents a network storm effect where lots of data is pushed around healthy nodes because only one (or a few) of them die. If the data is not found in the primary section of some node, it would (optionally) ask other instances to check in the backup data they store for other caches. This means that even if a cache containing your session dies, other instances will still be able to access this data by asking the cluster to search through their backups for this data. Once located, this data is transferred to the instance which requested it and is added to this instance's data tree. The data is then (optionally) removed from all other instances (and backups) so that if session affinity is used, the affinity should now be to this new cache instance which has just taken ownership of this data. Data Gravitation is implemented as an interceptor. The following (all optional) configuration properties pertain to data gravitation. dataGravitationRemoveOnFind - forces all remote caches that own the data or hold backups for the data to remove that data, thereby making the requesting cache the new data owner. This removal, of course, only happens after the new owner finishes replicating data to its buddy. If set to false an evict is broadcast instead of a remove, so any state persisted in cache loaders will remain. This is useful if you have a shared cache loader configured. Defaults to true . dataGravitationSearchBackupTrees - Asks remote instances to search through their backups as well as main data trees. Defaults to true . The resulting effect is that if this is true then backup nodes can respond to data gravitation requests in addition to data owners. autoDataGravitation - Whether data gravitation occurs for every cache miss. By default this is set to false to prevent unnecessary network calls. Most use cases will know when it may need to gravitate data and will pass in an Option to enable data gravitation on a per-invocation basis. If autoDataGravitation is true this Option is unnecessary.
Configuration true org.jboss.cache.buddyreplication.NextMemberBuddyLocator numBuddies = 1 ignoreColocatedBuddies = true myBuddyPoolReplicationGroup 2000 true true false ]]>
Invalidation If a cache is configured for invalidation rather than replication, every time data is changed in a cache other caches in the cluster receive a message informing them that their data is now stale and should be evicted from memory. Invalidation, when used with a shared cache loader (see chapter on Cache Loaders) would cause remote caches to refer to the shared cache loader to retrieve modified data. The benefit of this is twofold: network traffic is minimised as invalidation messages are very small compared to replicating updated data, and also that other caches in the cluster look up modified data in a lazy manner, only when needed. Invalidation messages are sent after each modification (no transactions), or at the end of a transaction, upon successful commit. This is usually more efficient as invalidation messages can be optimised for the transaction as a whole rather than on a per-modification basis. Invalidation too can be synchronous or asynchronous, and just as in the case of replication, synchronous invalidation blocks until all caches in the cluster receive invalidation messages and have evicted stale data while asynchronous invalidation works in a 'fire-and-forget' mode, where invalidation messages are broadcast but doesn't block and wait for responses.
State Transfer State Transfer refers to the process by which a JBoss Cache instance prepares itself to begin providing a service by acquiring the current state from another cache instance and integrating that state into its own state.
State Transfer Types There are three divisions of state transfer types depending on a point of view related to state transfer. First, in the context of particular state transfer implementation, the underlying plumbing, there are two starkly different state transfer types: byte array and streaming based state transfer. Second, state transfer can be full or partial state transfer depending on a subtree being transferred. Entire cache tree transfer represents full transfer while transfer of a particular subtree represents partial state transfer. And finally state transfer can be "in-memory" and "persistent" transfer depending on a particular use of cache.
Byte array and streaming based state transfer Byte array based transfer was a default and only transfer methodology for cache in all previous releases up to 2.0. Byte array based transfer loads entire state transferred into a byte array and sends it to a state receiving member. Major limitation of this approach is that the state transfer that is very large (>1GB) would likely result in OutOfMemoryException. Streaming state transfer provides an InputStream to a state reader and an OutputStream to a state writer. OutputStream and InputStream abstractions enable state transfer in byte chunks thus resulting in smaller memory requirements. For example, if application state is represented as a tree whose aggregate size is 1GB, rather than having to provide a 1GB byte array streaming state transfer transfers the state in chunks of N bytes where N is user configurable. Byte array and streaming based state transfer are completely API transparent, interchangeable, and statically configured through a standard cache configuration XML file. Refer to JGroups documentation on how to change from one type of transfer to another.
Full and partial state transfer If either in-memory or persistent state transfer is enabled, a full or partial state transfer will be done at various times, depending on how the cache is used. "Full" state transfer refers to the transfer of the state related to the entire tree -- i.e. the root node and all nodes below it. A "partial" state transfer is one where just a portion of the tree is transferred -- i.e. a node at a given Fqn and all nodes below it. If either in-memory or persistent state transfer is enabled, state transfer will occur at the following times: Initial state transfer. This occurs when the cache is first started (as part of the processing of the start() method). This is a full state transfer. The state is retrieved from the cache instance that has been operational the longest. The longest operating cache instance is always, in JGroups terms, the coordinator. If there is any problem receiving or integrating the state, the cache will not start. Initial state transfer will occur unless: The cache's InactiveOnStartup property is true . This property is used in conjunction with region-based marshalling. Buddy replication is used. See below for more on state transfer with buddy replication. Partial state transfer following region activation. When region-based marshalling is used, the application needs to register a specific class loader with the cache. This class loader is used to unmarshall the state for a specific region (subtree) of the cache. After registration, the application calls cache.getRegion(fqn, true).activate() , which initiates a partial state transfer of the relevant subtree's state. The request is first made to the oldest cache instance in the cluster. However, if that instance responds with no state, it is then requested from each instance in turn until one either provides state or all instances have been queried. Typically when region-based marshalling is used, the cache's InactiveOnStartup property is set to true . This suppresses initial state transfer, which would fail due to the inability to deserialize the transferred state. Buddy replication. When buddy replication is used, initial state transfer is disabled. Instead, when a cache instance joins the cluster, it becomes the buddy of one or more other instances, and one or more other instances become its buddy. Each time an instance determines it has a new buddy providing backup for it, it pushes it's current state to the new buddy. This "pushing" of state to the new buddy is slightly different from other forms of state transfer, which are based on a "pull" approach (i.e. recipient asks for and receives state). However, the process of preparing and integrating the state is the same. This "push" of state upon buddy group formation only occurs if the InactiveOnStartup property is set to false . If it is true , state transfer amongst the buddies only occurs when the application activates the region on the various members of the group. Partial state transfer following a region activation call is slightly different in the buddy replication case as well. Instead of requesting the partial state from one cache instance, and trying all instances until one responds, with buddy replication the instance that is activating a region will request partial state from each instance for which it is serving as a backup.
Transient ("in-memory") and persistent state transfer The state that is acquired and integrated can consist of two basic types: "Transient" or "in-memory" state. This consists of the actual in-memory state of another cache instance - the contents of the various in-memory nodes in the cache that is providing state are serialized and transferred; the recipient deserializes the data, creates corresponding nodes in its own in-memory tree, and populates them with the transferred data. "In-memory" state transfer is enabled by setting the cache's FetchInMemoryState configuration attribute to true . "Persistent" state. Only applicable if a non-shared cache loader is used. The state stored in the state-provider cache's persistent store is deserialized and transferred; the recipient passes the data to its own cache loader, which persists it to the recipient's persistent store. "Persistent" state transfer is enabled by setting a cache loader's fetchPersistentState attribute to true . If multiple cache loaders are configured in a chain, only one can have this property set to true; otherwise you will get an exception at startup. Persistent state transfer with a shared cache loader does not make sense, as the same persistent store that provides the data will just end up receiving it. Therefore, if a shared cache loader is used, the cache will not allow a persistent state transfer even if a cache loader has fetchPersistentState set to true . Which of these types of state transfer is appropriate depends on the usage of the cache. If a write-through cache loader is used, the current cache state is fully represented by the persistent state. Data may have been evicted from the in-memory state, but it will still be in the persistent store. In this case, if the cache loader is not shared, persistent state transfer is used to ensure the new cache has the correct state. In-memory state can be transferred as well if the desire is to have a "hot" cache -- one that has all relevant data in memory when the cache begins providing service. (Note that the ]]> element in the CacheLoaderConfig configuration parameter can be used as well to provide a "warm" or "hot" cache without requiring an in-memory state transfer. This approach somewhat reduces the burden on the cache instance providing state, but increases the load on the persistent store on the recipient side.) If a cache loader is used with passivation, the full representation of the state can only be obtained by combining the in-memory (i.e. non-passivated) and persistent (i.e. passivated) states. Therefore an in-memory state transfer is necessary. A persistent state transfer is necessary if the cache loader is not shared. If no cache loader is used and the cache is solely a write-aside cache (i.e. one that is used to cache data that can also be found in a persistent store, e.g. a database), whether or not in-memory state should be transferred depends on whether or not a "hot" cache is desired.
Configuring State Transfer To ensure state transfer behaves as expected, it is important that all nodes in the cluster are configured with the same settings for persistent and transient state. This is because byte array based transfers, when requested, rely only on the requester's configuration while stream based transfers rely on both the requester and sender's configuration, and this is expected to be identical.
jbosscache-core-2.2.2.GA/src/main/docbook/userguide/en/modules/deployment.xml0000644000175000017500000010547111017331556027110 0ustar twernertwerner Deploying JBoss Cache
Standalone Use / Programatic Deployment When used in a standalone Java program, all that needs to be done is to instantiate the cache using the CacheFactory and a Configuration instance or an XML file, as discussed in the User API and Configuration chapters. The same techniques can be used when an application running in an application server wishes to programatically deploy a cache rather than relying on an application server's deployment features. An example of this would be a webapp deploying a cache via a javax.servlet.ServletContextListener . If, after deploying your cache you wish to expose a management interface to it in JMX, see the section on Programatic Registration in JMX .
JMX-Based Deployment in JBoss AS (JBoss AS 5.x and 4.x) If JBoss Cache is run in JBoss AS then the cache can be deployed as an MBean simply by copying a standard cache configuration file to the server's deploy directory. The standard format of JBoss Cache's standard XML configuration file (as shown in the Configuration Reference ) is the same as a JBoss AS MBean deployment descriptor, so the AS's SAR Deployer has no trouble handling it. Also, you don't have to place the configuration file directly in deploy ; you can package it along with other services or JEE components in a SAR or EAR. In AS 5, if you're using a server config based on the standard all config, then that's all you need to do; all required jars will be on the classpath. Otherwise, you will need to ensure jbosscache.jar and jgroups-all.jar are on the classpath. You may need to add other jars if you're using things like JdbmCacheLoader . The simplest way to do this is to copy the jars from the JBoss Cache distribution's lib directory to the server config's lib directory. You could also package the jars with the configuration file in Service Archive (.sar) file or an EAR. It is possible to deploy a JBoss Cache 2.0 instance in JBoss AS 4.x (at least in 4.2.0.GA; other AS releases are completely untested). However, the significant API changes between the JBoss Cache 2.x and 1.x releases mean none of the standard AS 4.x clustering services (e.g. http session replication) that rely on JBoss Cache will work with JBoss Cache 2.x. Also, be aware that usage of JBoss Cache 2.x in AS 4.x is not something the JBoss Cache developers are making any significant effort to test, so be sure to test your application well (which of course you're doing anyway.) Note in the example the value of the mbean element's code attribute: org.jboss.cache.jmx.CacheJmxWrapper . This is the class JBoss Cache uses to handle JMX integration; the Cache itself does not expose an MBean interface. See the JBoss Cache MBeans section for more on the CacheJmxWrapper . Once your cache is deployed, in order to use it with an in-VM client such as a servlet, a JMX proxy can be used to get a reference to the cache: The MBeanServerLocator class is a helper to find the (only) JBoss MBean server inside the current JVM. The javax.management.MBeanServerInvocationHandler class' newProxyInstance method creates a dynamic proxy implementing the given interface and uses JMX to dynamically dispatch methods invoked against the generated interface to the MBean. The name used to look up the MBean is the same as defined in the cache's configuration file. Once the proxy to the CacheJmxWrapper is obtained, the getCache() will return a reference to the Cache itself.
Via JBoss Microcontainer (JBoss AS 5.x) Beginning with AS 5, JBoss AS also supports deployment of POJO services via deployment of a file whose name ends with -beans.xml . A POJO service is one whose implementation is via a "Plain Old Java Object", meaning a simple java bean that isn't required to implement any special interfaces or extend any particular superclass. A Cache is a POJO service, and all the components in a Configuration are also POJOS, so deploying a cache in this way is a natural step. Deployment of the cache is done using the JBoss Microcontainer that forms the core of JBoss AS. JBoss Microcontainer is a sophisticated IOC framework (similar to Spring). A -beans.xml file is basically a descriptor that tells the IOC framework how to assemble the various beans that make up a POJO service. For each configurable option exposed by the Configuration components, a getter/setter must be defined in the configuration class. This is required so that JBoss Microcontainer can, in typical IOC way, call these methods when the corresponding properties have been configured. The rules for how to deploy the file, how to package it, how to ensure the required jars are on the classpath, etc. are the same as for a JMX-based deployment . Following is an example -beans.xml file. If you look in the server/all/deploy directory of an AS 5 installation, you can find several more examples. udp Example-EntityCache REPEATABLE_READ REPL_SYNC 15000 20000 15000 true true true org.jboss.cache.eviction.LRUPolicy 5 /_default_ 5000 1000 false ]]> See the JBoss Microcontainer documentation http://labs.jboss.com/jbossmc/docs for details on the above syntax. Basically, each bean element represents an object; most going to create a Configuration and its constituent parts . An interesting thing to note in the above example is the use of the RuntimeConfig object. External resources like a TransactionManager and a JGroups ChannelFactory that are visible to the microcontainer are dependency injected into the RuntimeConfig . The assumption here is that in some other deployment descriptor in the AS, the referenced beans have been described.
Binding to JNDI in JBoss AS With the 1.x JBoss Cache releases, a proxy to the cache could be bound into JBoss AS's JNDI tree using the AS's JRMPProxyFactory service. With JBoss Cache 2.x, this no longer works. An alternative way of doing a similar thing with a POJO (i.e. non-JMX-based) service like a Cache is under development by the JBoss AS team http://jira.jboss.com/jira/browse/JBAS-4456 . That feature is not available as of the time of this writing, although it will be completed before AS 5.0.0.GA is released. We will add a wiki page describing how to use it once it becomes available.
Runtime Management Information JBoss Cache includes JMX MBeans to expose cache functionality and provide statistics that can be used to analyze cache operations. JBoss Cache can also broadcast cache events as MBean notifications for handling via JMX monitoring tools.
JBoss Cache MBeans JBoss Cache provides an MBean that can be registered with your environments JMX server to allow access to the cache instance via JMX. This MBean is the org.jboss.cache.jmx.CacheJmxWrapper . It is a StandardMBean, so it's MBean interface is org.jboss.cache.jmx.CacheJmxWrapperMBean . This MBean can be used to: Get a reference to the underlying Cache . Invoke create/start/stop/destroy lifecycle operations on the underlying Cache . Inspect various details about the cache's current state (number of nodes, lock information, etc.) See numerous details about the cache's configuration, and change those configuration items that can be changed when the cache has already been started. See the CacheJmxWrapperMBean javadoc for more details. It is important to note a significant architectural difference between JBoss Cache 1.x and 2.x. In 1.x, the old TreeCache class was itself an MBean, and essentially exposed the cache's entire API via JMX. In 2.x, JMX has been returned to it's fundamental role as a management layer. The Cache object itself is completely unaware of JMX; instead JMX functionality is added through a wrapper class designed for that purpose. Furthermore, the interface exposed through JMX has been limited to management functions; the general Cache API is no longer exposed through JMX. For example, it is no longer possible to invoke a cache put or get via the JMX interface. If a CacheJmxWrapper is registered, JBoss Cache also provides MBeans for each interceptor configured in the cache's interceptor stack. These MBeans are used to capture and expose statistics related to cache operations. They are hierarchically associated with the CacheJmxWrapper MBean and have service names that reflect this relationship. For example, a replication interceptor MBean for the jboss.cache:service=TomcatClusteringCache instance will be accessible through the service named jboss.cache:service=TomcatClusteringCache,cache-interceptor=ReplicationInterceptor .
Registering the CacheJmxWrapper with the MBeanServer The best way to ensure the CacheJmxWrapper is registered in JMX depends on how you are deploying your cache:
Programatic Registration Simplest way to do this is to create your Cache and pass it to the CacheJmxWrapper constructor. Alternatively, build a Configuration object and pass it to the CacheJmxWrapper . The wrapper will construct the Cache :
JMX-Based Deployment in JBoss AS (JBoss AS 4.x and 5.x) When you deploy your cache in JBoss AS using a -service.xml file , a CacheJmxWrapper is automatically registered. There is no need to do anything further. The CacheJmxWrapper is accessible from an MBean server through the service name specified in the cache configuration file's mbean element.
Via JBoss Microcontainer (JBoss AS 5.x) CacheJmxWrapper is a POJO, so the microcontainer has no problem creating one. The trick is getting it to register your bean in JMX. This can be done by specifying the org.jboss.aop.microcontainer.aspects.jmx.JMX annotation on the CacheJmxWrapper bean: ... build up the Configuration false @org.jboss.aop.microcontainer.aspects.jmx.JMX(name="jboss.cache:service=ExampleTreeCache", exposedInterface=org.jboss.cache.jmx.CacheJmxWrapperMBean.class, registerDirectly=true) ]]> As discussed in the Programatic Registration section, CacheJmxWrapper can do the work of building, creating and starting the Cache if it is provided with a Configuration . With the microcontainer, this is the preferred approach, as it saves the boilerplate XML needed to create the CacheFactory : ... build up the Configuration @org.jboss.aop.microcontainer.aspects.jmx.JMX(name="jboss.cache:service=ExampleTreeCache", exposedInterface=org.jboss.cache.jmx.CacheJmxWrapperMBean.class, registerDirectly=true) ]]>
JBoss Cache Statistics JBoss Cache captures statistics in its interceptors and exposes the statistics through interceptor MBeans. Gathering of statistics is enabled by default; this can be disabled for a specific cache instance through the ExposeManagementStatistics configuration attribute. Note that the majority of the statistics are provided by the CacheMgmtInterceptor , so this MBean is the most significant in this regard. If you want to disable all statistics for performance reasons, you set ExposeManagementStatistics to false as this will prevent the CacheMgmtInterceptor from being included in the cache's interceptor stack when the cache is started. If a CacheJmxWrapper is registered with JMX, the wrapper also ensures that an MBean is registered in JMX for each interceptor that exposes statistics Note that if the CacheJmxWrapper is not registered in JMX, the interceptor MBeans will not be registered either. The JBoss Cache 1.4 releases included code that would try to "discover" an MBeanServer and automatically register the interceptor MBeans with it. For JBoss Cache 2.x we decided that this sort of "discovery" of the JMX environment was beyond the proper scope of a caching library, so we removed this functionality. . Management tools can then access those MBeans to examine the statistics. See the section in the JMX Reference chapter pertaining to the statistics that are made available via JMX. The name under which the interceptor MBeans will be registered is derived by taking the ObjectName under which the CacheJmxWrapper is registered and adding a cache-interceptor attribute key whose value is the non-qualified name of the interceptor class. So, for example, if the CacheJmxWrapper were registered under jboss.cache:service=TreeCache , the name of the CacheMgmtInterceptor MBean would be jboss.cache:service=TreeCache,cache-interceptor=CacheMgmtInterceptor . Each interceptor's MBean exposes a StatisticsEnabled attribute that can be used to disable maintenance of statistics for that interceptor. In addition, each interceptor MBean provides the following common operations and attributes. dumpStatistics - returns a Map containing the interceptor's attributes and values. resetStatistics - resets all statistics maintained by the interceptor. setStatisticsEnabled(boolean) - allows statistics to be disabled for a specific interceptor.
Receiving JMX Notifications JBoss Cache users can register a listener to receive cache events described earlier in the User API chapter. Users can alternatively utilize the cache's management information infrastructure to receive these events via JMX notifications. Cache events are accessible as notifications by registering a NotificationListener for the CacheJmxWrapper . See the section in the JMX Reference chapter pertaining to JMX notifications for a list of notifications that can be received through the CacheJmxWrapper . The following is an example of how to programmatically receive cache notifications when running in a JBoss AS environment. In this example, the client uses a filter to specify which events are of interest. The following is the simple notification listener implementation used in the previous example. Note that the JBoss Cache management implementation only listens to cache events after a client registers to receive MBean notifications. As soon as no clients are registered for notifications, the MBean will remove itself as a cache listener.
Accessing Cache MBeans in a Standalone Environment JBoss Cache MBeans are easily accessed when running cache instances in an application server that provides an MBean server interface such as JBoss JMX Console. Refer to your server documentation for instructions on how to access MBeans running in a server's MBean container. In addition, though, JBoss Cache MBeans are also accessible when running in a non-server environment if the JVM is JDK 5.0 or later. When running a standalone cache in a JDK 5.0 environment, you can access the cache's MBeans as follows. Set the system property -Dcom.sun.management.jmxremote when starting the JVM where the cache will run. Once the JVM is running, start the JDK 5.0 jconsole utility, located in your JDK's /bin directory. When the utility loads, you will be able to select your running JVM and connect to it. The JBoss Cache MBeans will be available on the MBeans panel. Note that the jconsole utility will automatically register as a listener for cache notifications when connected to a JVM running JBoss Cache instances. The following figure shows cache interceptor MBeans in jconsole . Cache statistics are displayed for the CacheMgmtInterceptor :
CacheMgmtInterceptor MBean in jconsole
jbosscache-core-2.2.2.GA/src/main/docbook/userguide/en/modules/preface.xml0000644000175000017500000000524310767524210026333 0ustar twernertwerner Preface This is the official JBoss Cache user guide. Along with its accompanying documents (an FAQ, a tutorial and a whole set of documents on PojoCache), this is freely available on the JBoss Cache documentation site. When used, JBoss Cache refers to JBoss Cache Core, a tree-structured, clustered, transactional cache. Pojo Cache, also a part of the JBoss Cache distribution, is documented separately. (Pojo Cache is a cache that deals with Plain Old Java Objects, complete with object relationships, with the ability to cluster such pojos while maintaining their relationships. Please see the Pojo Cache documentation for more information about this.) This book is targeted at both developers wishing to use JBoss Cache as a clustering and caching library in their codebase, as well as people who wish to "OEM" JBoss Cache by building on and extending its features. As such, this book is split into two major sections - one detailing the "User" API and the other going much deeper into specialist topics and the JBoss Cache architecture. In general, a good knowledge of the Java programming language along with a strong appreciation and understanding of transactions and concurrent threads is necessary. No prior knowledge of JBoss Application Server is expected or required. For further discussion, use the user forum linked on the JBoss Cache website. We also provide a mechanism for tracking bug reports and feature requests on the JBoss Cache JIRA issue tracker. If you are interested in the development of JBoss Cache or in translating this documentation into other languages, we'd love to hear from you. Please post a message on the user forum or contact us by using the JBoss Cache developer mailing list. This book is specifically targeted at the JBoss Cache release of the same version number. It may not apply to older or newer releases of JBoss Cache. It is important that you use the documentation appropriate to the version of JBoss Cache you intend to use. jbosscache-core-2.2.2.GA/src/main/docbook/userguide/en/modules/basic_api.xml0000644000175000017500000006617511021220614026634 0ustar twernertwerner User API
API Classes The Cache interface is the primary mechanism for interacting with JBoss Cache. It is constructed and optionally started using the CacheFactory . The CacheFactory allows you to create a Cache either from a Configuration object or an XML file. Once you have a reference to a Cache , you can use it to look up Node objects in the tree structure, and store data in the tree. Reviewing the javadoc for the above interfaces is the best way to learn the API. Below we cover some of the main points.
Instantiating and Starting the Cache An instance of the Cache interface can only be created via a CacheFactory . (This is unlike JBoss Cache 1.x, where an instance of the old TreeCache class could be directly instantiated.) CacheFactory provides a number of overloaded methods for creating a Cache , but they all do the same thing: Gain access to a Configuration , either by having one passed in as a method parameter, or by parsing XML content and constructing one. The XML content can come from a provided input stream or from a classpath or filesystem location. See the chapter on Configuration for more on obtaining a Configuration . Instantiate the Cache and provide it with a reference to the Configuration . Optionally invoke the cache's create() and start() methods. An example of the simplest mechanism for creating and starting a cache, using the default configuration values: Here we tell the CacheFactory to find and parse a configuration file on the classpath: Here we configure the cache from a file, but want to programatically change a configuration element. So, we tell the factory not to start the cache, and instead do it ourselves:
Caching and Retrieving Data Next, let's use the Cache API to access a Node in the cache and then do some simple reads and writes to that node. The Cache interface also exposes put/get/remove operations that take an Fqn as an argument:
The <literal>Fqn</literal> Class The previous section used the Fqn class in its examples; now let's learn a bit more about that class. A Fully Qualified Name (Fqn) encapsulates a list of names which represent a path to a particular location in the cache's tree structure. The elements in the list are typically String s but can be any Object or a mix of different types. This path can be absolute (i.e., relative to the root node), or relative to any node in the cache. Reading the documentation on each API call that makes use of Fqn will tell you whether the API expects a relative or absolute Fqn . The Fqn class provides are variety of constructors; see the javadoc for all the possibilities. The following illustrates the most commonly used approaches to creating an Fqn: abc = Fqn.fromString("/people/Smith/Joe/"); // Build it directly. Marginally more efficient to construct than parsing String[] strings = new String[] { "people", "Smith", "Joe" }; Fqn abc = new Fqn(strings); // Here we want to use types other than String Object[] objs = new Object[]{ "accounts", "NY", new Integer(12345) }; Fqn acctFqn = new Fqn(objs); ]]> Note that f = new Fqn("/a/b/c");]]> is not the same as f = Fqn.fromString("/a/b/c");]]> The former will result in an Fqn with a single element, called "/a/b/c" which hangs directly under the cache root. The latter will result in a 3 element Fqn, where "c" idicates a child of "b", which is a child of "a", and "a" hangs off the cache root. Another way to look at it is that the "/" separarator is only parsed when it forms part of a String passed in to Fqn.fromString() and not otherwise. The JBoss Cache API in the 1.x releases included many overloaded convenience methods that took a string in the /a/b/c format in place of an Fqn . In the interests of API simplicity, no such convenience methods are available in the JBC 2.x API.
Stopping and Destroying the Cache It is good practice to stop and destroy your cache when you are done using it, particularly if it is a clustered cache and has thus used a JGroups channel. Stopping and destroying a cache ensures resources like the JGroups channel are properly cleaned up. Not also that a cache that has had stop() invoked on it can be started again with a new call to start() . Similarly, a cache that has had destroy() invoked on it can be created again with a new call to create() (and then started again with a start() call).
Cache Modes Although technically not part of the API, the mode in which the cache is configured to operate affects the cluster-wide behavior of any put or remove operation, so we'll briefly mention the various modes here. JBoss Cache modes are denoted by the org.jboss.cache.config.Configuration.CacheMode enumeration. They consist of: LOCAL - local, non-clustered cache. Local caches don't join a cluster and don't communicate with other caches in a cluster. Therefore their contents don't need to be Serializable; however, we recommend making them Serializable, allowing you the flexibility to change the cache mode at any time. REPL_SYNC - synchronous replication. Replicated caches replicate all changes to the other caches in the cluster. Synchronous replication means that changes are replicated and the caller blocks until replication acknowledgements are received. REPL_ASYNC - asynchronous replication. Similar to REPL_SYNC above, replicated caches replicate all changes to the other caches in the cluster. Being asynchronous, the caller does not block until replication acknowledgements are received. INVALIDATION_SYNC - if a cache is configured for invalidation rather than replication, every time data is changed in a cache other caches in the cluster receive a message informing them that their data is now stale and should be evicted from memory. This reduces replication overhead while still being able to invalidate stale data on remote caches. INVALIDATION_ASYNC - as above, except this invalidation mode causes invalidation messages to be broadcast asynchronously. See the chapter on Clustering for more details on how the cache's mode affects behavior. See the chapter on Configuration for info on how to configure things like the cache's mode.
Adding a Cache Listener - registering for cache events JBoss Cache provides a convenient mechanism for registering notifications on cache events. Similar methods exist for removing or querying registered listeners. See the javadocs on the Cache interface for more details. Basically any public class can be used as a listener, provided it is annotated with the @CacheListener annotation. In addition, the class needs to have one or more methods annotated with one of the method-level annotations (in the org.jboss.cache.notifications.annotation package). Methods annotated as such need to be public, have a void return type, and accept a single parameter of type org.jboss.cache.notifications.event.Event or one of its subtypes. @CacheStarted - methods annotated such receive a notification when the cache is started. Methods need to accept a parameter type which is assignable from CacheStartedEvent . @CacheStopped - methods annotated such receive a notification when the cache is stopped. Methods need to accept a parameter type which is assignable from CacheStoppedEvent . @NodeCreated - methods annotated such receive a notification when a node is created. Methods need to accept a parameter type which is assignable from NodeCreatedEvent . @NodeRemoved - methods annotated such receive a notification when a node is removed. Methods need to accept a parameter type which is assignable from NodeRemovedEvent . @NodeModified - methods annotated such receive a notification when a node is modified. Methods need to accept a parameter type which is assignable from NodeModifiedEvent . @NodeMoved - methods annotated such receive a notification when a node is moved. Methods need to accept a parameter type which is assignable from NodeMovedEvent . @NodeVisited - methods annotated such receive a notification when a node is started. Methods need to accept a parameter type which is assignable from NodeVisitedEvent . @NodeLoaded - methods annotated such receive a notification when a node is loaded from a CacheLoader . Methods need to accept a parameter type which is assignable from NodeLoadedEvent . @NodeEvicted - methods annotated such receive a notification when a node is evicted from memory. Methods need to accept a parameter type which is assignable from NodeEvictedEvent . @NodeActivated - methods annotated such receive a notification when a node is activated. Methods need to accept a parameter type which is assignable from NodeActivatedEvent . @NodePassivated - methods annotated such receive a notification when a node is passivated. Methods need to accept a parameter type which is assignable from NodePassivatedEvent . @TransactionRegistered - methods annotated such receive a notification when the cache registers a javax.transaction.Synchronization with a registered transaction manager. Methods need to accept a parameter type which is assignable from TransactionRegisteredEvent . @TransactionCompleted - methods annotated such receive a notification when the cache receives a commit or rollback call from a registered transaction manager. Methods need to accept a parameter type which is assignable from TransactionCompletedEvent . @ViewChanged - methods annotated such receive a notification when the group structure of the cluster changes. Methods need to accept a parameter type which is assignable from ViewChangedEvent . @CacheBlocked - methods annotated such receive a notification when the cluster requests that cache operations are blocked for a state transfer event. Methods need to accept a parameter type which is assignable from CacheBlockedEvent . @CacheUnblocked - methods annotated such receive a notification when the cluster requests that cache operations are unblocked after a state transfer event. Methods need to accept a parameter type which is assignable from CacheUnblockedEvent . Refer to the javadocs on the annotations as well as the Event subtypes for details of what is passed in to your method, and when. Example:
Using Cache Loaders Cache loaders are an important part of JBoss Cache. They allow persistence of nodes to disk or to remote cache clusters, and allow for passivation when caches run out of memory. In addition, cache loaders allow JBoss Cache to perform 'warm starts', where in-memory state can be preloaded from persistent storage. JBoss Cache ships with a number of cache loader implementations. org.jboss.cache.loader.FileCacheLoader - a basic, filesystem based cache loader that persists data to disk. Non-transactional and not very performant, but a very simple solution. Used mainly for testing and not recommended for production use. org.jboss.cache.loader.JDBCCacheLoader - uses a JDBC connection to store data. Connections could be created and maintained in an internal pool (uses the c3p0 pooling library) or from a configured DataSource. The database this CacheLoader connects to could be local or remotely located. org.jboss.cache.loader.BdbjeCacheLoader - uses Oracle's BerkeleyDB file-based transactional database to persist data. Transactional and very performant, but potentially restrictive license. org.jboss.cache.loader.JdbmCacheLoader - an upcoming open source alternative to the BerkeleyDB. org.jboss.cache.loader.tcp.TcpCacheLoader - uses a TCP socket to "persist" data to a remote cluster, using a "far cache" pattern. http://wiki.jboss.org/wiki/Wiki.jsp?page=JBossClusteringPatternFarCache org.jboss.cache.loader.ClusteredCacheLoader - used as a "read-only" CacheLoader, where other nodes in the cluster are queried for state. These CacheLoaders, along with advanced aspects and tuning issues, are discussed in the chapter dedicated to CacheLoaders .
Using Eviction Policies Eviction policies are the counterpart to CacheLoaders. They are necessary to make sure the cache does not run out of memory and when the cache starts to fill, the eviction algorithm running in a separate thread offloads in-memory state to the CacheLoader and frees up memory. Eviction policies can be configured on a per-region basis, so different subtrees in the cache could have different eviction preferences. JBoss Cache ships with several eviction policies: org.jboss.cache.eviction.LRUPolicy - an eviction policy that evicts the least recently used nodes when thresholds are hit. org.jboss.cache.eviction.LFUPolicy - an eviction policy that evicts the least frequently used nodes when thresholds are hit. org.jboss.cache.eviction.MRUPolicy - an eviction policy that evicts the most recently used nodes when thresholds are hit. org.jboss.cache.eviction.FIFOPolicy - an eviction policy that creates a first-in-first-out queue and evicts the oldest nodes when thresholds are hit. org.jboss.cache.eviction.ExpirationPolicy - an eviction policy that selects nodes for eviction based on an expiry time each node is configured with. org.jboss.cache.eviction.ElementSizePolicy - an eviction policy that selects nodes for eviction based on the number of key/value pairs held in the node. Detailed configuration and implementing custom eviction policies are discussed in the chapter dedicated to eviction policies. .
jbosscache-core-2.2.2.GA/src/main/release/0000755000175000017500000000000011376174001020126 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/main/release/licenses/0000755000175000017500000000000011376174000021732 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/main/release/licenses/README.txt0000644000175000017500000000063010660525510023430 0ustar twernertwernerThis directory contains the licenses for some of the other libraries shipped with the distribution, that are not covered by the LGPL-2.1 license. It is important to note that not all the libraries shipped are needed in all cases and if you do not use certain libraries you may not come under some of the licenses in this directory. See README-libs.txt for details on the licenses attached to each library. jbosscache-core-2.2.2.GA/src/main/release/licenses/cpl-1.0.txt0000755000175000017500000002677610660525510023573 0ustar twernertwernerCommon Public License Version 1.0 THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS COMMON PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. 1. DEFINITIONS "Contribution" means: a) in the case of the initial Contributor, the initial code and documentation distributed under this Agreement, and b) in the case of each subsequent Contributor: i) changes to the Program, and ii) additions to the Program; where such changes and/or additions to the Program originate from and are distributed by that particular Contributor. A Contribution 'originates' from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf. Contributions do not include additions to the Program which: (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program. "Contributor" means any person or entity that distributes the Program. "Licensed Patents " mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program. "Program" means the Contributions distributed in accordance with this Agreement. "Recipient" means anyone who receives the Program under this Agreement, including all Contributors. 2. GRANT OF RIGHTS a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form. b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder. c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program. d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement. 3. REQUIREMENTS A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that: a) it complies with the terms and conditions of this Agreement; and b) its license agreement: i) effectively disclaims on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose; ii) effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits; iii) states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party; and iv) states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange. When the Program is made available in source code form: a) it must be made available under this Agreement; and b) a copy of this Agreement must be included with each copy of the Program. Contributors may not remove or alter any copyright notices contained within the Program. Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution. 4. COMMERCIAL DISTRIBUTION Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor ("Commercial Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor n writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense. For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages. 5. NO WARRANTY EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED 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. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement, including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations. 6. DISCLAIMER OF LIABILITY EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 7. GENERAL If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. If Recipient institutes patent litigation against a Contributor with respect to a patent applicable to software (including a cross-claim or counterclaim in a lawsuit), then any patent licenses granted by that Contributor to such Recipient under this Agreement shall terminate as of the date such litigation is filed. In addition, if Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed. All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive. Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. IBM is the initial Agreement Steward. IBM may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new version. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved. This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation. jbosscache-core-2.2.2.GA/src/main/release/licenses/apache-2.0.txt0000755000175000017500000002613610660525510024225 0ustar twernertwerner 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. jbosscache-core-2.2.2.GA/src/main/release/licenses/LICENSE-bdbje-lib.txt0000644000175000017500000000402010660525510025362 0ustar twernertwerner/* * Copyright (c) 2002-2006 * Oracle Corporation. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Redistributions in any form must be accompanied by information on * how to obtain complete source code for the DB software and any * accompanying software that uses the DB software. The source code * must either be included in the distribution or be available for no * more than the cost of distribution plus a nominal fee, and must be * freely redistributable under reasonable conditions. For an * executable file, complete source code means the source code for all * modules it contains. It does not include source code for modules or * files that typically accompany the major components of the operating * system on which the executable file runs. * * THIS SOFTWARE IS PROVIDED BY ORACLE CORPORATION ``AS IS'' AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR * NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL SLEEPYCAT SOFTWARE * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */jbosscache-core-2.2.2.GA/src/main/release/licenses/apache-1.1.txt0000755000175000017500000000541310660525510024220 0ustar twernertwerner/* ==================================================================== * The Apache Software License, Version 1.1 * * Copyright (c) 2000 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, * if any, must include the following acknowledgment: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowledgment may appear in the software itself, * if and wherever such third-party acknowledgments normally appear. * * 4. The names "Apache" and "Apache Software Foundation" must * not be used to endorse or promote products derived from this * software without prior written permission. For written * permission, please contact apache@apache.org. * * 5. Products derived from this software may not be called "Apache", * nor may "Apache" appear in their name, without prior written * permission of the Apache Software Foundation. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * . * * Portions of this software are based upon public domain software * originally written at the National Center for Supercomputing Applications, * University of Illinois, Urbana-Champaign. */ jbosscache-core-2.2.2.GA/src/main/release/licenses/README-bdbje-lib.txt0000644000175000017500000000202410660525510025237 0ustar twernertwerner $Id: README-bdbje-lib.txt 4275 2007-08-15 07:29:12Z manik.surtani@jboss.com $ NAME: Sleepycat Berkeley DB Java Edition VERSION: 1.7.0 PROJECT: http://www.oracle.com/database/berkeley-db/je/index.html PURPOSE: Used for Berkeley DB-based CacheLoader (org.jboss.cache.loader.bdbje.BdbjeCacheLoader) in JBossCache RESTRICTION: JBossCache can use Berkeley DB Java Edition from Oracle for persistent, reliable and transaction-protected data storage. If you choose to use Berkeley DB Java Edition with JBossCache, you must comply with the terms of Oracle's public license, included in the file LICENSE.txt. If you prefer not to release the source code for your own application in order to comply with the Oracle public license, you may purchase a different license for use of Berkeley DB Java Edition with JBossCache. For details, please go to: http://www.oracle.com/database/berkeley-db/je/index.html jbosscache-core-2.2.2.GA/src/main/release/README.txt0000644000175000017500000000237010703164716021633 0ustar twernertwernerJBossCache -- a replicated, transactional, and fine-grained cache system ======================================================================== Note: - We offer different packaging for download: + jbosscache-core-2.1.x-bin, just the core jar file and dependencies. + jbosscache-core-2.1.x-all, everything including docs, sources and tests. + jbosscache-core-2.1.x-doc, just the documentation. - If you are looking for JBoss Cache POJO edition, this is a separate distribution: + jbosscache-pojo-2.1.x-bin, just the core jar file and dependencies. Note that this will include jbosscache-core.jar as a dependency. + jbosscache-pojo-2.1.x-all, everything including docs, sources and tests. + jbosscache-pojo-2.1.x-doc, just the documentation. Requirements: - JDK 5.0 and up Running the tutorial: - The bin and all distros come with a build.xml file in the directory root, which contains targets for running tests as well as the demo outlined in the tutorial. You need an installation of Jakarta Ant 1.6.0 or higher to run the script. - Just run 'ant' in the root directry for instructions. Problems: - Please report problems to the JBoss cache forum (http://www.jboss.org/forum.jsp?forum=157) or the JBoss dev mailing list jbosscache-core-2.2.2.GA/src/main/release/build.xml0000755000175000017500000001203311005732256021752 0ustar twernertwerner where command is: compile -- compile the test code clean -- clean up the whole directory run.tests -- run batch examples one.test -- run one single test case. Need -Dtest=org/jboss/cache/??Test run.demo -- run demo GUI ]]> **** FATAL **** REQUIRES JDK 1.5.0 or greater. **** Compilation won't proceed! jbosscache-core-2.2.2.GA/src/main/release/JBossORG-EULA.txt0000644000175000017500000001764610660517123023022 0ustar twernertwernerLICENSE AGREEMENT JBOSS(r) This License Agreement governs the use of the Software Packages and any updates to the Software Packages, regardless of the delivery mechanism. Each Software Package is a collective work under U.S. Copyright Law. Subject to the following terms, Red Hat, Inc. ("Red Hat") grants to the user ("Client") a license to the applicable collective work(s) pursuant to the GNU Lesser General Public License v. 2.1 except for the following Software Packages: (a) JBoss Portal Forums and JBoss Transactions JTS, each of which is licensed pursuant to the GNU General Public License v.2; (b) JBoss Rules, which is licensed pursuant to the Apache License v.2.0; (c) an optional download for JBoss Cache for the Berkeley DB for Java database, which is licensed under the (open source) Sleepycat License (if Client does not wish to use the open source version of this database, it may purchase a license from Sleepycat Software); and (d) the BPEL extension for JBoss jBPM, which is licensed under the Common Public License v.1, and, pursuant to the OASIS BPEL4WS standard, requires parties wishing to redistribute to enter various royalty-free patent licenses. Each of the foregoing licenses is available at http://www.opensource.org/licenses/index.php. 1. The Software. "Software Packages" refer to the various software modules that are created and made available for distribution by the JBoss.org open source community at http://www.jboss.org. Each of the Software Packages may be comprised of hundreds of software components. The end user license agreement for each component is located in the component's source code. With the exception of certain image files identified in Section 2 below, the license terms for the components permit Client to copy, modify, and redistribute the component, in both source code and binary code forms. This agreement does not limit Client's rights under, or grant Client rights that supersede, the license terms of any particular component. 2. Intellectual Property Rights. The Software Packages are owned by Red Hat and others and are protected under copyright and other laws. Title to the Software Packages and any component, or to any copy, modification, or merged portion shall remain with the aforementioned, subject to the applicable license. The "JBoss" trademark, "Red Hat" trademark, the individual Software Package trademarks, and the "Shadowman" logo are registered trademarks of Red Hat and its affiliates in the U.S. and other countries. This agreement permits Client to distribute unmodified copies of the Software Packages using the Red Hat trademarks that Red Hat has inserted in the Software Packages on the condition that Client follows Red Hat's trademark guidelines for those trademarks located at http://www.redhat.com/about/corporate/trademark/. Client must abide by these trademark guidelines when distributing the Software Packages, regardless of whether the Software Packages have been modified. If Client modifies the Software Packages, then Client must replace all Red Hat trademarks and logos identified at http://www.jboss.com/company/logos, unless a separate agreement with Red Hat is executed or other permission granted. Merely deleting the files containing the Red Hat trademarks may corrupt the Software Packages. 3. Limited Warranty. Except as specifically stated in this Paragraph 3 or a license for a particular component, to the maximum extent permitted under applicable law, the Software Packages and the components are provided and licensed "as is" without warranty of any kind, expressed or implied, including the implied warranties of merchantability, non-infringement or fitness for a particular purpose. Red Hat warrants that the media on which Software Packages may be furnished will be free from defects in materials and manufacture under normal use for a period of 30 days from the date of delivery to Client. Red Hat does not warrant that the functions contained in the Software Packages will meet Client's requirements or that the operation of the Software Packages will be entirely error free or appear precisely as described in the accompanying documentation. This warranty extends only to the party that purchases the Services pertaining to the Software Packages from Red Hat or a Red Hat authorized distributor. 4. Limitation of Remedies and Liability. To the maximum extent permitted by applicable law, the remedies described below are accepted by Client as its only remedies. Red Hat's entire liability, and Client's exclusive remedies, shall be: If the Software media is defective, Client may return it within 30 days of delivery along with a copy of Client's payment receipt and Red Hat, at its option, will replace it or refund the money paid by Client for the Software. To the maximum extent permitted by applicable law, Red Hat or any Red Hat authorized dealer will not be liable to Client for any incidental or consequential damages, including lost profits or lost savings arising out of the use or inability to use the Software, even if Red Hat or such dealer has been advised of the possibility of such damages. In no event shall Red Hat's liability under this agreement exceed the amount that Client paid to Red Hat under this Agreement during the twelve months preceding the action. 5. Export Control. As required by U.S. law, Client represents and warrants that it: (a) understands that the Software Packages are subject to export controls under the U.S. Commerce Department's Export Administration Regulations ("EAR"); (b) is not located in a prohibited destination country under the EAR or U.S. sanctions regulations (currently Cuba, Iran, Iraq, Libya, North Korea, Sudan and Syria); (c) will not export, re-export, or transfer the Software Packages to any prohibited destination, entity, or individual without the necessary export license(s) or authorizations(s) from the U.S. Government; (d) will not use or transfer the Software Packages for use in any sensitive nuclear, chemical or biological weapons, or missile technology end-uses unless authorized by the U.S. Government by regulation or specific license; (e) understands and agrees that if it is in the United States and exports or transfers the Software Packages to eligible end users, it will, as required by EAR Section 740.17(e), submit semi-annual reports to the Commerce Department's Bureau of Industry & Security (BIS), which include the name and address (including country) of each transferee; and (f) understands that countries other than the United States may restrict the import, use, or export of encryption products and that it shall be solely responsible for compliance with any such import, use, or export restrictions. 6. Third Party Programs. Red Hat may distribute third party software programs with the Software Packages that are not part of the Software Packages and which Client must install separately. These third party programs are subject to their own license terms. The license terms either accompany the programs or can be viewed at http://www.redhat.com/licenses/. If Client does not agree to abide by the applicable license terms for such programs, then Client may not install them. If Client wishes to install the programs on more than one system or transfer the programs to another party, then Client must contact the licensor of the programs. 7. General. If any provision of this agreement is held to be unenforceable, that shall not affect the enforceability of the remaining provisions. This License Agreement shall be governed by the laws of the State of North Carolina and of the United States, without regard to any conflict of laws provisions, except that the United Nations Convention on the International Sale of Goods shall not apply. Copyright 2006 Red Hat, Inc. All rights reserved. "JBoss" and the JBoss logo are registered trademarks of Red Hat, Inc. All other trademarks are the property of their respective owners. Page 1 of 1 18 October 2006 jbosscache-core-2.2.2.GA/src/main/release/README-libs.txt0000644000175000017500000000463710660525510022565 0ustar twernertwernerJARs included in the distribution * berkeleydb/je.jar - OPTIONAL (licenses/LICENSE-bdbje-lib.txt) Used by the BDBJECacheLoader, and only necessary if you use this cache loader implementation. BerkeleyDB is a fast and efficient filesystem-based database from Sleepycat Software (now Oracle). * bsh-2.0b4.jar - (http://www.beanshell.org) - OPTIONAL BeanShell . Used by demo programs and the JBossCacheGUI program, used in the tutorial. * c3p0-0.9.1.jar - (http://sourceforge.net/projects/c3p0) - OPTIONAL Database connection pooling library. Optionally used if you wish to use the JDBCCacheLoader and are not running within an application server (and hence don't have external connection pooling via a DataSource). See the User Guide on enabling C3P0 connection pooling for the JDBCCacheLoader. * commons-logging-1.1.jar - REQUIRED Used for logging. * concurrent.jar - OPTIONAL but REQUIRED for PojoCache A dependency of jboss-aop, which in turn is a dependency of Pojo Cache. Not necessary if you don't use Pojo Cache. * derby.jar - OPTIONAL Used for unit tests when testing the JDBCCacheLoader. * javassist.jar - OPTIONAL but REQUIRED for PojoCache A dependency of jboss-aop, which in turn is a dependency of Pojo Cache. Not necessary if you don't use Pojo Cache. * jboss-aop-jdk50.jar - OPTIONAL but REQUIRED for PojoCache A dependency of Pojo Cache. Not necessary if you don't use Pojo Cache. * jboss-common-core.jar - REQUIRED JBoss utilities used by JBoss Cache. Version 2.0.5.GA or above needed if run with JDK 6. * jboss-j2ee.jar - OPTIONAL Sun Java EE interfaces. Necessary if you are not running in a Java EE environment. * jcip-annotations.jar - (http://jcip.net) - REQUIRED Annotations used to assert concurrency behaviour of internal classes. * jdbm-1.0.jar - (http://jdbm.sourceforge.net/) - OPTIONAL A filesystem-based database similar to BerkeleyDB. Used by the JdbmCacheLoader. * jgroups.jar (http://jgroups.com) - REQUIRED Group communications library that is the backbone of JBoss Cache's replication. Necessary even when the cache is run in LOCAL mode. * jmock-1.1.0.jar - OPTIONAL Used by unit tests. * junit.jar - OPTIONAL Used by unit tests. * log4j-1.2.14.jar - OPTIONAL Used for logging. * trove.jar - OPTIONAL but REQUIRED for PojoCache A dependency of jboss-aop, which in turn is a dependency of Pojo Cache. Not necessary if you don't use Pojo Cache.jbosscache-core-2.2.2.GA/src/main/release/LICENSE-lgpl-2.1.txt0000644000175000017500000006350410660517123023214 0ustar twernertwerner GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! jbosscache-core-2.2.2.GA/src/main/etc/0000755000175000017500000000000011376174036017271 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/main/etc/jndi.properties0000644000175000017500000000035410757176162022341 0ustar twernertwerner# DO NOT EDIT THIS FILE UNLESS YOU KNOW WHAT YOU ARE DOING # #java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory #java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces #java.naming.provider.url=localhost:1099jbosscache-core-2.2.2.GA/src/main/etc/cache-jdbc.properties0000755000175000017500000000443610757176162023370 0ustar twernertwerner## # Standard JBC table properties # The table name can also be prepended with schema name for the given table. # Even though there is an Sql92 standard syntax for this: . #schema has different meanings accross various DBMS: Oracle - user name; PointBase - database name # Microsoft SQL Server & DB2 - schema name corresponds to the catalog owner cache.jdbc.table.name=jbosscache cache.jdbc.table.create=true cache.jdbc.table.drop=false cache.jdbc.table.primarykey=jbosscache_pk cache.jdbc.fqn.column=fqn cache.jdbc.fqn.type=varchar(255) cache.jdbc.node.column=node cache.jdbc.node.type=blob cache.jdbc.parent.column=parent # Specify your DBMS's string concatenation function syntax in the following manner: concat(1 , 2) -> '12'. # This syntax should work an most popular DBMS like oracle, db2, mssql, mysql, PostgreSQL. Derby - on which #the tests are run does not support 'concat', but '1 || 2' . If no value is sepcified then concat(1 , 2) is used by default. cache.jdbc.sql-concat=1 || 2 # JBoss Cache Table properties for Hypersonic, just overrides #cache.jdbc.node.type=OBJECT ## # DataSource #cache.jdbc.datasource=DefaultDS ## # JDBC driver specific properties # Hypersonic #cache.jdbc.node.type=OBJECT ## MySql #cache.jdbc.driver=com.mysql.jdbc.Driver #cache.jdbc.url=jdbc:mysql://localhost:3306/jbossdb #cache.jdbc.user=root #cache.jdbc.password=admin ## Oracle #cache.jdbc.driver=oracle.jdbc.OracleDriver #cache.jdbc.url=jdbc:oracle:thin:@192.168.0.100:1521:JBOSSDB #cache.jdbc.user=jboss #cache.jdbc.password=sa ## MS Sql Server #cache.jdbc.driver=com.microsoft.jdbc.sqlserver.SQLServerDriver #cache.jdbc.url=jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=jbossdb;SelectMethod=cursor #cache.jdbc.user=sa #cache.jdbc.password= #cache.jdbc.node.type=image ## Pointbase #cache.jdbc.driver=com.pointbase.jdbc.jdbcUniversalDriver #cache.jdbc.url=jdbc:pointbase:server://localhost:9092/jboss,new #cache.jdbc.user=PBPUBLIC #cache.jdbc.password=PBPUBLIC ## PostgreSQL #cache.jdbc.driver = org.postgresql.Driver #cache.jdbc.url=jdbc:postgresql://192.168.0.100:5432/jbossdb #cache.jdbc.user=postgres #cache.jdbc.password=admin ## Derby cache.jdbc.driver = org.apache.derby.jdbc.EmbeddedDriver cache.jdbc.url=jdbc:derby:jbossdb;create=true cache.jdbc.user=user1 cache.jdbc.password=user1jbosscache-core-2.2.2.GA/src/main/etc/META-INF/0000755000175000017500000000000011376174035020430 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/main/etc/META-INF/multiplexer-enabled-cache-service.xml0000644000175000017500000001726310757176162027631 0ustar twernertwerner jboss:service=Naming jboss:service=TransactionManager org.jboss.cache.transaction.GenericTransactionManagerLookup REPEATABLE_READ REPL_SYNC JBossCache-Cluster jgroups.mux:name=Multiplexer fc-fast-minimalthreads 20000 15000 10000 true org.jboss.cache.buddyreplication.NextMemberBuddyLocator numBuddies = 1 ignoreColocatedBuddies = true myBuddyPoolReplicationGroup 2000 false true true jbosscache-core-2.2.2.GA/src/main/etc/META-INF/optimistically-locked-cache-service.xml0000644000175000017500000000636610757176162030176 0ustar twernertwerner jboss:service=Naming jboss:service=TransactionManager org.jboss.cache.transaction.GenericTransactionManagerLookup false true Optimistic READ_COMMITTED LOCAL 10000 1 org.jboss.cache.eviction.LRUPolicy 10 0 0 10 0 0 10 1 1 jbosscache-core-2.2.2.GA/src/main/etc/META-INF/buddy-replication-cache-service.xml0000644000175000017500000001774010757176162027305 0ustar twernertwerner jboss:service=Naming jboss:service=TransactionManager org.jboss.cache.transaction.GenericTransactionManagerLookup REPEATABLE_READ REPL_SYNC JBossCache-Cluster 20000 15000 10000 true org.jboss.cache.buddyreplication.NextMemberBuddyLocator numBuddies = 1 ignoreColocatedBuddies = true myBuddyPoolReplicationGroup 2000 false true true jbosscache-core-2.2.2.GA/src/main/etc/META-INF/local-cache-service.xml0000644000175000017500000000353410757176162024755 0ustar twernertwerner jboss:service=Naming jboss:service=TransactionManager org.jboss.cache.transaction.GenericTransactionManagerLookup REPEATABLE_READ LOCAL 15000 jbosscache-core-2.2.2.GA/src/main/etc/META-INF/eviction-enabled-cache-service.xml0000644000175000017500000002506010757176162027071 0ustar twernertwerner jboss:service=Naming jboss:service=TransactionManager org.jboss.cache.transaction.GenericTransactionManagerLookup REPEATABLE_READ LOCAL 15000 5 200000 org.jboss.cache.eviction.LRUPolicy 5000 1000 5000 1000 5 4 10000 4 10000 8 10 jbosscache-core-2.2.2.GA/src/main/etc/META-INF/total-replication-cache-service.xml0000644000175000017500000001372211005732256027302 0ustar twernertwerner jboss:service=Naming jboss:service=TransactionManager org.jboss.cache.transaction.GenericTransactionManagerLookup REPEATABLE_READ REPL_SYNC JBossCache-Cluster true 20000 15000 10000 jbosscache-core-2.2.2.GA/src/main/etc/META-INF/cacheloader-enabled-cache-service.xml0000644000175000017500000001222510757176162027502 0ustar twernertwerner jboss:service=Naming jboss:service=TransactionManager org.jboss.cache.transaction.GenericTransactionManagerLookup REPEATABLE_READ LOCAL 15000 5 200000 org.jboss.cache.eviction.LRUPolicy 5000 3 100 3 false / false org.jboss.cache.loader.JDBCCacheLoader cache.jdbc.table.name=jbosscache cache.jdbc.table.create=true cache.jdbc.table.drop=true cache.jdbc.table.primarykey=jbosscache_pk cache.jdbc.fqn.column=fqn cache.jdbc.fqn.type=varchar(255) cache.jdbc.node.column=node cache.jdbc.node.type=blob cache.jdbc.parent.column=parent cache.jdbc.driver=com.mysql.jdbc.Driver cache.jdbc.url=jdbc:mysql://localhost:3306/jbossdb cache.jdbc.user=root cache.jdbc.password= cache.jdbc.sql-concat=concat(1,2) false true false false jbosscache-core-2.2.2.GA/src/main/etc/cache-config.xml0000644000175000017500000000156410757176162022333 0ustar twernertwerner jboss:service=Naming jboss:service=TransactionManager org.jboss.cache.transaction.GenericTransactionManagerLookup READ_COMMITTED LOCAL org.jboss.cache.loader.bdbje.BdbjeCacheLoader location=./ jbosscache-core-2.2.2.GA/src/main/etc/dependencies.xml0000644000175000017500000000563110757176162022452 0ustar twernertwerner 1.4 bsh-2.0b4.jar 1.3.0 2.0b4 Embeddable Java source interpreter. Used in distribution demo. commons-logging.jar 1.0.3 Apache commons logging. Used in logging now. concurrent.jar 1.3.4 Doug Lea's concurrent package. javassist.jar 3.1RC2 Simple Java bytecode manipulation. Used by Aop. qdox.jar 1.4 Parser for annotation used in Aop. jboss-aop.jar 1.3.5 Standalone JBoss Aop. jboss-aop-jdk50.jar 1.3.5 Standalone JBoss Aop for JDK50. jboss-common.jar 4.0.3 JBoss common classes jboss-j2ee.jar 4.0.3 j2ee interfaces jboss-jmx.jar 4.0.3 jmx implementaiton jboss-minimal.jar 4.0.3 jboss-system.jar 4.0.3 jgroups.jar 2.2.7 2.2.8 Reliable messaging library. Used in replication. junit.jar 3.8.1 Unit testing framework. Used for examples. sleepycat/je.jar 1.7.0 2.0.54 Embeded DB. Used for JE cache loader. trove.jar 1.0.2 High performance collections for Java. Used by Aop. jbosscache-core-2.2.2.GA/src/main/java/0000755000175000017500000000000011376174001017427 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/main/java/org/0000755000175000017500000000000011376174001020216 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/main/java/org/jboss/0000755000175000017500000000000011376174001021336 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/0000755000175000017500000000000011376174035022410 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/jmx/0000755000175000017500000000000011376174020023200 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/jmx/CacheJmxWrapper.java0000644000175000017500000007251111010550166027066 0ustar twernertwerner/* * JBoss, Home of Professional Open Source. * Copyright 2006, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.cache.jmx; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.Cache; import org.jboss.cache.CacheException; import org.jboss.cache.CacheFactory; import org.jboss.cache.CacheSPI; import org.jboss.cache.CacheStatus; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.config.BuddyReplicationConfig; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.ConfigurationException; import org.jboss.cache.config.EvictionConfig; import org.jboss.cache.config.RuntimeConfig; import org.jboss.cache.factories.XmlConfigurationParser; import org.jboss.cache.interceptors.base.CommandInterceptor; import org.jboss.cache.util.CachePrinter; import org.jgroups.Address; import org.jgroups.Channel; import org.jgroups.ChannelFactory; import org.jgroups.jmx.JChannelFactoryMBean; import org.w3c.dom.Element; import javax.management.*; import javax.transaction.TransactionManager; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; /** * Wrapper class that exposes a * {@link CacheJmxWrapperMBean JMX management interface} * * @author Brian Stansberry * @version $Revision: 5808 $ */ public class CacheJmxWrapper extends NotificationBroadcasterSupport implements CacheJmxWrapperMBean, MBeanRegistration, CacheNotificationBroadcaster { private Log log = LogFactory.getLog(getClass().getName()); private MBeanServer server; private String cacheObjectName; private boolean interceptorsRegistered; private CacheSPI cache; private Configuration config; private boolean registerInterceptors = true; private final AtomicInteger listenerCount = new AtomicInteger(0); private final AtomicLong sequence = new AtomicLong(0); private final CacheNotificationListener cacheNotificationListener; private CacheStatus cacheStatus; private String notificationServiceName; private boolean registered; private boolean disableStateChangeNotifications; // Legacy config support private Element buddyReplConfig; private Element evictionConfig; private Element cacheLoaderConfig; private Element clusterConfig; private JChannelFactoryMBean multiplexerService; // ----------------------------------------------------------- Constructors public CacheJmxWrapper() { cacheNotificationListener = new CacheNotificationListener(this); cacheStatus = CacheStatus.INSTANTIATED; } public CacheJmxWrapper(Cache cache) { this(); setCache(cache); } // --------------------------------------------------- CacheJmxWrapperMBean public Cache getCache() { return cache; } public Configuration getConfiguration() { Configuration cfg = (cache == null ? config : cache.getConfiguration()); if (cfg == null) { cfg = config = new Configuration(); } return cfg; } public String printConfigurationAsString() { Configuration cfg = getConfiguration(); return cfg == null ? "Configuration is null" : cfg.toString(); } public String printConfigurationAsHtmlString() { Configuration cfg = getConfiguration(); return cfg == null ? "Configuration is null" : formatHtml(cfg.toString()); } public String printCacheDetails() { return cache == null ? "Cache is null" : CachePrinter.printCacheDetails(cache); } public String printCacheDetailsAsHtml() { return cache == null ? "Cache is null" : formatHtml(CachePrinter.printCacheDetails(cache)); } public CacheStatus getCacheStatus() { return cacheStatus; } public int getState() { switch (cacheStatus) { case INSTANTIATED: case CREATING: return registered ? REGISTERED : UNREGISTERED; case CREATED: return CREATED; case STARTING: return STARTING; case STARTED: return STARTED; case STOPPING: return STOPPING; case STOPPED: case DESTROYING: return STOPPED; case DESTROYED: return registered ? DESTROYED : UNREGISTERED; case FAILED: default: return FAILED; } } public Address getLocalAddress() { return cache == null ? null : cache.getLocalAddress(); } public List
getMembers() { return cache == null ? null : cache.getMembers(); } public int getNumberOfNodes() { return cache == null ? -1 : cache.getNumberOfNodes(); } public int getNumberOfAttributes() { return cache == null ? -1 : cache.getNumberOfAttributes(); } public String printLockInfo() { return cache == null ? "Cache is null" : CachePrinter.printCacheLockingInfo(cache); } public String printLockInfoAsHtml() { return cache == null ? "Cache is null" : formatHtml(CachePrinter.printCacheLockingInfo(cache)); } public boolean getRegisterInterceptors() { return registerInterceptors; } public void setRegisterInterceptors(boolean register) { this.registerInterceptors = register; } // ---------------------------------------------------- LegacyConfiguration public Element getBuddyReplicationConfig() { return buddyReplConfig; } public Element getCacheLoaderConfig() { return cacheLoaderConfig; } public Element getCacheLoaderConfiguration() { return getCacheLoaderConfig(); } public String getCacheMode() { return getConfiguration().getCacheModeString(); } public String getClusterName() { return getConfiguration().getClusterName(); } public String getClusterProperties() { return getConfiguration().getClusterConfig(); } public Element getClusterConfig() { return clusterConfig; } public Element getEvictionPolicyConfig() { return evictionConfig; } public boolean getExposeManagementStatistics() { return getConfiguration().getExposeManagementStatistics(); } public boolean getUseInterceptorMbeans() { return getExposeManagementStatistics(); } public boolean getFetchInMemoryState() { return getConfiguration().isFetchInMemoryState(); } public long getStateRetrievalTimeout() { return getConfiguration().getStateRetrievalTimeout(); } @Deprecated public void setInitialStateRetrievalTimeout(long timeout) { setStateRetrievalTimeout(timeout); } public String getIsolationLevel() { return getConfiguration().getIsolationLevelString(); } public long getLockAcquisitionTimeout() { return getConfiguration().getLockAcquisitionTimeout(); } public JChannelFactoryMBean getMultiplexerService() { return multiplexerService; } public String getMultiplexerStack() { return getConfiguration().getMultiplexerStack(); } public ChannelFactory getMuxChannelFactory() { return getConfiguration().getRuntimeConfig().getMuxChannelFactory(); } public String getNodeLockingScheme() { return getConfiguration().getNodeLockingSchemeString(); } public long getReplQueueInterval() { return getConfiguration().getReplQueueInterval(); } public int getReplQueueMaxElements() { return getConfiguration().getReplQueueMaxElements(); } public String getReplicationVersion() { return getConfiguration().getReplVersionString(); } public boolean getSyncCommitPhase() { return getConfiguration().isSyncCommitPhase(); } public long getSyncReplTimeout() { return getConfiguration().getSyncReplTimeout(); } public boolean getSyncRollbackPhase() { return getConfiguration().isSyncRollbackPhase(); } public TransactionManager getTransactionManager() { return getConfiguration().getRuntimeConfig().getTransactionManager(); } public String getTransactionManagerLookupClass() { return getConfiguration().getTransactionManagerLookupClass(); } @Deprecated @SuppressWarnings("deprecation") public boolean getUseRegionBasedMarshalling() { return getConfiguration().isUseRegionBasedMarshalling(); } public boolean isUseLazyDeserialization() { return getConfiguration().isUseLazyDeserialization(); } public boolean getUseReplQueue() { return getConfiguration().isUseReplQueue(); } public boolean isInactiveOnStartup() { return getConfiguration().isInactiveOnStartup(); } public void setBuddyReplicationConfig(Element config) { BuddyReplicationConfig brc = null; if (config != null) { brc = XmlConfigurationParser.parseBuddyReplicationConfig(config); } getConfiguration().setBuddyReplicationConfig(brc); this.buddyReplConfig = config; } public void setCacheLoaderConfig(Element cache_loader_config) { CacheLoaderConfig clc = null; if (cache_loader_config != null) { clc = XmlConfigurationParser.parseCacheLoaderConfig(cache_loader_config); } getConfiguration().setCacheLoaderConfig(clc); this.cacheLoaderConfig = cache_loader_config; } public void setCacheLoaderConfiguration(Element config) { log.warn("MBean attribute 'CacheLoaderConfiguration' is deprecated; " + "use 'CacheLoaderConfig'"); setCacheLoaderConfig(config); } public void setCacheMode(String mode) throws Exception { getConfiguration().setCacheModeString(mode); } public void setClusterConfig(Element config) { String props = null; if (config != null) { props = XmlConfigurationParser.parseClusterConfigXml(config); } getConfiguration().setClusterConfig(props); this.clusterConfig = config; } @Deprecated public long getInitialStateRetrievalTimeout() { return getStateRetrievalTimeout(); } public void setClusterName(String name) { getConfiguration().setClusterName(name); } public void setClusterProperties(String cluster_props) { getConfiguration().setClusterConfig(cluster_props); } public void setEvictionPolicyConfig(Element config) { EvictionConfig ec = null; if (config != null) { ec = XmlConfigurationParser.parseEvictionConfig(config); } getConfiguration().setEvictionConfig(ec); this.evictionConfig = config; } public void setExposeManagementStatistics(boolean expose) { getConfiguration().setExposeManagementStatistics(expose); } public void setUseInterceptorMbeans(boolean use) { log.warn("MBean attribute 'UseInterceptorMbeans' is deprecated; " + "use 'ExposeManagementStatistics'"); setExposeManagementStatistics(use); } public void setFetchInMemoryState(boolean flag) { getConfiguration().setFetchInMemoryState(flag); } public void setInactiveOnStartup(boolean inactiveOnStartup) { getConfiguration().setInactiveOnStartup(inactiveOnStartup); } public void setStateRetrievalTimeout(long timeout) { getConfiguration().setStateRetrievalTimeout(timeout); } public void setIsolationLevel(String level) { getConfiguration().setIsolationLevelString(level); } public void setLockAcquisitionTimeout(long timeout) { getConfiguration().setLockAcquisitionTimeout(timeout); } public void setMultiplexerService(JChannelFactoryMBean muxService) { this.multiplexerService = muxService; } public void setMultiplexerStack(String stackName) { getConfiguration().setMultiplexerStack(stackName); } public void setMuxChannelFactory(ChannelFactory factory) { getConfiguration().getRuntimeConfig().setMuxChannelFactory(factory); } public void setNodeLockingScheme(String nodeLockingScheme) { getConfiguration().setNodeLockingSchemeString(nodeLockingScheme); } public void setReplQueueInterval(long interval) { getConfiguration().setReplQueueInterval(interval); } public void setReplQueueMaxElements(int max_elements) { getConfiguration().setReplQueueMaxElements(max_elements); } public void setReplicationVersion(String version) { getConfiguration().setReplVersionString(version); } public void setSyncCommitPhase(boolean sync_commit_phase) { getConfiguration().setSyncCommitPhase(sync_commit_phase); } public void setSyncReplTimeout(long timeout) { getConfiguration().setSyncReplTimeout(timeout); } public void setSyncRollbackPhase(boolean sync_rollback_phase) { getConfiguration().setSyncRollbackPhase(sync_rollback_phase); } public void setTransactionManager(TransactionManager manager) { getConfiguration().getRuntimeConfig().setTransactionManager(manager); } public void setTransactionManagerLookupClass(String cl) throws Exception { getConfiguration().setTransactionManagerLookupClass(cl); } @Deprecated @SuppressWarnings("deprecation") public void setUseRegionBasedMarshalling(boolean isTrue) { getConfiguration().setUseRegionBasedMarshalling(isTrue); } public void setUseReplQueue(boolean flag) { getConfiguration().setUseReplQueue(flag); } // -------------------------------------------------------------- Lifecycle public void create() throws CacheException { if (!cacheStatus.createAllowed()) { if (cacheStatus.needToDestroyFailedCache()) destroy(); else return; } try { cacheStatus = CacheStatus.CREATING; if (cache == null) { if (config == null) { throw new ConfigurationException("Must call setConfiguration() or setCache() before call to create()"); } constructCache(); } cache.create(); cacheStatus = CacheStatus.CREATED; } catch (Throwable t) { handleLifecycleTransitionFailure(t); } } public void start() throws CacheException { if (!cacheStatus.startAllowed()) { if (cacheStatus.needToDestroyFailedCache()) destroy(); // this will take us back to DESTROYED if (cacheStatus.needCreateBeforeStart()) create(); else return; } try { int oldState = getState(); cacheStatus = CacheStatus.STARTING; int startingState = getState(); sendStateChangeNotification(oldState, startingState, getClass().getSimpleName() + " starting", null); cache.start(); registerInterceptors(); cacheStatus = CacheStatus.STARTED; sendStateChangeNotification(startingState, getState(), getClass().getSimpleName() + " started", null); } catch (Throwable t) { handleLifecycleTransitionFailure(t); } } public void stop() { if (!cacheStatus.stopAllowed()) { return; } // Trying to stop() from FAILED is valid, but may not work boolean failed = cacheStatus == CacheStatus.FAILED; try { int oldState = getState(); cacheStatus = CacheStatus.STOPPING; int stoppingState = getState(); sendStateChangeNotification(oldState, stoppingState, getClass().getSimpleName() + " stopping", null); cache.stop(); if (cache.getCacheStatus() == CacheStatus.DESTROYED) { // Cache was already destroyed externally; // so get rid of the interceptor mbeans unregisterInterceptors(); } cacheStatus = CacheStatus.STOPPED; sendStateChangeNotification(stoppingState, getState(), getClass().getSimpleName() + " stopped", null); } catch (Throwable t) { if (failed) { log.warn("Attempted to stop() from FAILED state, " + "but caught exception; try calling destroy()", t); } handleLifecycleTransitionFailure(t); } } public void destroy() { if (!cacheStatus.destroyAllowed()) { if (cacheStatus.needStopBeforeDestroy()) { try { stop(); } catch (CacheException e) { log.warn("Needed to call stop() before destroying but stop() " + "threw exception. Proceeding to destroy", e); } } else return; } try { cacheStatus = CacheStatus.DESTROYING; // The cache is destroyed, so we shouldn't leave the interceptors // in JMX, even if we didn't register them in create unregisterInterceptors(); if (cache != null) cache.destroy(); } finally { // We always proceed to DESTROYED cacheStatus = CacheStatus.DESTROYED; } } // ------------------------------------------------------ MBeanRegistration /** * Caches the provided server and objName. */ public ObjectName preRegister(MBeanServer server, ObjectName objName) throws Exception { this.server = server; if (cacheObjectName == null) { if (objName == null) { // Calling this will create a value for cacheObjectName getCacheObjectName(); } else { cacheObjectName = objName.getCanonicalName(); } } ObjectName result = new ObjectName(cacheObjectName); // Inform our CacheNotificationListener of the ObjectName it should transmit if (notificationServiceName == null) notificationServiceName = result.getCanonicalName(); cacheNotificationListener.setServiceName(notificationServiceName); return result; } /** * Registers the cache's interceptors, if {@link #getRegisterInterceptors()} * is true. */ public void postRegister(Boolean registrationDone) { if (Boolean.TRUE.equals(registrationDone)) { log.debug("Registered in JMX under " + cacheObjectName); if (cache != null && CacheStatus.STARTED.equals(cache.getCacheStatus())) { try { registerInterceptors(); } catch (Exception e) { log.error("Caught exception registering cache interceptors with JMX", e); } } registered = true; } } /** * No-op. */ public void preDeregister() throws Exception { } /** * Unregisters the interceptors, if {@link #getRegisterInterceptors()} is * true. */ public void postDeregister() { unregisterInterceptors(); server = null; registered = false; } // --------------------------------------------------------- Public methods /** * Sets the configuration that the underlying cache should use. * * @param config the configuration * @throws IllegalArgumentException if config is null. */ public void setConfiguration(Configuration config) { this.config = config; } /** * Allows direct injection of the underlying cache. * * @param cache */ public void setCache(Cache cache) { if (cacheStatus != CacheStatus.INSTANTIATED && cacheStatus != CacheStatus.CREATING && cacheStatus != CacheStatus.DESTROYED) { throw new IllegalStateException("Cannot set underlying cache after call to create()"); } this.cache = (CacheSPI) cache; this.config = (cache == null ? null : cache.getConfiguration()); synchronized (listenerCount) { if (listenerCount.get() > 0 && cache != null) { cache.addCacheListener(cacheNotificationListener); } } } public String getCacheObjectName() { if (cacheObjectName == null) { cacheObjectName = JmxUtil.getDefaultCacheObjectName(config, CacheSPI.class.getName()); } return cacheObjectName; } public void setCacheObjectName(String name) throws MalformedObjectNameException { if (name != null) { // test the name new ObjectName(name); } this.cacheObjectName = name; } /** * Gets whether sending of JMX notifications for this mbean's * start/stop lifecycle changes is disabled. * * @see #setDisableStateChangeNotifications(boolean) */ public boolean isDisableStateChangeNotifications() { return disableStateChangeNotifications; } /** * Hook to allow PojoCacheJmxWrapper to suppress state change * notifications from this mbean in lieu of its own. * * @param disableStateChangeNotifications * */ public void setDisableStateChangeNotifications(boolean disableStateChangeNotifications) { this.disableStateChangeNotifications = disableStateChangeNotifications; } public MBeanServer getMBeanServer() { return server; } /** * Formats a given String for display as an HTML snippet. * * @param s string to format * @return formatted string */ public static String formatHtml(String s) { s = s.replaceAll("\r\n", "
"); s = s.replaceAll("\r", "
"); s = s.replaceAll("\n", "
"); s = s.replaceAll("\t", "    "); s = s.replaceAll(" ", " "); return s; } public String getNotificationServiceName() { return notificationServiceName; } public void setNotificationServiceName(String notificationServiceName) { this.notificationServiceName = notificationServiceName; this.cacheNotificationListener.setServiceName(notificationServiceName); } public long getNextNotificationSequenceNumber() { return sequence.getAndIncrement(); } // ---------------------------------------------------- Superclass Overrides @Override public void addNotificationListener(NotificationListener notificationListener, NotificationFilter notificationFilter, Object object) throws IllegalArgumentException { super.addNotificationListener(notificationListener, notificationFilter, object); notificationRegistration(true); } @Override public void removeNotificationListener(NotificationListener notificationListener) throws ListenerNotFoundException { super.removeNotificationListener(notificationListener); notificationRegistration(false); } @Override public void removeNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback) throws ListenerNotFoundException { super.removeNotificationListener(listener, filter, handback); notificationRegistration(false); } @Override public MBeanNotificationInfo[] getNotificationInfo() { return CacheNotificationListener.getNotificationInfo(); } // ------------------------------------------------------ Protected Methods protected void constructCache() throws ConfigurationException { log.debug("Constructing Cache"); CacheFactory cf = new DefaultCacheFactory(); setCache(cf.createCache(config, false)); if (multiplexerService != null) { injectMuxChannel(); } } protected boolean registerInterceptors() throws CacheException { if (registerInterceptors && !interceptorsRegistered && server != null) { log.debug("Registering interceptors"); List interc = cache.getInterceptorChain(); if (interc != null && interc.size() > 0) { try { JmxUtil.registerInterceptors(server, interc, cacheObjectName); interceptorsRegistered = true; return true; } catch (JMException e) { throw new CacheException("Failed to register interceptors", e); } } } return false; } protected void unregisterInterceptors() { if (registerInterceptors && interceptorsRegistered && server != null) { try { log.debug("Unregistering interceptors"); JmxUtil.unregisterInterceptors(server, cache.getInterceptorChain(), getCacheObjectName()); interceptorsRegistered = false; } catch (Exception e) { log.error("Exception unregistering interceptors from JMX", e); } } } // -------------------------------------------------------- Private methods private void injectMuxChannel() throws CacheException { Configuration cfg = getConfiguration(); RuntimeConfig rtcfg = cfg.getRuntimeConfig(); // Only inject if there isn't already a channel or factory if (rtcfg.getMuxChannelFactory() == null && rtcfg.getChannel() == null) { Channel ch; try { ch = multiplexerService.createMultiplexerChannel(cfg.getMultiplexerStack(), cfg.getClusterName()); } catch (Exception e) { throw new CacheException("Exception creating multiplexed channel", e); } rtcfg.setChannel(ch); } } /** * Adds and removes the CacheListener. * A counter is used to determine whether we have any clients who are * registered for notifications from this mbean. When the count is zero, * we don't need to listen to cache events, so we remove the CacheListener. * Note that a client who terminates without unregistering for notifications * will leave the count greater than zero so we'll still listen in that case. * * @param add true if the event was a listerner addition, * false if it was a removal */ private void notificationRegistration(boolean add) { // This method adds and removes the CacheImpl listener. // The m_listeners counter is used to determine whether // we have any clients who are registered for notifications // from this mbean. When the count is zero, we don't need to // listen to cache events. Note that a client who terminates // without unregistering for notifications will leave the count // greater than zero so we'll still listen in that case. synchronized (listenerCount) { if (add) { listenerCount.incrementAndGet(); if (cache != null) cache.addCacheListener(cacheNotificationListener); } else { if (listenerCount.decrementAndGet() <= 0) { if (cache != null) cache.removeCacheListener(cacheNotificationListener); listenerCount.set(0); } } } } /** * Sets the cacheStatus to FAILED and rethrows the problem as one * of the declared types. Converts any non-RuntimeException Exception * to CacheException. * * @param t * @throws CacheException * @throws RuntimeException * @throws Error */ private void handleLifecycleTransitionFailure(Throwable t) throws RuntimeException, Error { int oldState = getState(); cacheStatus = CacheStatus.FAILED; sendStateChangeNotification(oldState, getState(), getClass().getSimpleName() + " failed", t); if (t instanceof CacheException) throw (CacheException) t; else if (t instanceof RuntimeException) throw (RuntimeException) t; else if (t instanceof Error) throw (Error) t; else throw new CacheException(t); } /** * Helper for sending out state change notifications */ private void sendStateChangeNotification(int oldState, int newState, String msg, Throwable t) { if (isDisableStateChangeNotifications()) return; long now = System.currentTimeMillis(); AttributeChangeNotification stateChangeNotification = new AttributeChangeNotification( this, getNextNotificationSequenceNumber(), now, msg, "State", "java.lang.Integer", oldState, newState); stateChangeNotification.setUserData(t); sendNotification(stateChangeNotification); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/jmx/CacheNotificationBroadcaster.java0000644000175000017500000000240011005374170031560 0ustar twernertwernerpackage org.jboss.cache.jmx; import javax.management.Notification; import javax.management.NotificationEmitter; public interface CacheNotificationBroadcaster extends NotificationEmitter { // Notification Types String NOTIF_CACHE_STARTED = "org.jboss.cache.CacheStarted", NOTIF_CACHE_STOPPED = "org.jboss.cache.CacheStopped", NOTIF_NODE_CREATED = "org.jboss.cache.NodeCreated", NOTIF_NODE_MODIFIED = "org.jboss.cache.NodeModified", NOTIF_NODE_REMOVED = "org.jboss.cache.NodeRemoved", NOTIF_NODE_MOVED = "org.jboss.cache.NodeMoved", NOTIF_NODE_VISITED = "org.jboss.cache.NodeVisited", NOTIF_NODE_EVICTED = "org.jboss.cache.NodeEvicted", NOTIF_NODE_LOADED = "org.jboss.cache.NodeLoaded", NOTIF_NODE_ACTIVATED = "org.jboss.cache.NodeActivated", NOTIF_NODE_PASSIVATED = "org.jboss.cache.NodePassivated", NOTIF_VIEW_CHANGED = "org.jboss.cache.ViewChanged"; /** * Sends a notification to any interested NotificationListener. * * @param notification the notification to send */ void sendNotification(Notification notification); /** * Gets the sequence number for the next notification. */ long getNextNotificationSequenceNumber(); } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/jmx/CacheNotificationListener.java0000644000175000017500000002150710660643050031127 0ustar twernertwerner/* * JBoss, Home of Professional Open Source. * Copyright 2006, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.cache.jmx; import org.jboss.cache.notifications.annotation.CacheListener; import org.jboss.cache.notifications.annotation.CacheStarted; import org.jboss.cache.notifications.annotation.CacheStopped; import org.jboss.cache.notifications.annotation.NodeActivated; import org.jboss.cache.notifications.annotation.NodeCreated; import org.jboss.cache.notifications.annotation.NodeEvicted; import org.jboss.cache.notifications.annotation.NodeLoaded; import org.jboss.cache.notifications.annotation.NodeModified; import org.jboss.cache.notifications.annotation.NodeMoved; import org.jboss.cache.notifications.annotation.NodePassivated; import org.jboss.cache.notifications.annotation.NodeRemoved; import org.jboss.cache.notifications.annotation.NodeVisited; import org.jboss.cache.notifications.annotation.ViewChanged; import org.jboss.cache.notifications.event.Event; import org.jboss.cache.notifications.event.NodeEvent; import org.jboss.cache.notifications.event.NodeMovedEvent; import org.jboss.cache.notifications.event.ViewChangedEvent; import javax.management.MBeanNotificationInfo; import javax.management.Notification; /** * A CacheListener that creates JMX notifications from listener * events. * * @author Brian Stansberry * @version $Revision: 4298 $ */ @CacheListener public class CacheNotificationListener { // Notification Messages private static final String MSG_CACHE_STARTED = "Cache has been started."; private static final String MSG_CACHE_STOPPED = "Cache has been stopped."; private static final String MSG_NODE_CREATED = "Node has been created."; private static final String MSG_NODE_MODIFIED = "Node has been modifed."; private static final String MSG_NODE_REMOVED = "Node has been removed."; private static final String MSG_NODE_MOVED = "Node has been moved."; private static final String MSG_NODE_VISITED = "Node has been visited."; private static final String MSG_NODE_EVICTED = "Node has been evicted."; private static final String MSG_NODE_LOADED = "Node has been loaded."; private static final String MSG_NODE_ACTIVATED = "Node has been activated."; private static final String MSG_NODE_PASSIVATED = "Node has been passivated."; private static final String MSG_VIEW_CHANGED = "Cache cluster view has changed."; // Notification Info private static final String NOTIFICATION_NAME = Notification.class.getName(); private static final String NOTIFICATION_DESCR = "JBossCache event notifications"; private final CacheNotificationBroadcaster broadcaster; private String serviceName; // ------------------------------------------------------------ Constructors CacheNotificationListener(CacheNotificationBroadcaster support) { this.broadcaster = support; } // ----------------------------------------------------------- PublicMethods public String getServiceName() { return serviceName; } public void setServiceName(String serviceName) { this.serviceName = serviceName; } public static MBeanNotificationInfo[] getNotificationInfo() { String[] types = new String[] { CacheNotificationBroadcaster.NOTIF_CACHE_STARTED, CacheNotificationBroadcaster.NOTIF_CACHE_STOPPED, CacheNotificationBroadcaster.NOTIF_NODE_CREATED, CacheNotificationBroadcaster.NOTIF_NODE_EVICTED, CacheNotificationBroadcaster.NOTIF_NODE_LOADED, CacheNotificationBroadcaster.NOTIF_NODE_MODIFIED, CacheNotificationBroadcaster.NOTIF_NODE_ACTIVATED, CacheNotificationBroadcaster.NOTIF_NODE_PASSIVATED, CacheNotificationBroadcaster.NOTIF_NODE_REMOVED, CacheNotificationBroadcaster.NOTIF_NODE_VISITED, CacheNotificationBroadcaster.NOTIF_VIEW_CHANGED, }; MBeanNotificationInfo info = new MBeanNotificationInfo(types, NOTIFICATION_NAME, NOTIFICATION_DESCR); return new MBeanNotificationInfo[]{info}; } // ----------------------------------------------------------- CacheListener @CacheStarted @CacheStopped @NodeCreated @NodeEvicted @NodeLoaded @NodeModified @NodeRemoved @NodeMoved @NodeVisited @NodeActivated @NodePassivated @ViewChanged public void broadcast(Event e) { NodeEvent ne; Notification n = null; switch (e.getType()) { case CACHE_STARTED: n = new Notification(CacheNotificationBroadcaster.NOTIF_CACHE_STARTED, broadcaster, seq(), MSG_CACHE_STARTED); n.setUserData(serviceName); break; case CACHE_STOPPED: n = new Notification(CacheNotificationBroadcaster.NOTIF_CACHE_STOPPED, broadcaster, seq(), MSG_CACHE_STOPPED); n.setUserData(serviceName); break; case NODE_CREATED: n = new Notification(CacheNotificationBroadcaster.NOTIF_NODE_CREATED, broadcaster, seq(), MSG_NODE_CREATED); ne = (NodeEvent) e; n.setUserData(new Object[]{ne.getFqn().toString(), e.isPre(), ne.isOriginLocal()}); break; case NODE_EVICTED: n = new Notification(CacheNotificationBroadcaster.NOTIF_NODE_EVICTED, broadcaster, seq(), MSG_NODE_EVICTED); ne = (NodeEvent) e; n.setUserData(new Object[]{ne.getFqn().toString(), e.isPre(), ne.isOriginLocal()}); break; case NODE_LOADED: n = new Notification(CacheNotificationBroadcaster.NOTIF_NODE_LOADED, broadcaster, seq(), MSG_NODE_LOADED); ne = (NodeEvent) e; n.setUserData(new Object[]{ne.getFqn().toString(), e.isPre()}); break; case NODE_MODIFIED: n = new Notification(CacheNotificationBroadcaster.NOTIF_NODE_MODIFIED, broadcaster, seq(), MSG_NODE_MODIFIED); ne = (NodeEvent) e; n.setUserData(new Object[]{ne.getFqn().toString(), e.isPre(), ne.isOriginLocal()}); break; case NODE_REMOVED: n = new Notification(CacheNotificationBroadcaster.NOTIF_NODE_REMOVED, broadcaster, seq(), MSG_NODE_REMOVED); ne = (NodeEvent) e; n.setUserData(new Object[]{ne.getFqn().toString(), e.isPre(), ne.isOriginLocal()}); break; case NODE_MOVED: n = new Notification(CacheNotificationBroadcaster.NOTIF_NODE_MOVED, broadcaster, seq(), MSG_NODE_MOVED); NodeMovedEvent nme = (NodeMovedEvent) e; n.setUserData(new Object[]{nme.getFqn().toString(), nme.getTargetFqn().toString(), e.isPre()}); break; case NODE_VISITED: n = new Notification(CacheNotificationBroadcaster.NOTIF_NODE_VISITED, broadcaster, seq(), MSG_NODE_VISITED); ne = (NodeEvent) e; n.setUserData(new Object[]{ne.getFqn().toString(), e.isPre()}); break; case NODE_ACTIVATED: n = new Notification(CacheNotificationBroadcaster.NOTIF_NODE_ACTIVATED, broadcaster, seq(), MSG_NODE_ACTIVATED); ne = (NodeEvent) e; n.setUserData(new Object[]{ne.getFqn().toString(), e.isPre()}); break; case NODE_PASSIVATED: n = new Notification(CacheNotificationBroadcaster.NOTIF_NODE_PASSIVATED, broadcaster, seq(), MSG_NODE_PASSIVATED); ne = (NodeEvent) e; n.setUserData(new Object[]{ne.getFqn().toString(), e.isPre()}); break; case VIEW_CHANGED: n = new Notification(CacheNotificationBroadcaster.NOTIF_VIEW_CHANGED, broadcaster, seq(), MSG_VIEW_CHANGED); n.setUserData(((ViewChangedEvent) e).getNewView().toString()); break; } broadcaster.sendNotification(n); } private long seq() { return broadcaster.getNextNotificationSequenceNumber(); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/jmx/JmxUtil.java0000644000175000017500000001447211032145203025435 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * Copyright 2005, JBoss Inc., and individual contributors as indicated * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.cache.jmx; import org.jboss.cache.config.Configuration; import org.jboss.cache.interceptors.base.CommandInterceptor; import javax.management.JMException; import javax.management.MBeanServer; import javax.management.ObjectName; import java.util.List; /** * Various JMX related utilities * * @author Jerry Gauthier * @author Manik Surtani * @version $Id: JmxUtil.java 6120 2008-06-30 11:58:59Z manik.surtani@jboss.com $ */ public class JmxUtil { public static final String JBOSS_SERVER_DOMAIN = "jboss"; public static final String JBOSS_CACHE_DOMAIN = "jboss.cache"; public static final String SERVICE_KEY_NAME = "service"; public static final String BASE_PREFIX = JBOSS_CACHE_DOMAIN + ":" + SERVICE_KEY_NAME + "=JBossCache"; public static final String CLUSTER_KEY = "cluster"; public static final String PREFIX = BASE_PREFIX + "," + CLUSTER_KEY + "="; public static final String UNIQUE_ID_KEY = "uniqueId"; public static final String NO_CLUSTER_PREFIX = BASE_PREFIX + "," + UNIQUE_ID_KEY + "="; public static final String CACHE_TYPE_KEY = "cacheType"; public static final String PLAIN_CACHE_TYPE = "Cache"; public static final String MBEAN_CLASS_SUFFIX = "MBean"; public static final String INTERCEPTOR_KEY = ",cache-interceptor="; public static void registerCacheMBean(MBeanServer server, CacheJmxWrapperMBean cache, String cacheObjectName) throws JMException { ObjectName on = new ObjectName(cacheObjectName); if (!server.isRegistered(on)) { server.registerMBean(cache, on); } } /* * Register the associated mbeans for cache interceptors * * @param server the mbean server with which the mbeans should be registered * @param cache the cache having the set of interceptors to be registered * @param registerCache whether the cache itself should be registered */ public static void registerInterceptors(MBeanServer server, List interceptors, String cacheObjectName) throws JMException { if (server == null || interceptors == null || cacheObjectName == null) { return; } for (CommandInterceptor interceptor : interceptors) { if (!interceptor.getStatisticsEnabled()) continue; boolean mbeanExists = true; try { // the mbean for interceptor AbcInterceptor will be named AbcInterceptorMBean Class.forName(interceptor.getClass().getName() + MBEAN_CLASS_SUFFIX); } catch (Throwable e) { // if the class can't be instantiated, no mbean is available mbeanExists = false; } // for JDK 1.4, must parse name and remove package prefix // for JDK 1.5, can use getSimpleName() to establish class name without package prefix String className = interceptor.getClass().getName(); String serviceName = cacheObjectName + INTERCEPTOR_KEY + className.substring(className.lastIndexOf('.') + 1); ObjectName objName = new ObjectName(serviceName); if (mbeanExists && !server.isRegistered(objName)) { // register associated interceptor mbean server.registerMBean(interceptor, objName); } } } public static String getDefaultCacheObjectName(org.jboss.cache.Cache cache) { // get the cache's registration name return getDefaultCacheObjectName(cache.getConfiguration(), cache.getClass().getName()); } public static String getDefaultCacheObjectName(Configuration config, String cacheImplClass) { // get the cache's registration name String tmpName; if (config.getClusterName() == null) { tmpName = NO_CLUSTER_PREFIX + getUniqueId(cacheImplClass); } else { tmpName = PREFIX + config.getClusterName(); } return tmpName; } public static String getUniqueId(String cacheImplClass) { return cacheImplClass + System.currentTimeMillis(); } public static void unregisterCacheMBean(MBeanServer server, String cacheObjectName) throws Exception { server.unregisterMBean(new ObjectName(cacheObjectName)); } /* * Unregister the associated mbeans for cache interceptors * * @param server the mbean server for which the mbeans should be unregistered * @param cache the cache having the set of interceptors to be unregistered * @param unregisterCache whether the cache itself should be unregistered */ public static void unregisterInterceptors(MBeanServer server, List interceptors, String cacheObjectName) throws Exception { if (server == null || interceptors == null || cacheObjectName == null) { return; } for (CommandInterceptor interceptor : interceptors) { // for JDK 1.4, must parse name and remove package prefix // for JDK 1.5, can use getSimpleName() to establish class name without package prefix String className = interceptor.getClass().getName(); String serviceName = cacheObjectName + INTERCEPTOR_KEY + className.substring(className.lastIndexOf('.') + 1); ObjectName objName = new ObjectName(serviceName); if (server.isRegistered(objName)) { server.unregisterMBean(objName); } } } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/jmx/CacheJmxWrapperMBean.java0000644000175000017500000001105711005374170027772 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.jmx; import org.jboss.cache.Cache; import org.jboss.cache.CacheException; import org.jboss.cache.CacheStatus; import org.jboss.cache.config.Configuration; import org.jgroups.Address; import java.util.List; /** * JMX interface to the {@link org.jboss.cache.Cache}. Full access to the cache is not supported, only a certain * set of operations are exposed via JMX: *

*

    *
  1. Lifecycle methods - create, start, stop, destroy
  2. *
  3. Configuration (read-only) getter - which retrieves a String (or formatted HTML for web based JMX consoles) representation of the configuration
  4. *
  5. Properties exposing {@link Configuration} elements
  6. *
  7. Cache information methods (numNodes, numAttributes, lockInfo, printDetails) which print as Strings or as formatted HTML (for web based JMX consoles)
  8. * * @since 2.0.0 */ public interface CacheJmxWrapperMBean extends LegacyConfiguration { /** * The lifecycle method stop has completed */ int STOPPED = 0; /** * The lifecycle method stop has been invoked */ int STOPPING = 1; /** * The lifecycle method start has been invoked */ int STARTING = 2; /** * The lifecycle method start has completed */ int STARTED = 3; /** * There has been an error during some operation */ int FAILED = 4; /** * The lifecycle method destroy has completed */ int DESTROYED = 5; /** * The lifecycle method create has completed */ int CREATED = 6; /** * The MBean has been instantiated but has not completed MBeanRegistration.postRegister */ int UNREGISTERED = 7; /** * The MBean has been instantiated and has completed MBeanRegistration.postRegister */ int REGISTERED = 8; void create() throws CacheException; void start() throws CacheException; void stop(); void destroy(); /** * Gets where this object is in its lifecycle transitions. * * @return the current status. Will not return null */ CacheStatus getCacheStatus(); /** * Legacy attribute to expose the {@link #getCacheStatus() cache status} * in terms of the JBoss AS ServiceMBean values. This interface does * not extend ServiceMBean, but this attribute is retained to provide * compatibility with the JBoss AS JSR-77 integration layer. * * @return the current status, e.g. {@link #STARTED}. */ int getState(); /** * Retrieves a reference to the underlying {@link Cache} */ Cache getCache(); /** * @return an immutable configuration */ Configuration getConfiguration(); /** * @return a string based representation of the configuration */ String printConfigurationAsString(); /** * @return an HTML formatted string based representation of the configuration */ String printConfigurationAsHtmlString(); /** * @return details of nodes in the cache */ String printCacheDetails(); /** * @return details of nodes in the cache, formatted as HTML */ String printCacheDetailsAsHtml(); /** * Returns the local address of this cache in a cluster, or null * if running in local mode. * * @return the local address of this cache in a cluster, or null * if running in local mode. */ Address getLocalAddress(); /** * Returns a list of members in the cluster, or null * if running in local mode. * * @return a {@link List} of members in the cluster, or null * if running in local mode. */ List
    getMembers(); /** * @return number of nodes in the cache */ int getNumberOfNodes(); /** * @return number of attributes in the cache */ int getNumberOfAttributes(); /** * @return information on the state of node locks */ String printLockInfo(); /** * @return information on the state of node locks, formatted as HTML */ String printLockInfoAsHtml(); /** * Gets whether this object should register the cache's interceptors * with JMX during {@link {@link #create()}. *

    * Default is true. */ boolean getRegisterInterceptors(); /** * Sets whether this object should register the cache's interceptors * with JMX during {@link #create()}. *

    * Default is true. */ void setRegisterInterceptors(boolean register); } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/jmx/LegacyConfiguration.java0000644000175000017500000002724310634264571030016 0ustar twernertwerner/* * JBoss, Home of Professional Open Source. * Copyright 2007, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.cache.jmx; import org.jboss.cache.Region; import org.jgroups.ChannelFactory; import org.jgroups.jmx.JChannelFactoryMBean; import org.w3c.dom.Element; import javax.transaction.TransactionManager; /** * Legacy configuration attributes from JBC 1.x. * * @author Brian Stansberry * @version $Revision: 4016 $ */ public interface LegacyConfiguration { /** * Get the name of the replication group */ String getClusterName(); /** * Set the name of the replication group */ void setClusterName(String name); /** * Sets whether marshalling uses scoped class loaders on a per region basis. *

    * This property must be set to true before any call to * {@link Region#registerContextClassLoader(ClassLoader)}. * * @param isTrue */ void setUseRegionBasedMarshalling(boolean isTrue); /** * Gets whether marshalling uses scoped class loaders on a per region basis. * * @return true if region based marshalling is used. */ boolean getUseRegionBasedMarshalling(); /** * Gets whether the cache should create interceptor mbeans * that are used to capture and publish interceptor statistics. * * @return true if mbeans should be created for each interceptor */ boolean getExposeManagementStatistics(); void setExposeManagementStatistics(boolean expose); /** * @deprecated use {@link #getExposeManagementStatistics()} */ @Deprecated boolean getUseInterceptorMbeans(); /** * @deprecated use {@link #setExposeManagementStatistics(boolean)} */ @Deprecated void setUseInterceptorMbeans(boolean expose); /** * Get the cluster properties (e.g. the protocol stack specification in case of JGroups) */ String getClusterProperties(); /** * Set the cluster properties. If the cache is to use the new properties, it has to be redeployed * * @param cluster_props The properties for the cluster (JGroups) */ void setClusterProperties(String cluster_props); /** * Retrieves the cache loader configuration element * * @return whatever was passed to {@link #setCacheLoaderConfig(Element)} * or null if nothing was */ Element getCacheLoaderConfig(); void setCacheLoaderConfig(Element cache_loader_config); /** * @deprecated use {@link #getCacheLoaderConfig()} */ @Deprecated Element getCacheLoaderConfiguration(); /** * @deprecated use {@link #setCacheLoaderConfig(org.w3c.dom.Element)} */ @Deprecated void setCacheLoaderConfiguration(Element cache_loader_config); boolean getSyncCommitPhase(); void setSyncCommitPhase(boolean sync_commit_phase); boolean getSyncRollbackPhase(); void setSyncRollbackPhase(boolean sync_rollback_phase); /** * @return whatever was passed to {@link #setEvictionPolicyConfig(Element)} * or null if nothing was */ Element getEvictionPolicyConfig(); /** * Setup eviction policy configuration */ void setEvictionPolicyConfig(Element config); /** * Gets the JGroups protocol stack config in W3C DOM Element form. * * @return the protocol stack, or null if it was not * set via {@link #setClusterConfig(Element)} */ Element getClusterConfig(); /** * Convert a list of elements to the JG property string */ void setClusterConfig(Element config); /** * Get the max time to wait until the initial state is retrieved. This is used in a replicating cache: when a new cache joins the cluster, it needs to acquire the (replicated) state of the other members to initialize itself. If no state has been received within timeout milliseconds, the map will be empty. * * @return long Number of milliseconds to wait for the state. 0 means to wait forever. * @deprecated use {@link #getStateRetrievalTimeout()} */ @Deprecated long getInitialStateRetrievalTimeout(); /** * Get the max time to wait until the state is retrieved. This is used in a replicating cache: when a new cache joins the cluster, it needs to acquire the (replicated) state of the other members to initialize itself. If no state has been received within timeout milliseconds, the map will be empty. * * @return long Number of milliseconds to wait for the state. 0 means to wait forever. */ long getStateRetrievalTimeout(); /** * Set the initial state transfer timeout (see {@link #getInitialStateRetrievalTimeout()}) * * @deprecated use {@link #setStateRetrievalTimeout(long)} */ @Deprecated void setInitialStateRetrievalTimeout(long timeout); /** * Set the state transfer timeout (see {@link #getStateRetrievalTimeout()}) */ void setStateRetrievalTimeout(long timeout); /** * Returns the current caching mode. Valid values are

    • LOCAL
    • REPL_ASYNC
    • REPL_SYNC
        * * @return String The caching mode */ String getCacheMode(); /** * Sets the default caching mode) */ void setCacheMode(String mode) throws Exception; /** * Returns the default max timeout after which synchronous replication calls return. * * @return long Number of milliseconds after which a sync repl call must return. 0 means to wait forever */ long getSyncReplTimeout(); /** * Sets the default maximum wait time for synchronous replication to receive all results */ void setSyncReplTimeout(long timeout); boolean getUseReplQueue(); void setUseReplQueue(boolean flag); long getReplQueueInterval(); void setReplQueueInterval(long interval); int getReplQueueMaxElements(); void setReplQueueMaxElements(int max_elements); /** * Returns the transaction isolation level. */ String getIsolationLevel(); /** * Set the transaction isolation level. This determines the locking strategy to be used */ void setIsolationLevel(String level); /** * Returns whether or not any initial state transfer or subsequent partial * state transfer following an activateRegion call should * include in-memory state. Allows for warm/hot caches (true/false). The * characteristics of a state transfer can be further defined by a cache * loader's FetchPersistentState property. */ boolean getFetchInMemoryState(); /** * Sets whether or not any initial or subsequent partial state transfer * should include in-memory state. */ void setFetchInMemoryState(boolean flag); /** * Gets the format version of the data transferred during an initial state * transfer or a call to {@link Region#activate()}. Different * releases of JBossCache may format this data differently; this property * identifies the format version being used by this cache instance. *

        * The default value for this property is * {@link org.jboss.cache.config.Configuration#DEFAULT_REPLICATION_VERSION}. *

        * * @return a short identifying JBossCache release; e.g. 124 * for JBossCache 1.2.4 */ String getReplicationVersion(); /** * Sets the format version of the data transferred during an initial state * transfer or a call to {@link Region#activate()}. Different * releases of JBossCache may format this data differently; this property * identifies the format version being used by this cache instance. Setting * this property to a value other than the default allows a cache instance * from a later release to interoperate with a cache instance from an * earlier release. * * @param version a short identifying JBossCache release; * e.g. 124 for JBossCache 1.2.4 */ void setReplicationVersion(String version); /** * Default max time to wait for a lock. If the lock cannot be acquired within this time, a LockingException will be thrown. * * @return long Max number of milliseconds to wait for a lock to be acquired */ long getLockAcquisitionTimeout(); /** * Set the max time for lock acquisition. A value of 0 means to wait forever (not recomended). Note that lock acquisition timeouts may be removed in the future when we have deadlock detection. * * @param timeout */ void setLockAcquisitionTimeout(long timeout); String getTransactionManagerLookupClass(); /** * Sets the class of the TransactionManagerLookup impl. This will attempt to create an instance, and will throw an exception if this fails. * * @param cl * @throws Exception */ void setTransactionManagerLookupClass(String cl) throws Exception; TransactionManager getTransactionManager(); void setTransactionManager(TransactionManager manager); void setNodeLockingScheme(String nodeLockingScheme); String getNodeLockingScheme(); /** * Gets whether the entire tree is inactive upon startup, only responding * to replication messages after activateRegion is * called to activate one or more parts of the tree. *

        * This property is only relevant if {@link org.jboss.cache.config.Configuration#isUseRegionBasedMarshalling()} is * true. */ boolean isInactiveOnStartup(); /** * Sets whether the entire tree is inactive upon startup, only responding * to replication messages after {@link Region#activate()} is * called to activate one or more parts of the tree. *

        * This property is only relevant if {@link org.jboss.cache.config.Configuration#isUseRegionBasedMarshalling()} is * true. */ void setInactiveOnStartup(boolean inactiveOnStartup); /** * Sets the buddy replication configuration element * * @param config */ void setBuddyReplicationConfig(Element config); /** * Retrieves the buddy replication configuration element * * @return whatever was passed to {@link #setBuddyReplicationConfig(Element)} * or null if nothing was */ Element getBuddyReplicationConfig(); /** * Retrieves the JGroups multiplexer stack name if defined. * * @return the multiplexer stack name */ String getMultiplexerStack(); /** * Used with JGroups multiplexer, specifies stack to be used (e.g., fc-fast-minimalthreads) * This attribute is optional; if not provided, a default multiplexer stack will be used. * * @param stackName the name of the multiplexer stack */ void setMultiplexerStack(String stackName); ChannelFactory getMuxChannelFactory(); void setMuxChannelFactory(ChannelFactory factory); JChannelFactoryMBean getMultiplexerService(); void setMultiplexerService(JChannelFactoryMBean muxService); }jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/io/0000755000175000017500000000000011376174033023015 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/io/ExposedByteArrayOutputStream.java0000644000175000017500000001201011075062420031471 0ustar twernertwerner/* * JBoss, Home of Professional Open Source. * Copyright 2000 - 2008, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.cache.io; import net.jcip.annotations.NotThreadSafe; import java.io.ByteArrayOutputStream; /** * Extends ByteArrayOutputStream, but exposes the internal buffer. * Using this, callers don't need to call toByteArray() which copies the * internal buffer. *

        * Also overrides the superclass' behavior of always doubling the size of the * internal buffer any time more capacity is needed. This class doubles the * size until the internal buffer reaches a configurable max size (default is * 4MB), after which it begins growing the buffer in 25% increments. This is * intended to help prevent an OutOfMemoryError during a resize of a large * buffer. *

        *

        * A version of this class was originally created by Bela Ban as part of the * JGroups library. *

        * This class is not threadsafe as it will not support concurrent readers and writers. *

        * * @author Brian Stansberry * @version $Id$ */ @NotThreadSafe public class ExposedByteArrayOutputStream extends ByteArrayOutputStream { /** * Default buffer size after which if more buffer capacity * is needed the buffer will grow by 25% rather than 100% */ public static final int DEFAULT_DOUBLING_SIZE = 4 * 1024 * 1024; // 4MB private int maxDoublingSize = DEFAULT_DOUBLING_SIZE; public ExposedByteArrayOutputStream() { super(); } public ExposedByteArrayOutputStream(int size) { super(size); } /** * Creates a new byte array output stream, with a buffer capacity of * the specified size, in bytes. * * @param size the initial size. * @param maxDoublingSize the buffer size, after which if more capacity * is needed the buffer will grow by 25% * rather than 100% * @throws IllegalArgumentException if size is negative. */ public ExposedByteArrayOutputStream(int size, int maxDoublingSize) { super(size); this.maxDoublingSize = maxDoublingSize; } /** * Gets the internal buffer array. Note that the length of this array * will almost certainly be longer than the data written to it; call * size() to get the number of bytes of actual data. */ public final byte[] getRawBuffer() { return buf; } @Override public void write(byte[] b, int off, int len) { if ((off < 0) || (off > b.length) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0)) { throw new IndexOutOfBoundsException(); } else if (len == 0) { return; } int newcount = count + len; if (newcount > buf.length) { byte newbuf[] = new byte[getNewBufferSize(buf.length, newcount)]; System.arraycopy(buf, 0, newbuf, 0, count); buf = newbuf; } System.arraycopy(b, off, buf, count, len); count = newcount; } @Override public void write(int b) { int newcount = count + 1; if (newcount > buf.length) { byte newbuf[] = new byte[getNewBufferSize(buf.length, newcount)]; System.arraycopy(buf, 0, newbuf, 0, count); buf = newbuf; } buf[count] = (byte) b; count = newcount; } /** * Gets the highest internal buffer size after which if more capacity * is needed the buffer will grow in 25% increments rather than 100%. */ public final int getMaxDoublingSize() { return maxDoublingSize; } /** * Gets the number of bytes to which the internal buffer should be resized. * * @param curSize the current number of bytes * @param minNewSize the minimum number of bytes required * @return the size to which the internal buffer should be resized */ public final int getNewBufferSize(int curSize, int minNewSize) { if (curSize <= maxDoublingSize) return Math.max(curSize << 1, minNewSize); else return Math.max(curSize + (curSize >> 2), minNewSize); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/io/ByteBuffer.java0000644000175000017500000000301511075062420025704 0ustar twernertwerner/* * JBoss, Home of Professional Open Source. * Copyright 2000 - 2008, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.cache.io; import org.jgroups.util.Buffer; import java.io.ByteArrayInputStream; import java.io.InputStream; /** * A subclass of a JGroups Buffer */ public class ByteBuffer extends Buffer { public ByteBuffer(byte[] bytes, int offset, int length) { super(bytes, offset, length); } /** * @return an input stream for the bytes in the buffer */ public InputStream getStream() { return new ByteArrayInputStream(getBuf(), getOffset(), getLength()); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/RegionEmptyException.java0000644000175000017500000000107311017543331027364 0ustar twernertwernerpackage org.jboss.cache; /** * Exception to represent a region being empty when state was expected in that region. * * @author Manik Surtani (manik@jboss.org) * @since 2.2.0 */ public class RegionEmptyException extends CacheException { public RegionEmptyException() { } public RegionEmptyException(Throwable cause) { super(cause); } public RegionEmptyException(String msg) { super(msg); } public RegionEmptyException(String msg, Throwable cause) { super(msg, cause); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/TreeCacheViewMBean.java0000644000175000017500000000114410660427364026635 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache; import org.jboss.cache.jmx.CacheJmxWrapperMBean; /** * MBean interface. * * @author Bela Ban March 27 2003 */ public interface TreeCacheViewMBean { void create() throws Exception; void start() throws Exception; void stop() ; void destroy() ; Cache getCache(); void setCache(Cache cache); CacheJmxWrapperMBean getCacheService(); void setCacheService(CacheJmxWrapperMBean cache_service); } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/marshall/0000755000175000017500000000000011376174025024212 5ustar twernertwerner././@LongLink0000000000000000000000000000014600000000000011566 Lustar rootrootjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/marshall/InactiveRegionAwareRpcDispatcher.javajbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/marshall/InactiveRegionAwareRpcDispatcher.jav0000644000175000017500000000533311017533045033253 0ustar twernertwernerpackage org.jboss.cache.marshall; import org.jboss.cache.commands.ReplicableCommand; import org.jboss.cache.factories.ComponentRegistry; import org.jboss.cache.interceptors.InterceptorChain; import org.jboss.cache.invocation.InvocationContextContainer; import org.jgroups.Channel; import org.jgroups.MembershipListener; import org.jgroups.Message; import org.jgroups.MessageListener; import org.jgroups.blocks.RpcDispatcher; /** * Extends {@link org.jgroups.blocks.RpcDispatcher} and adds the possibility that the marshaller may throw {@link org.jboss.cache.marshall.InactiveRegionException}s. * * @author Manik Surtani * @since 2.0.0 */ public class InactiveRegionAwareRpcDispatcher extends CommandAwareRpcDispatcher { org.jboss.cache.marshall.Marshaller requestMarshaller; /** * Only provide the flavour of the {@link RpcDispatcher} constructor that we care about. */ public InactiveRegionAwareRpcDispatcher(Channel channel, MessageListener l, MembershipListener l2, Object serverObj, InvocationContextContainer container, InterceptorChain interceptorChain, ComponentRegistry componentRegistry) { super(channel, l, l2, serverObj, container, interceptorChain, componentRegistry); } @Override public void setRequestMarshaller(Marshaller m) { super.setRequestMarshaller(m); requestMarshaller = (org.jboss.cache.marshall.Marshaller) m; } /** * Message contains MethodCall. Execute it against *this* object and return result. * Use MethodCall.invoke() to do this. Return result. */ @Override public Object handle(Message req) { if (isValid(req)) { RegionalizedMethodCall rmc; ReplicableCommand command; try { // we will ALWAYS be using the marshaller to unmarshall requests. rmc = requestMarshaller.regionalizedMethodCallFromByteBuffer(req.getBuffer()); command = rmc.command; } catch (Throwable e) { if (e instanceof InactiveRegionException) { if (trace) log.trace("Exception from marshaller: " + e.getMessage()); return null; } if (trace) log.error("exception unmarshalling object", e); return e; } try { Object retVal = executeCommand(command, req); return new RegionalizedReturnValue(retVal, rmc); } catch (Throwable x) { if (trace) log.trace("Problems invoking command", x); return x; } } else { return null; } } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/marshall/CacheMarshaller210.java0000644000175000017500000000753210752205470030321 0ustar twernertwernerpackage org.jboss.cache.marshall; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; /** * An evolution of {@link org.jboss.cache.marshall.CacheMarshaller200}, created to fix JBCACHE-1211. *

        * To prevent ints taking too much space, they are written as variable-length ints. Details here on VInts. * * @author Manik Surtani * @see VInt * @see VLong * @since 2.1.0 */ public class CacheMarshaller210 extends CacheMarshaller200 { public CacheMarshaller210() { initLogger(); // disabled, since this is always disabled in JBC 2.0.0. // Java ObjectOutputStreams will have it's own built-in ref counting. No need to repeat this. useRefs = false; } /** * This version of writeReference is written to solve JBCACHE-1211, where references are encoded as ints rather than shorts. * * @param out stream to write to * @param reference reference to write * @throws IOException propagated from OOS * @see JBCACHE-1211 */ @Override protected void writeReference(ObjectOutputStream out, int reference) throws IOException { writeUnsignedInt(out, reference); } /** * This version of readReference is written to solve JBCACHE-1211, where references are encoded as ints rather than shorts. * * @param in stream to read from * @return reference * @throws IOException propagated from OUS * @see JBCACHE-1211 */ @Override protected int readReference(ObjectInputStream in) throws IOException { return readUnsignedInt(in); } /** * Reads an int stored in variable-length format. Reads between one and * five bytes. Smaller values take fewer bytes. Negative numbers are not * supported. */ @Override protected int readUnsignedInt(ObjectInputStream in) throws IOException { byte b = in.readByte(); int i = b & 0x7F; for (int shift = 7; (b & 0x80) != 0; shift += 7) { b = in.readByte(); i |= (b & 0x7FL) << shift; } return i; } /** * Writes an int in a variable-length format. Writes between one and * five bytes. Smaller values take fewer bytes. Negative numbers are not * supported. * * @param i int to write */ @Override protected void writeUnsignedInt(ObjectOutputStream out, int i) throws IOException { while ((i & ~0x7F) != 0) { out.writeByte((byte) ((i & 0x7f) | 0x80)); i >>>= 7; } out.writeByte((byte) i); } /** * Reads an int stored in variable-length format. Reads between one and * nine bytes. Smaller values take fewer bytes. Negative numbers are not * supported. */ @Override protected long readUnsignedLong(ObjectInputStream in) throws IOException { byte b = in.readByte(); long i = b & 0x7F; for (int shift = 7; (b & 0x80) != 0; shift += 7) { b = in.readByte(); i |= (b & 0x7FL) << shift; } return i; } /** * Writes an int in a variable-length format. Writes between one and * nine bytes. Smaller values take fewer bytes. Negative numbers are not * supported. * * @param i int to write */ @Override protected void writeUnsignedLong(ObjectOutputStream out, long i) throws IOException { while ((i & ~0x7F) != 0) { out.writeByte((byte) ((i & 0x7f) | 0x80)); i >>>= 7; } out.writeByte((byte) i); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/marshall/MarshalledValueMap.java0000644000175000017500000000702610745367774030607 0ustar twernertwernerpackage org.jboss.cache.marshall; import net.jcip.annotations.Immutable; import org.jboss.cache.CacheException; import java.io.Externalizable; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Set; /** * A Map that is able to wrap/unwrap MarshalledValues in keys or values. Note that calling keySet(), entrySet() or values() * could be expensive if this map is large!! *

        * Also note that this is an immutable Map. *

        * * @author Manik Surtani (manik@jboss.org) * @see MarshalledValue * @since 2.1.0 */ @Immutable public class MarshalledValueMap implements Map, Externalizable { Map delegate; Map unmarshalled; public MarshalledValueMap() { // for externalization } public MarshalledValueMap(Map delegate) { this.delegate = delegate; } @SuppressWarnings("unchecked") protected synchronized Map getUnmarshalledMap() { if (unmarshalled == null) unmarshalled = unmarshalledMap(delegate.entrySet()); return unmarshalled; } public int size() { return delegate.size(); } public boolean isEmpty() { return delegate.isEmpty(); } public boolean containsKey(Object key) { return getUnmarshalledMap().containsKey(key); } public boolean containsValue(Object value) { return getUnmarshalledMap().containsValue(value); } public Object get(Object key) { return getUnmarshalledMap().get(key); } public Object put(Object key, Object value) { throw new UnsupportedOperationException("This is an immutable map!"); } public Object remove(Object key) { throw new UnsupportedOperationException("This is an immutable map!"); } public void putAll(Map t) { throw new UnsupportedOperationException("This is an immutable map!"); } public void clear() { throw new UnsupportedOperationException("This is an immutable map!"); } public Set keySet() { return getUnmarshalledMap().keySet(); } public Collection values() { return getUnmarshalledMap().values(); } public Set entrySet() { return getUnmarshalledMap().entrySet(); } @SuppressWarnings("unchecked") protected Map unmarshalledMap(Set entries) { if (entries == null || entries.isEmpty()) return Collections.emptyMap(); Map map = new HashMap(entries.size()); for (Object e : entries) { Map.Entry entry = (Map.Entry) e; map.put(getUnmarshalledValue(entry.getKey()), getUnmarshalledValue(entry.getValue())); } return map; } private Object getUnmarshalledValue(Object o) { try { return o instanceof MarshalledValue ? ((MarshalledValue) o).get() : o; } catch (Exception e) { throw new CacheException("Unable to unmarshall value", e); } } @Override public boolean equals(Object other) { if (other instanceof Map) { return getUnmarshalledMap().equals(other); } return false; } @Override public int hashCode() { return getUnmarshalledMap().hashCode(); } public void writeExternal(ObjectOutput out) throws IOException { out.writeObject(delegate); } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { delegate = (Map) in.readObject(); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/marshall/AbstractMarshaller.java0000644000175000017500000001644511075062420030634 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.marshall; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.Fqn; import org.jboss.cache.RegionManager; import org.jboss.cache.commands.DataCommand; import org.jboss.cache.commands.ReplicableCommand; import org.jboss.cache.commands.ReversibleCommand; import org.jboss.cache.commands.read.ExistsCommand; import org.jboss.cache.commands.read.GetChildrenNamesCommand; import org.jboss.cache.commands.read.GetDataMapCommand; import org.jboss.cache.commands.read.GetKeyValueCommand; import org.jboss.cache.commands.read.GetKeysCommand; import org.jboss.cache.commands.read.GravitateDataCommand; import org.jboss.cache.commands.remote.AnnounceBuddyPoolNameCommand; import org.jboss.cache.commands.remote.AssignToBuddyGroupCommand; import org.jboss.cache.commands.remote.ClusteredGetCommand; import org.jboss.cache.commands.remote.DataGravitationCleanupCommand; import org.jboss.cache.commands.remote.RemoveFromBuddyGroupCommand; import org.jboss.cache.commands.remote.ReplicateCommand; import org.jboss.cache.commands.tx.AbstractTransactionCommand; import org.jboss.cache.commands.tx.CommitCommand; import org.jboss.cache.commands.tx.OptimisticPrepareCommand; import org.jboss.cache.commands.tx.PrepareCommand; import org.jboss.cache.commands.tx.RollbackCommand; import org.jboss.cache.commands.write.EvictCommand; import org.jboss.cache.commands.write.InvalidateCommand; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.annotations.Inject; import org.jboss.cache.factories.annotations.Start; import org.jboss.cache.io.ByteBuffer; import org.jboss.cache.transaction.GlobalTransaction; import org.jgroups.util.Buffer; import java.io.InputStream; import java.io.ObjectInputStream; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * Abstract AbstractMarshaller for JBoss Cache. * * @author Manik Surtani (manik@jboss.org) */ public abstract class AbstractMarshaller implements Marshaller { protected boolean useRegionBasedMarshalling; protected RegionManager regionManager; protected boolean defaultInactive; protected Log log; protected boolean trace; /** * Map for prepared tx that have not committed */ private Map transactions = new ConcurrentHashMap(16); protected Configuration configuration; protected ClassLoader defaultClassLoader; protected boolean useRefs = false; @Inject void injectDependencies(RegionManager regionManager, Configuration configuration, ClassLoader defaultClassLoader) { this.defaultClassLoader = defaultClassLoader; this.regionManager = regionManager; this.configuration = configuration; } @Start @SuppressWarnings("deprecation") protected void init() { this.useRegionBasedMarshalling = configuration.isUseRegionBasedMarshalling(); this.defaultInactive = configuration.isInactiveOnStartup(); } protected void initLogger() { log = LogFactory.getLog(getClass()); trace = log.isTraceEnabled(); } // implement the basic contract set in RPCDispatcher.AbstractMarshaller public Object objectFromStream(InputStream in) throws Exception { throw new RuntimeException("Needs to be overridden!"); } public RegionalizedMethodCall regionalizedMethodCallFromByteBuffer(byte[] buffer) throws Exception { throw new RuntimeException("Needs to be overridden!"); } public RegionalizedMethodCall regionalizedMethodCallFromObjectStream(ObjectInputStream in) throws Exception { throw new RuntimeException("Needs to be overridden!"); } public ByteBuffer objectToBuffer(Object o) throws Exception { throw new RuntimeException("Needs to be overridden!"); } public Object objectFromByteBuffer(byte[] bytes) throws Exception { return objectFromByteBuffer(bytes, 0, bytes.length); } public Object objectFromByteBuffer(byte[] bytes, int offset, int len) throws Exception { throw new RuntimeException("Needs to be overridden!"); } public byte[] objectToByteBuffer(Object obj) throws Exception { Buffer b = objectToBuffer(obj); byte[] bytes = new byte[b.getLength()]; System.arraycopy(b.getBuf(), b.getOffset(), bytes, 0, b.getLength()); return bytes; } protected Fqn extractFqn(ReplicableCommand cmd) { if (cmd == null) throw new NullPointerException("Command is null"); Fqn fqn = null; //Object[] args = cmd.getParameters(); switch (cmd.getCommandId()) { case OptimisticPrepareCommand.METHOD_ID: case PrepareCommand.METHOD_ID: // Prepare method has a list of modifications. We will just take the first one and extract. PrepareCommand pc = (PrepareCommand) cmd; List modifications = pc.getModifications(); fqn = extractFqn(modifications.get(0)); // If this is two phase commit, map the FQN to the GTX so // we can find it when the commit/rollback comes through if (!pc.isOnePhaseCommit()) transactions.put(pc.getGlobalTransaction(), fqn); break; case RollbackCommand.METHOD_ID: case CommitCommand.METHOD_ID: // We stored the fqn in the transactions map during the prepare phase fqn = transactions.remove(((AbstractTransactionCommand) cmd).getGlobalTransaction()); break; case GravitateDataCommand.METHOD_ID: case EvictCommand.METHOD_ID: case EvictCommand.VERSIONED_METHOD_ID: case InvalidateCommand.METHOD_ID: case GetChildrenNamesCommand.METHOD_ID: case GetDataMapCommand.METHOD_ID: case GetKeyValueCommand.METHOD_ID: case GetKeysCommand.METHOD_ID: case ExistsCommand.METHOD_ID: fqn = ((DataCommand) cmd).getFqn(); break; case DataGravitationCleanupCommand.METHOD_ID: fqn = ((DataGravitationCleanupCommand) cmd).getFqn(); break; case AnnounceBuddyPoolNameCommand.METHOD_ID: case AssignToBuddyGroupCommand.METHOD_ID: case RemoveFromBuddyGroupCommand.METHOD_ID: break; // possible when we have a replication queue. case ReplicateCommand.SINGLE_METHOD_ID: fqn = extractFqn(((ReplicateCommand) cmd).getSingleModification()); break; case ReplicateCommand.MULTIPLE_METHOD_ID: fqn = extractFqn(((ReplicateCommand) cmd).getModifications().get(0)); break; case ClusteredGetCommand.METHOD_ID: fqn = ((ClusteredGetCommand) cmd).getDataCommand().getFqn(); break; default: if (cmd instanceof DataCommand) { fqn = ((DataCommand) cmd).getFqn(); } else { throw new IllegalArgumentException("AbstractMarshaller.extractFqn(): Unknown id in method call: " + cmd); } break; } if (trace) log.trace("extract(): received " + cmd + "extracted fqn: " + fqn); return fqn; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/marshall/RegionNameConflictException.java0000644000175000017500000000111210537345073032436 0ustar twernertwernerpackage org.jboss.cache.marshall; /** * MarshRegion name conflicts with pre-existing regions. The conflict may come from there is already an * parent region defined. * * @author Ben Wang 8-2005 */ public class RegionNameConflictException extends Exception { private static final long serialVersionUID = -7719282503234213209L; public RegionNameConflictException() { super(); } public RegionNameConflictException(String msg) { super(msg); } public RegionNameConflictException(String msg, Throwable cause) { super(msg, cause); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/marshall/CommandAwareRpcDispatcher.java0000644000175000017500000001461711063772476032107 0ustar twernertwernerpackage org.jboss.cache.marshall; import org.jboss.cache.InvocationContext; import org.jboss.cache.commands.ReplicableCommand; import org.jboss.cache.commands.VisitableCommand; import org.jboss.cache.commands.remote.AnnounceBuddyPoolNameCommand; import org.jboss.cache.commands.remote.AssignToBuddyGroupCommand; import org.jboss.cache.commands.remote.RemoveFromBuddyGroupCommand; import org.jboss.cache.factories.ComponentRegistry; import org.jboss.cache.interceptors.InterceptorChain; import org.jboss.cache.invocation.InvocationContextContainer; import org.jgroups.Address; import org.jgroups.Channel; import org.jgroups.MembershipListener; import org.jgroups.Message; import org.jgroups.MessageListener; import org.jgroups.blocks.GroupRequest; import org.jgroups.blocks.RpcDispatcher; import org.jgroups.blocks.RspFilter; import org.jgroups.util.Rsp; import org.jgroups.util.RspList; import java.io.NotSerializableException; import java.util.Vector; /** * A JGroups RPC dispatcher that knows how to deal with {@link org.jboss.cache.commands.ReplicableCommand}s. * * @author Manik Surtani (manik@jboss.org) * @since 2.2.0 */ public class CommandAwareRpcDispatcher extends RpcDispatcher { protected InvocationContextContainer invocationContextContainer; protected InterceptorChain interceptorChain; protected ComponentRegistry componentRegistry; protected boolean trace; public CommandAwareRpcDispatcher() { } public CommandAwareRpcDispatcher(Channel channel, MessageListener l, MembershipListener l2, Object serverObj, InvocationContextContainer container, InterceptorChain interceptorChain, ComponentRegistry componentRegistry) { super(channel, l, l2, serverObj); this.invocationContextContainer = container; this.componentRegistry = componentRegistry; this.interceptorChain = interceptorChain; trace = log.isTraceEnabled(); } protected boolean isValid(Message req) { if (server_obj == null) { log.error("no method handler is registered. Discarding request."); return false; } if (req == null || req.getLength() == 0) { log.error("message or message buffer is null"); return false; } return true; } /** * Similar to {@link #callRemoteMethods(java.util.Vector, org.jgroups.blocks.MethodCall, int, long, boolean, boolean, org.jgroups.blocks.RspFilter)} except that this version * is aware of {@link org.jboss.cache.commands.ReplicableCommand} objects. */ public RspList invokeRemoteCommands(Vector

        dests, ReplicableCommand command, int mode, long timeout, boolean use_anycasting, boolean oob, RspFilter filter) throws NotSerializableException { if (dests != null && dests.isEmpty()) { // don't send if dest list is empty if (trace) log.trace("Destination list is empty: no need to send message"); return new RspList(); } if (trace) log.trace(new StringBuilder("dests=").append(dests).append(", command=").append(command). append(", mode=").append(mode).append(", timeout=").append(timeout)); byte[] buf; try { buf = getRequestMarshaller().objectToByteBuffer(command); } catch (Exception e) { throw new RuntimeException("failure to marshal argument(s)", e); } Message msg = new Message(null, null, buf); if (oob) msg.setFlag(Message.OOB); RspList retval = super.castMessage(dests, msg, mode, timeout, use_anycasting, filter); if (log.isTraceEnabled()) log.trace("responses: " + retval); // a null response is 99% likely to be due to a marshalling problem - we throw a NSE, this needs to be changed when // JGroups supports http://jira.jboss.com/jira/browse/JGRP-193 // the serialization problem could be on the remote end and this is why we cannot catch this above, when marshalling. if (retval == null) { throw new NotSerializableException("RpcDispatcher returned a null. This is most often caused by args for " + command.getClass().getSimpleName() + " not being serializable."); } if (mode == GroupRequest.GET_NONE || retval.isEmpty() || containsOnlyNulls(retval)) return null; else return retval; } private boolean containsOnlyNulls(RspList l) { for (Rsp r : l.values()) { if (r.getValue() != null || !r.wasReceived() || r.wasSuspected()) return false; } return true; } /** * Message contains a Command. Execute it against *this* object and return result. */ @Override public Object handle(Message req) { if (isValid(req)) { try { return executeCommand((ReplicableCommand) getRequestMarshaller().objectFromByteBuffer(req.getBuffer()), req); } catch (Throwable x) { if (trace) log.trace("Problems invoking command.", x); return x; } } else { return null; } } protected Object executeCommand(ReplicableCommand cmd, Message req) throws Throwable { if (trace) log.trace("Executing command: " + cmd + " [sender=" + req.getSrc() + "]"); if (cmd instanceof VisitableCommand) { InvocationContext ctx = invocationContextContainer.get(); ctx.setOriginLocal(false); if (!componentRegistry.invocationsAllowed(false)) { return null; } return interceptorChain.invoke(ctx, (VisitableCommand) cmd); } else { if (trace) log.trace("This is a non-visitable command - so performing directly and not via the invoker."); // need to check cache status for all except buddy replication commands. if (!(cmd instanceof AnnounceBuddyPoolNameCommand || cmd instanceof AssignToBuddyGroupCommand || cmd instanceof RemoveFromBuddyGroupCommand) && !componentRegistry.invocationsAllowed(false)) { return null; } return cmd.perform(null); } } @Override public String toString() { return getClass().getSimpleName() + "[Outgoing marshaller: " + getRequestMarshaller() + "; incoming marshaller: " + getResponseMarshaller() + "]"; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/marshall/MarshalledValueHelper.java0000644000175000017500000000277711010550166031270 0ustar twernertwernerpackage org.jboss.cache.marshall; import org.jboss.cache.Fqn; import org.jboss.cache.commands.ReplicableCommand; import org.jboss.cache.transaction.GlobalTransaction; import org.jgroups.Address; /** * Common functionality used by the {@link org.jboss.cache.interceptors.MarshalledValueInterceptor} and the {@link MarshalledValueMap}. * * @author Manik Surtani (manik@jboss.org) * @see MarshalledValue * @see org.jboss.cache.interceptors.MarshalledValueInterceptor * @see org.jboss.cache.marshall.MarshalledValueMap * @since 2.1.0 */ public class MarshalledValueHelper { /** * Tests whether the type should be excluded from MarshalledValue wrapping. * * @param type type to test. Should not be null. * @return true if it should be excluded from MarshalledValue wrapping. */ public static boolean isTypeExcluded(Class type) { return type.equals(String.class) || type.isPrimitive() || type.equals(Void.class) || type.equals(Boolean.class) || type.equals(Character.class) || type.equals(Byte.class) || type.equals(Short.class) || type.equals(Integer.class) || type.equals(Long.class) || type.equals(Float.class) || type.equals(Double.class) || (type.isArray() && isTypeExcluded(type.getComponentType())) || type.equals(Fqn.class) || type.equals(GlobalTransaction.class) || type.equals(Address.class) || ReplicableCommand.class.isAssignableFrom(type) || type.equals(MarshalledValue.class); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/marshall/NodeData.java0000644000175000017500000000537011005362317026531 0ustar twernertwernerpackage org.jboss.cache.marshall; import org.jboss.cache.Fqn; import java.io.Externalizable; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.util.Map; /** * Serializable representation of the data of a node (FQN and attributes) * * @author Bela Ban * @version $Id: NodeData.java 5723 2008-04-28 14:53:03Z manik.surtani@jboss.com $ */ // TODO: 3.0.0: remove Externalizable and rely on the CacheMarshaller. public class NodeData implements Externalizable { private Fqn fqn = null; private Map attrs = null; static final long serialVersionUID = -7571995794010294485L; public NodeData() { } public NodeData(Fqn fqn) { this.fqn = fqn; } public NodeData(Fqn fqn, Map attrs) { this.fqn = fqn; this.attrs = attrs; } public NodeData(String fqn, Map attrs) { this.fqn = Fqn.fromString(fqn); this.attrs = attrs; } public Map getAttributes() { return attrs; } public Fqn getFqn() { return fqn; } public boolean isMarker() { return false; } public boolean isExceptionMarker() { return false; } // TODO: 3.0.0: Remove and replace with marshallNodeData/unmarshallNodeData methods in the CacheMarshaller so that we can use the same marshalling framework for Fqns. public void writeExternal(ObjectOutput out) throws IOException { out.writeObject(fqn); if (attrs != null) { out.writeBoolean(true); out.writeObject(attrs); } else { out.writeBoolean(false); } } // TODO: 3.0.0: Remove in and replace with marshallNodeData/unmarshallNodeData methods in the CacheMarshaller so that we can use the same marshalling framework for Fqns. @SuppressWarnings("unchecked") public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { fqn = (Fqn) in.readObject(); if (in.readBoolean()) { attrs = (Map) in.readObject(); } } @Override public String toString() { return "NodeData {fqn: " + fqn + ", attrs=" + attrs + "}"; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; NodeData nodeData = (NodeData) o; if (attrs != null ? !attrs.equals(nodeData.attrs) : nodeData.attrs != null) return false; if (fqn != null ? !fqn.equals(nodeData.fqn) : nodeData.fqn != null) return false; return true; } @Override public int hashCode() { int result; result = (fqn != null ? fqn.hashCode() : 0); result = 31 * result + (attrs != null ? attrs.hashCode() : 0); return result; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/marshall/NodeDataMarker.java0000644000175000017500000000046611004125045027666 0ustar twernertwernerpackage org.jboss.cache.marshall; public class NodeDataMarker extends NodeData { private static final long serialVersionUID = 4851793846346021014L; @Override public boolean isMarker() { return true; } @Override public String toString() { return "NodeDataMarker"; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/marshall/MarshallingException.java0000644000175000017500000000074110537345073031200 0ustar twernertwernerpackage org.jboss.cache.marshall; /** * Thrown when there is an exception in marshalling. * * @author Ben Wang 8-2005 */ public class MarshallingException extends Exception { private static final long serialVersionUID = -5991339859063633060L; public MarshallingException() { super(); } public MarshallingException(String msg) { super(msg); } public MarshallingException(String msg, Throwable cause) { super(msg, cause); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/marshall/RegionalizedReturnValue.java0000644000175000017500000000146411004116445031661 0ustar twernertwernerpackage org.jboss.cache.marshall; import org.jboss.cache.Fqn; /** * A return value that holds region information, so that the marshaller knows which region to use (and hence which * class loader to use) when marshalling this return value. * * @author Manik Surtani (manik@jboss.org) * @since 2.1.1 */ class RegionalizedReturnValue { final Object returnValue; final Fqn region; /** * Creates this value object. * * @param returnValue return value to marshall * @param regionalizedMethodCall method call that requested this return value. */ RegionalizedReturnValue(Object returnValue, RegionalizedMethodCall regionalizedMethodCall) { this.returnValue = returnValue; this.region = regionalizedMethodCall.region; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/marshall/NodeDataExceptionMarker.java0000644000175000017500000000232311004125045031537 0ustar twernertwernerpackage org.jboss.cache.marshall; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; public class NodeDataExceptionMarker extends NodeData { private static final long serialVersionUID = 240199474174502551L; private Throwable cause; private Object cacheNodeIdentity; public NodeDataExceptionMarker() { super(); } public NodeDataExceptionMarker(Throwable t, Object node) { cause = t; cacheNodeIdentity = node; } public Throwable getCause() { return cause; } public Object getCacheNodeIdentity() { return cacheNodeIdentity; } @Override public boolean isExceptionMarker() { return true; } @Override public void writeExternal(ObjectOutput out) throws IOException { super.writeExternal(out); out.writeObject(cause); out.writeObject(cacheNodeIdentity); } @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { super.readExternal(in); cause = (Throwable) in.readObject(); cacheNodeIdentity = in.readObject(); } @Override public String toString() { return "NodeDataExceptionMarker"; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/marshall/MarshallUtil.java0000644000175000017500000000462510517375203027462 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.marshall; import org.jboss.util.stream.MarshalledValueInputStream; import org.jboss.util.stream.MarshalledValueOutputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.InputStream; /** * Utility methods related to marshalling and unmarshalling objects. * * @author Brian Stansberry * @version $Revision$ */ public class MarshallUtil { /** * Creates an object from a byte buffer using * {@link MarshalledValueInputStream}. * * @param bytes serialized form of an object * @return the object, or null if bytes * is null */ public static Object objectFromByteBuffer(byte[] bytes) throws Exception { if (bytes == null) { return null; } ByteArrayInputStream bais = new ByteArrayInputStream(bytes); MarshalledValueInputStream input = new MarshalledValueInputStream(bais); Object result = input.readObject(); input.close(); return result; } /** * Creates an object from a byte buffer using * {@link MarshalledValueInputStream}. * * @param bytes serialized form of an object * @return the object, or null if bytes * is null */ public static Object objectFromByteBuffer(InputStream bytes) throws Exception { if (bytes == null) { return null; } MarshalledValueInputStream input = new MarshalledValueInputStream(bytes); Object result = input.readObject(); input.close(); return result; } /** * Serializes an object into a byte buffer using * {@link org.jboss.util.stream.MarshalledValueOutputStream}. * * @param obj an object that implements Serializable or Externalizable * @return serialized form of the object */ public static byte[] objectToByteBuffer(Object obj) throws Exception { ByteArrayOutputStream baos = new ByteArrayOutputStream(); MarshalledValueOutputStream out = new MarshalledValueOutputStream(baos); out.writeObject(obj); out.close(); return baos.toByteArray(); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/marshall/InactiveRegionException.java0000644000175000017500000000124310552741661031643 0ustar twernertwernerpackage org.jboss.cache.marshall; import org.jboss.cache.CacheException; /** * Thrown by a {@link org.jboss.cache.marshall.AbstractMarshaller} instance when attempting to perform an operation on an inactive region. * * @author Manik Surtani * @since 2.0.0 */ public class InactiveRegionException extends CacheException { private static final long serialVersionUID = -5672201755094521936L; public InactiveRegionException() { super(); } public InactiveRegionException(String msg) { super(msg); } public InactiveRegionException(String msg, Throwable cause) { super(msg, cause); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/marshall/UnmarshalledReferences.java0000644000175000017500000000315511004116445031470 0ustar twernertwernerpackage org.jboss.cache.marshall; import org.jboss.cache.CacheException; import java.util.ArrayList; /** * An efficient array-based list of referenced objects, using the reference id as a subscript for the array. * * @author Manik Surtani (manik@jboss.org) */ public class UnmarshalledReferences { private final ArrayList referencedObjects = new ArrayList(); /** * Retrieves an object referenced by an id * * @param ref reference * @return object */ public Object getReferencedObject(int ref) { if (ref >= referencedObjects.size()) throw new CacheException("Attempting to look up a ref that hasn't been inserted yet"); return referencedObjects.get(ref); } /** * Adds a referenced object to the list of references * * @param ref reference id * @param o object */ public void putReferencedObject(int ref, Object o) { int sz = referencedObjects.size(); // if we are not adding the object to the end of the list, make sure we use a specific position if (ref < sz) { referencedObjects.set(ref, o); return; } else if (ref > sz) { // if we are adding the reference to a position beyond the end of the list, make sure we expand the list first. // this can happen, weirdly enough, since marshallObject() can be called recursively, such as from marshallFqn(). for (int i = sz; i < ref; i++) referencedObjects.add(null); } referencedObjects.add(o); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/marshall/RegionalizedMethodCall.java0000644000175000017500000000145011020007335031407 0ustar twernertwernerpackage org.jboss.cache.marshall; import org.jboss.cache.Fqn; import org.jboss.cache.commands.ReplicableCommand; /** * A regionalized MethodCall object, created when {@link Marshaller#regionalizedMethodCallFromByteBuffer(byte[])} or * {@link org.jboss.cache.marshall.Marshaller#regionalizedMethodCallFromObjectStream(java.io.ObjectInputStream)} is called. *

        * Specifically used by the {@link org.jboss.cache.marshall.InactiveRegionAwareRpcDispatcher} so that the region used to unmarshall * the method call is known, and can be used to marshall a result to return to the remote caller. *

        * * @author Manik Surtani (manik@jboss.org) * @since 2.1.1 */ public class RegionalizedMethodCall { public ReplicableCommand command; public Fqn region; } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/marshall/VersionAwareMarshaller.java0000644000175000017500000002436311075062420031474 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.marshall; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.Fqn; import org.jboss.cache.factories.ComponentRegistry; import org.jboss.cache.factories.annotations.Inject; import org.jboss.cache.factories.annotations.Start; import org.jboss.cache.io.ByteBuffer; import org.jboss.cache.io.ExposedByteArrayOutputStream; import org.jboss.cache.util.Util; import org.jboss.util.stream.MarshalledValueInputStream; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.HashMap; import java.util.Map; import java.util.StringTokenizer; /** * A delegate to various other marshallers like {@link org.jboss.cache.marshall.CacheMarshaller200}. * This delegating marshaller adds versioning information to the stream when marshalling objects and * is able to pick the appropriate marshaller to delegate to based on the versioning information when * unmarshalling objects. * * @author Manik Surtani (manik@jboss.org) * @author Galder Zamarreno */ public class VersionAwareMarshaller extends AbstractMarshaller { private static final Log log = LogFactory.getLog(VersionAwareMarshaller.class); private static final int VERSION_200 = 20; private static final int VERSION_210 = 21; private static final int VERSION_220 = 22; private static final int CUSTOM_MARSHALLER = 999; private ComponentRegistry componentRegistry; Marshaller defaultMarshaller; final Map marshallers = new HashMap(); private int versionInt; @Inject void injectComponents(ComponentRegistry componentRegistry) { this.componentRegistry = componentRegistry; } @Start public void initReplicationVersions() { String replVersionString = configuration.getReplVersionString(); // this will cause the correct marshaller to be created and put in the map of marshallers defaultMarshaller = configuration.getMarshaller(); if (defaultMarshaller == null) { String marshallerClass = configuration.getMarshallerClass(); if (marshallerClass != null) { if (trace) log.trace("Cache marshaller implementation specified as " + marshallerClass + ". Overriding any version strings passed in. "); try { defaultMarshaller = (Marshaller) Util.loadClass(marshallerClass).newInstance(); } catch (Exception e) { log.warn("Unable to instantiate marshaller of class " + marshallerClass, e); log.warn("Falling back to using the default marshaller for version string " + replVersionString); } } } if (defaultMarshaller == null) { // "Rounds down" the replication version passed in to the MINOR version. // E.g., 1.4.1.SP3 -> 1.4.0 versionInt = toMinorVersionInt(replVersionString); this.defaultMarshaller = getMarshaller(versionInt); } else { if (log.isDebugEnabled()) log.debug("Using the marshaller passed in - " + defaultMarshaller); versionInt = getCustomMarshallerVersionInt(); marshallers.put(versionInt, defaultMarshaller); } if (log.isDebugEnabled()) { log.debug("Started with version " + replVersionString + " and versionInt " + versionInt); log.debug("Using default marshaller class " + this.defaultMarshaller.getClass()); } } protected int getCustomMarshallerVersionInt() { if (defaultMarshaller.getClass().equals(CacheMarshaller210.class)) return VERSION_210; if (defaultMarshaller.getClass().equals(CacheMarshaller200.class)) return VERSION_200; return CUSTOM_MARSHALLER; } /** * Converts versions to known compatible version ids. *

        * Typical return values: *

        * < 1.4.0 = "1" * 1.4.x = "14" * 1.5.x = "15" * 2.0.x = "20" * 2.1.x = "21" *

        * etc. * * @param version * @return a version integer */ private int toMinorVersionInt(String version) { try { StringTokenizer strtok = new StringTokenizer(version, "."); // major, minor, micro, patch String[] versionComponents = {null, null, null, null}; int i = 0; while (strtok.hasMoreTokens()) { versionComponents[i++] = strtok.nextToken(); } int major = Integer.parseInt(versionComponents[0]); int minor = Integer.parseInt(versionComponents[1]); return (major > 1 || minor > 3) ? (10 * major) + minor : 1; } catch (Exception e) { throw new IllegalArgumentException("Unsupported replication version string " + version); } } @Override public ByteBuffer objectToBuffer(Object obj) throws Exception { ExposedByteArrayOutputStream baos = new ExposedByteArrayOutputStream(128); ObjectOutputStream out = new ObjectOutputStream(baos); out.writeShort(versionInt); if (trace) log.trace("Wrote version " + versionInt); //now marshall the contents of the object defaultMarshaller.objectToObjectStream(obj, out); out.close(); // and return bytes. return new ByteBuffer(baos.getRawBuffer(), 0, baos.size()); } @Override public Object objectFromByteBuffer(byte[] bytes, int offset, int len) throws Exception { Marshaller marshaller; int versionId; ObjectInputStream in = new MarshalledValueInputStream(new ByteArrayInputStream(bytes, offset, len)); try { versionId = in.readShort(); if (trace) log.trace("Read version " + versionId); } catch (Exception e) { log.error("Unable to read version id from first two bytes of stream, barfing."); throw e; } marshaller = getMarshaller(versionId); return marshaller.objectFromObjectStream(in); } @Override public RegionalizedMethodCall regionalizedMethodCallFromByteBuffer(byte[] buf) throws Exception { Marshaller marshaller; int versionId; ObjectInputStream in = new MarshalledValueInputStream(new ByteArrayInputStream(buf)); try { versionId = in.readShort(); if (trace) log.trace("Read version " + versionId); } catch (Exception e) { log.error("Unable to read version id from first two bytes of stream, barfing."); throw e; } marshaller = getMarshaller(versionId); return marshaller.regionalizedMethodCallFromObjectStream(in); } @Override public Object objectFromStream(InputStream is) throws Exception { if (is instanceof ByteArrayInputStream) { int avbl = is.available(); byte[] bytes = new byte[avbl]; is.read(bytes, 0, avbl); return objectFromByteBuffer(bytes); } else { // actually attempt to "stream" this stuff. We need to revert to an old-fashioned Object Input Stream since // we don't have a reusable implementation for non-byte-backed streams as yet. short versionId; Marshaller marshaller; ObjectInputStream in = new MarshalledValueInputStream(is); try { versionId = in.readShort(); if (trace) log.trace("Read version " + versionId); } catch (Exception e) { log.error("Unable to read version id from first two bytes of stream, barfing."); throw e; } marshaller = getMarshaller(versionId); return marshaller.objectFromObjectStream(in); } } public void objectToObjectStream(Object obj, ObjectOutputStream out, Fqn region) throws Exception { out.writeShort(versionInt); if (trace) log.trace("Wrote version " + versionInt); defaultMarshaller.objectToObjectStream(obj, out, region); } /** * Lazily instantiates and loads the relevant marshaller for a given version. * * @param versionId * @return appropriate marshaller for the version requested. */ Marshaller getMarshaller(int versionId) { Marshaller marshaller; AbstractMarshaller am; boolean knownVersion = false; switch (versionId) { case VERSION_200: marshaller = marshallers.get(VERSION_200); if (marshaller == null) { am = new CacheMarshaller200(); marshaller = am; componentRegistry.wireDependencies(am); am.init(); marshallers.put(VERSION_200, marshaller); } break; case VERSION_220: case VERSION_210: knownVersion = true; default: if (!knownVersion && log.isWarnEnabled()) log.warn("Unknown replication version String. Falling back to the default marshaller installed."); marshaller = marshallers.get(VERSION_210); if (marshaller == null) { am = new CacheMarshaller210(); marshaller = am; componentRegistry.wireDependencies(am); am.init(); marshallers.put(VERSION_210, marshaller); } break; } return marshaller; } public void objectToObjectStream(Object obj, ObjectOutputStream out) throws Exception { out.writeShort(versionInt); if (trace) log.trace("Wrote version " + versionInt); defaultMarshaller.objectToObjectStream(obj, out); } public Object objectFromObjectStream(ObjectInputStream in) throws Exception { Marshaller marshaller; int versionId; try { versionId = in.readShort(); if (trace) log.trace("Read version " + versionId); } catch (Exception e) { log.error("Unable to read version id from first two bytes of stream, barfing."); throw e; } marshaller = getMarshaller(versionId); return marshaller.objectFromObjectStream(in); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/marshall/Marshaller.java0000644000175000017500000000771511075062420027150 0ustar twernertwernerpackage org.jboss.cache.marshall; import org.jboss.cache.Fqn; import org.jgroups.blocks.RpcDispatcher; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; /** * A marshaller is a class that is able to marshall and unmarshall objects efficiently. *

        * The reason why this is implemented specially in JBoss Cache rather than resorting to * Java serialization or even the more efficient JBoss serialization is that a lot of efficiency * can be gained when a majority of the serialization that occurs has to do with a small set * of known types such as {@link org.jboss.cache.Fqn} or {@link org.jboss.cache.commands.ReplicableCommand}, and class type information * can be replaced with simple magic numbers. *

        * Unknown types (typically user data) falls back to JBoss serialization. *

        * In addition, using a marshaller allows adding additional data to the byte stream, such as context * class loader information on which class loader to use to deserialize the object stream, or versioning * information to allow streams to interoperate between different versions of JBoss Cache (see {@link VersionAwareMarshaller} *

        * This interface implements the JGroups building-block interface {@link org.jgroups.blocks.RpcDispatcher.Marshaller} which * is used to marshall {@link org.jboss.cache.commands.ReplicableCommand}s, their parameters and their response values. *

        * The interface is also used by the {@link org.jboss.cache.loader.CacheLoader} framework to efficiently serialize data to be persisted, as well as * the {@link org.jboss.cache.statetransfer.StateTransferManager} when serializing the cache for transferring state en-masse. * * @author Manik Surtani * @since 2.0.0 */ public interface Marshaller extends RpcDispatcher.Marshaller2 { /** * Marshalls an object to a given {@link ObjectOutputStream} * * @param obj object to marshall * @param out stream to marshall to * @throws Exception */ void objectToObjectStream(Object obj, ObjectOutputStream out) throws Exception; /** * Unmarshalls an object from an {@link ObjectInputStream} * * @param in stream to unmarshall from * @throws Exception */ Object objectFromObjectStream(ObjectInputStream in) throws Exception; /** * Unmarshalls an object from an {@link java.io.InputStream} * * @param is stream to unmarshall from * @return Object from stream passed in. * @throws Exception */ Object objectFromStream(InputStream is) throws Exception; /** * Overloaded form of {@link #objectToObjectStream(Object,java.io.ObjectOutputStream)} which adds a hint to the {@link Fqn} region * * @param obj object to marshall * @param region fqn that this object pertains to * @param out stream to marshall to * @throws Exception */ void objectToObjectStream(Object obj, ObjectOutputStream out, Fqn region) throws Exception; /** * Returns a RegionalizedMethodCall from a byte buffer. Only use if you know that the byte buffer contains a * MethodCall and that you are using region-based marshalling, otherwise use {@link #objectFromByteBuffer(byte[])} * * @param buffer byte buffer * @return a RegionalizedMethodCall * @throws Exception if there are issues * @since 2.1.1 */ RegionalizedMethodCall regionalizedMethodCallFromByteBuffer(byte[] buffer) throws Exception; /** * Returns a RegionalizedMethodCall from an object input stream. Only use if you know that the byte buffer contains a * MethodCall and that you are using region-based marshalling, otherwise use {@link #objectFromObjectStream(java.io.ObjectInputStream)} * * @param in object inout stream * @return a RegionalizedMethodCall * @throws Exception if there are issues * @since 2.1.1 */ RegionalizedMethodCall regionalizedMethodCallFromObjectStream(ObjectInputStream in) throws Exception; } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/marshall/MethodCall.java0000644000175000017500000000654411031017377027074 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.marshall; import org.jboss.cache.commands.tx.OptimisticPrepareCommand; import org.jboss.cache.commands.tx.PrepareCommand; import java.lang.reflect.Method; /** * An extension of the JGroups MethodCall class. The reason for this subclass is a minor * optimisation in the way method IDs are dealt with. The JGroups class of the same name uses * a short as a method id, which is more efficient as far as network streaming is concerned. *

        * However, JBossCache uses this id for a lot of == and switch comparisons. Java, being an * integer oriented virtual machine, goes through a lot of extra steps when performing such simple * comparisons or arithmetic on non-integer numeric types. *

        * See http://www.liemur.com/Articles/FineTuningJavaCode-IntOrientedMachine.html *

        * Thanks to Elias Ross/genman for this info. * * @author Manik Surtani (manik@jboss.org) * @deprecated - in favour of {@link org.jboss.cache.commands.ReplicableCommand} instances. Will be removed in 3.X. */ @Deprecated public class MethodCall extends org.jgroups.blocks.MethodCall { /** * It's unclear why this class would be serialized. */ private static final long serialVersionUID = -5316198032742449998L; private int methodIdInteger = -1; public MethodCall() { // for serialization } /** * This only works for prepare() and optimisticPrepare() method calls. */ public boolean isOnePhaseCommitPrepareMehod() { switch (this.getMethodId()) { case PrepareCommand.METHOD_ID: return (Boolean) this.getArgs()[3]; case OptimisticPrepareCommand.METHOD_ID: return (Boolean) this.getArgs()[4]; default: return false; } } protected MethodCall(Method method, Object... arguments) { super(method, arguments); } protected MethodCall(Method method, int methodIdInteger, Object... arguments) { super(method, arguments); this.methodIdInteger = methodIdInteger; } public void setMethodId(int id) { methodIdInteger = id; } public int getMethodId() { return methodIdInteger; } @Override public short getId() { throw new RuntimeException("Use of incorrect method! Are you sure you intend to do this instead of getMethodId()?!?"); } @Override public String toString() { StringBuilder ret = new StringBuilder(); ret.append("MethodName: "); ret.append(method_name); ret.append("; MethodIdInteger: "); ret.append(methodIdInteger); ret.append("; Args: ("); if (args != null && args.length > 0) { if (log.isTraceEnabled()) { boolean first = true; for (Object arg : args) { if (first) first = false; else ret.append(", "); ret.append(arg); } } else { ret.append(" arg[0] = "); ret.append(args[0]); if (args.length > 1) ret.append(" ..."); } } ret.append(')'); return ret.toString(); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/marshall/MarshalledValue.java0000644000175000017500000001465011012441070030114 0ustar twernertwernerpackage org.jboss.cache.marshall; import org.jboss.cache.CacheException; import org.jboss.util.stream.MarshalledValueInputStream; import java.io.*; import java.util.Arrays; /** * Wrapper that wraps cached data, providing lazy deserialization using the calling thread's context class loader. *

        * The {@link org.jboss.cache.interceptors.MarshalledValueInterceptor} handles transparent * wrapping/unwrapping of cached data. *

        * * @author Manik Surtani (manik@jboss.org) * @see org.jboss.cache.interceptors.MarshalledValueInterceptor * @since 2.1.0 */ public class MarshalledValue implements Externalizable { protected Object instance; protected byte[] raw; private int cachedHashCode = 0; // by default equals() will test on the istance rather than the byte array if conversion is required. private transient boolean equalityPreferenceForInstance = true; public MarshalledValue(Object instance) throws NotSerializableException { if (instance == null) throw new NullPointerException("Null values cannot be wrapped as MarshalledValues!"); if (instance instanceof Serializable) this.instance = instance; else throw new NotSerializableException("Marshalled values can only wrap Objects that are serializable! Instance of " + instance.getClass() + " won't Serialize."); } public MarshalledValue() { // empty ctor for serialization } public void setEqualityPreferenceForInstance(boolean equalityPreferenceForInstance) { this.equalityPreferenceForInstance = equalityPreferenceForInstance; } public synchronized void serialize() { if (raw == null) { try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(instance); oos.close(); baos.close(); // Do NOT set instance to null over here, since it may be used elsewhere (e.g., in a cache listener). // this will be compacted by the MarshalledValueInterceptor when the call returns. // instance = null; raw = baos.toByteArray(); } catch (Exception e) { throw new CacheException("Unable to marshall value " + instance, e); } } } public synchronized void deserialize() { if (instance == null) { try { ByteArrayInputStream bais = new ByteArrayInputStream(raw); // use a MarshalledValueInputStream since it needs to be aware of any context class loaders on the current thread. ObjectInputStream ois = new MarshalledValueInputStream(bais); instance = ois.readObject(); ois.close(); bais.close(); // raw = null; } catch (Exception e) { throw new CacheException("Unable to unmarshall value", e); } } } /** * Compacts the references held by this class to a single reference. If only one representation exists this method * is a no-op unless the 'force' parameter is used, in which case the reference held is forcefully switched to the * 'preferred representation'. *

        * Either way, a call to compact() will ensure that only one representation is held. *

        * * @param preferSerializedRepresentation if true and both representations exist, the serialized representation is favoured. If false, the deserialized representation is preferred. * @param force ensures the preferred representation is maintained and the other released, even if this means serializing or deserializing. */ public void compact(boolean preferSerializedRepresentation, boolean force) { // reset the equalityPreference equalityPreferenceForInstance = true; if (force) { if (preferSerializedRepresentation && raw == null) serialize(); else if (!preferSerializedRepresentation && instance == null) deserialize(); } if (instance != null && raw != null) { // need to lose one representation! if (preferSerializedRepresentation) { instance = null; } else { raw = null; } } } public void writeExternal(ObjectOutput out) throws IOException { if (raw == null) serialize(); out.writeInt(raw.length); out.write(raw); out.writeInt(hashCode()); } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { int size = in.readInt(); raw = new byte[size]; cachedHashCode = 0; in.readFully(raw); cachedHashCode = in.readInt(); } public Object get() throws IOException, ClassNotFoundException { if (instance == null) deserialize(); return instance; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; MarshalledValue that = (MarshalledValue) o; // if both versions are serialized or deserialized, just compare the relevant representations. if (raw != null && that.raw != null) return Arrays.equals(raw, that.raw); if (instance != null && that.instance != null) return instance.equals(that.instance); // if conversion of one representation to the other is necessary, then see which we prefer converting. if (equalityPreferenceForInstance) { if (instance == null) deserialize(); if (that.instance == null) that.deserialize(); return instance.equals(that.instance); } else { if (raw == null) serialize(); if (that.raw == null) that.serialize(); return Arrays.equals(raw, that.raw); } } @Override public int hashCode() { if (cachedHashCode == 0) { // always calculate the hashcode based on the instance since this is where we're getting the equals() if (instance == null) deserialize(); cachedHashCode = instance.hashCode(); if (cachedHashCode == 0) // degenerate case { cachedHashCode = 0xFEED; } } return cachedHashCode; } @Override public String toString() { return "MarshalledValue(cachedHashCode=" + cachedHashCode + "; serialized=" + (raw != null) + ")"; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/marshall/CacheMarshaller200.java0000644000175000017500000006564211066201252030317 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.marshall; import org.jboss.cache.Fqn; import org.jboss.cache.Region; import static org.jboss.cache.Region.Status; import org.jboss.cache.buddyreplication.GravitateResult; import org.jboss.cache.commands.ReplicableCommand; import org.jboss.cache.factories.CommandsFactory; import org.jboss.cache.factories.annotations.Inject; import org.jboss.cache.optimistic.DefaultDataVersion; import org.jboss.cache.transaction.GlobalTransaction; import org.jboss.cache.util.MapCopy; import org.jgroups.Address; import org.jgroups.stack.IpAddress; import java.io.Externalizable; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.IdentityHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; /** * An enhanced marshaller for RPC calls between CacheImpl instances. * * @author Manik Surtani (manik@jboss.org) */ public class CacheMarshaller200 extends AbstractMarshaller { // magic numbers protected static final int MAGICNUMBER_METHODCALL = 1; protected static final int MAGICNUMBER_FQN = 2; protected static final int MAGICNUMBER_GTX = 3; protected static final int MAGICNUMBER_IPADDRESS = 4; protected static final int MAGICNUMBER_ARRAY_LIST = 5; protected static final int MAGICNUMBER_INTEGER = 6; protected static final int MAGICNUMBER_LONG = 7; protected static final int MAGICNUMBER_BOOLEAN = 8; protected static final int MAGICNUMBER_STRING = 9; protected static final int MAGICNUMBER_DEFAULT_DATA_VERSION = 10; protected static final int MAGICNUMBER_LINKED_LIST = 11; protected static final int MAGICNUMBER_HASH_MAP = 12; protected static final int MAGICNUMBER_TREE_MAP = 13; protected static final int MAGICNUMBER_HASH_SET = 14; protected static final int MAGICNUMBER_TREE_SET = 15; protected static final int MAGICNUMBER_NODEDATA_MARKER = 16; protected static final int MAGICNUMBER_NODEDATA_EXCEPTION_MARKER = 17; protected static final int MAGICNUMBER_NODEDATA = 18; protected static final int MAGICNUMBER_GRAVITATERESULT = 19; protected static final int MAGICNUMBER_SHORT = 20; protected static final int MAGICNUMBER_MAPCOPY = 21; protected static final int MAGICNUMBER_MARSHALLEDVALUE = 22; protected static final int MAGICNUMBER_NULL = 99; protected static final int MAGICNUMBER_SERIALIZABLE = 100; protected static final int MAGICNUMBER_REF = 101; protected static final InactiveRegionException IRE = new InactiveRegionException("Cannot unmarshall to an inactive region"); public CacheMarshaller200() { initLogger(); // enabled, since this is always enabled in JBC 2.0.0. useRefs = true; } protected CommandsFactory commandsFactory; @Inject public void injectCommandsFactory(CommandsFactory commandsFactory) { this.commandsFactory = commandsFactory; } // -------- AbstractMarshaller interface public void objectToObjectStream(Object o, ObjectOutputStream out) throws Exception { if (useRegionBasedMarshalling) { Fqn region = null; if (o instanceof RegionalizedReturnValue) { RegionalizedReturnValue rrv = (RegionalizedReturnValue) o; region = rrv.region; o = rrv.returnValue; } else if (o instanceof ReplicableCommand) { ReplicableCommand marshallableCommand = (ReplicableCommand) o; region = extractFqnRegion(marshallableCommand); } if (trace) log.trace("Region based call. Using region " + region); objectToObjectStream(o, out, region); } else { // not region based! objectToObjectStream(o, out, null); } } @Override public RegionalizedMethodCall regionalizedMethodCallFromObjectStream(ObjectInputStream in) throws Exception { // parse the stream as per normal. Object[] retVal = objectFromObjectStreamRegionBased(in); RegionalizedMethodCall rmc = new RegionalizedMethodCall(); rmc.command = (ReplicableCommand) retVal[0]; rmc.region = (Fqn) retVal[1]; return rmc; } public Object objectFromObjectStream(ObjectInputStream in) throws Exception { if (useRegionBasedMarshalling) { return objectFromObjectStreamRegionBased(in)[0]; } else { UnmarshalledReferences refMap = useRefs ? new UnmarshalledReferences() : null; Object retValue = unmarshallObject(in, defaultClassLoader, refMap, false); if (trace) log.trace("Unmarshalled object " + retValue); return retValue; } } public void objectToObjectStream(Object o, ObjectOutputStream out, Fqn region) throws Exception { if (trace) log.trace("Marshalling object " + o); Map refMap = useRefs ? new IdentityHashMap() : null; ClassLoader toUse = defaultClassLoader; Thread current = Thread.currentThread(); ClassLoader old = current.getContextClassLoader(); if (old != null) toUse = old; try { if (useRegionBasedMarshalling) // got to check again in case this meth is called directly { if (trace) log.trace("Writing region " + region + " to stream"); Region r = null; if (region != null) r = regionManager.getRegion(region, false); if (r != null && r.getClassLoader() != null) toUse = r.getClassLoader(); current.setContextClassLoader(toUse); marshallObject(region, out, refMap); } else { current.setContextClassLoader(toUse); } marshallObject(o, out, refMap); } finally { current.setContextClassLoader(old); } } /** * @param in * @return a 2-object array. The first one is the unmarshalled object and the 2nd is an Fqn that relates to the region used. If region-based marshalling is not used, the 2nd value is null. * @throws Exception */ protected Object[] objectFromObjectStreamRegionBased(ObjectInputStream in) throws Exception { UnmarshalledReferences refMap = useRefs ? new UnmarshalledReferences() : null; Object o = unmarshallObject(in, refMap); Fqn regionFqn = null; if (o == null) { // a null region. Could happen. Use std marshalling. log.trace("Unmarshalled region as null. Not using a context class loader to unmarshall."); } else { regionFqn = (Fqn) o; } if (trace) log.trace("Unmarshalled regionFqn " + regionFqn + " from stream"); Region region = null; Object[] retValue = {null, null}; if (regionFqn != null) { region = findRegion(regionFqn); } if (region == null) { if (log.isDebugEnabled()) log.debug("Region does not exist for Fqn " + regionFqn + " - not using a context classloader."); retValue[0] = unmarshallObject(in, defaultClassLoader, refMap, false); } else { retValue[0] = unmarshallObject(in, region.getClassLoader(), refMap, true); retValue[1] = regionFqn; } if (trace) log.trace("Unmarshalled object " + retValue[0] + " with region " + retValue[1]); return retValue; } private Region findRegion(Fqn fqn) throws InactiveRegionException { Region region = regionManager.getValidMarshallingRegion(fqn); if (region != null) { Status status = region.getStatus(); if (status == Status.INACTIVATING || status == Status.INACTIVE) { if (log.isDebugEnabled()) { throw new InactiveRegionException("Cannot unmarshall message for region " + fqn + ". This region is inactive."); } else { throw IRE; } } } else if (defaultInactive) { if (log.isDebugEnabled()) { // No region but default inactive means region is inactive throw new InactiveRegionException("Cannot unmarshall message for region " + fqn + ". By default region " + fqn + " is inactive."); } else { throw IRE; } } return region; } private Fqn extractFqnRegion(ReplicableCommand cmd) throws Exception { Fqn fqn = extractFqn(cmd); Region r = regionManager.getValidMarshallingRegion(fqn); return r == null ? null : r.getFqn(); } // --------- Marshalling methods protected void marshallObject(Object o, ObjectOutputStream out, Map refMap) throws Exception { if (o == null) { out.writeByte(MAGICNUMBER_NULL); } else if (useRefs && refMap.containsKey(o))// see if this object has been marshalled before. { out.writeByte(MAGICNUMBER_REF); writeReference(out, refMap.get(o)); } else if (o instanceof ReplicableCommand) { ReplicableCommand command = (ReplicableCommand) o; if (command.getCommandId() > -1) { out.writeByte(MAGICNUMBER_METHODCALL); marshallCommand(command, out, refMap); } else { throw new IllegalArgumentException("MethodCall does not have a valid method id. Was this method call created with MethodCallFactory?"); } } else if (o instanceof org.jgroups.blocks.MethodCall) { throw new IllegalArgumentException("Usage of a legacy MethodCall object!!"); } else if (o instanceof MarshalledValue) { out.writeByte(MAGICNUMBER_MARSHALLEDVALUE); ((MarshalledValue) o).writeExternal(out); } else if (o instanceof Fqn) { out.writeByte(MAGICNUMBER_FQN); if (useRefs) writeReference(out, createReference(o, refMap)); marshallFqn((Fqn) o, out, refMap); } else if (o instanceof GlobalTransaction) { out.writeByte(MAGICNUMBER_GTX); if (useRefs) writeReference(out, createReference(o, refMap)); marshallGlobalTransaction((GlobalTransaction) o, out, refMap); } else if (o instanceof IpAddress) { out.writeByte(MAGICNUMBER_IPADDRESS); marshallIpAddress((IpAddress) o, out); } else if (o instanceof DefaultDataVersion) { out.writeByte(MAGICNUMBER_DEFAULT_DATA_VERSION); marshallDefaultDataVersion((DefaultDataVersion) o, out); } else if (o.getClass().equals(ArrayList.class)) { out.writeByte(MAGICNUMBER_ARRAY_LIST); marshallCollection((Collection) o, out, refMap); } else if (o.getClass().equals(LinkedList.class)) { out.writeByte(MAGICNUMBER_LINKED_LIST); marshallCollection((Collection) o, out, refMap); } else if (o.getClass().equals(HashMap.class)) { out.writeByte(MAGICNUMBER_HASH_MAP); marshallMap((Map) o, out, refMap); } else if (o.getClass().equals(TreeMap.class)) { out.writeByte(MAGICNUMBER_TREE_MAP); marshallMap((Map) o, out, refMap); } else if (o.getClass().equals(MapCopy.class)) { out.writeByte(MAGICNUMBER_MAPCOPY); marshallMap((Map) o, out, refMap); } else if (o.getClass().equals(HashSet.class)) { out.writeByte(MAGICNUMBER_HASH_SET); marshallCollection((Collection) o, out, refMap); } else if (o.getClass().equals(TreeSet.class)) { out.writeByte(MAGICNUMBER_TREE_SET); marshallCollection((Collection) o, out, refMap); } else if (o instanceof Boolean) { out.writeByte(MAGICNUMBER_BOOLEAN); out.writeBoolean(((Boolean) o).booleanValue()); } else if (o instanceof Integer) { out.writeByte(MAGICNUMBER_INTEGER); out.writeInt(((Integer) o).intValue()); } else if (o instanceof Long) { out.writeByte(MAGICNUMBER_LONG); out.writeLong(((Long) o).longValue()); } else if (o instanceof Short) { out.writeByte(MAGICNUMBER_SHORT); out.writeShort(((Short) o).shortValue()); } else if (o instanceof String) { out.writeByte(MAGICNUMBER_STRING); if (useRefs) writeReference(out, createReference(o, refMap)); marshallString((String) o, out); } else if (o instanceof NodeDataMarker) { out.writeByte(MAGICNUMBER_NODEDATA_MARKER); ((Externalizable) o).writeExternal(out); } else if (o instanceof NodeDataExceptionMarker) { out.writeByte(MAGICNUMBER_NODEDATA_EXCEPTION_MARKER); ((Externalizable) o).writeExternal(out); } else if (o instanceof NodeData) { out.writeByte(MAGICNUMBER_NODEDATA); ((Externalizable) o).writeExternal(out); } else if (o instanceof GravitateResult) { out.writeByte(MAGICNUMBER_GRAVITATERESULT); marshallGravitateResult((GravitateResult) o, out, refMap); } else if (o instanceof Serializable) { if (trace) { log.trace("Warning: using object serialization for " + o.getClass()); } out.writeByte(MAGICNUMBER_SERIALIZABLE); if (useRefs) writeReference(out, createReference(o, refMap)); out.writeObject(o); } else { throw new Exception("Don't know how to marshall object of type " + o.getClass()); } } private void marshallGravitateResult(GravitateResult gravitateResult, ObjectOutputStream out, Map refMap) throws Exception { marshallObject(gravitateResult.isDataFound(), out, refMap); if (gravitateResult.isDataFound()) { marshallObject(gravitateResult.getNodeData(), out, refMap); marshallObject(gravitateResult.getBuddyBackupFqn(), out, refMap); } } private int createReference(Object o, Map refMap) { int reference = refMap.size(); refMap.put(o, reference); return reference; } protected void marshallString(String s, ObjectOutputStream out) throws Exception { //StringUtil.saveString(out, s); out.writeObject(s); } private void marshallCommand(ReplicableCommand command, ObjectOutputStream out, Map refMap) throws Exception { out.writeShort(command.getCommandId()); Object[] args = command.getParameters(); byte numArgs = (byte) (args == null ? 0 : args.length); out.writeByte(numArgs); for (int i = 0; i < numArgs; i++) { marshallObject(args[i], out, refMap); } } private void marshallGlobalTransaction(GlobalTransaction globalTransaction, ObjectOutputStream out, Map refMap) throws Exception { out.writeLong(globalTransaction.getId()); marshallObject(globalTransaction.getAddress(), out, refMap); } protected void marshallFqn(Fqn fqn, ObjectOutputStream out, Map refMap) throws Exception { boolean isRoot = fqn.isRoot(); out.writeBoolean(isRoot); if (!isRoot) { out.writeShort(fqn.size()); for (Object o : fqn.peekElements()) { marshallObject(o, out, refMap); } } } private void marshallIpAddress(IpAddress ipAddress, ObjectOutputStream out) throws Exception { ipAddress.writeExternal(out); } private void marshallCollection(Collection c, ObjectOutputStream out, Map refMap) throws Exception { writeUnsignedInt(out, c.size()); for (Object o : c) { marshallObject(o, out, refMap); } } private void marshallMap(Map map, ObjectOutputStream out, Map refMap) throws Exception { int mapSize = map.size(); writeUnsignedInt(out, mapSize); if (mapSize == 0) return; for (Map.Entry me : (Set) map.entrySet()) { marshallObject(me.getKey(), out, refMap); marshallObject(me.getValue(), out, refMap); } } // --------- Unmarshalling methods protected Object unmarshallObject(ObjectInputStream in, ClassLoader loader, UnmarshalledReferences refMap, boolean overrideContextClassloaderOnThread) throws Exception { if (loader == null) { return unmarshallObject(in, refMap); } else { Thread currentThread = Thread.currentThread(); ClassLoader old = currentThread.getContextClassLoader(); try { // only do this if we haven't already set a context class loader elsewhere. if (overrideContextClassloaderOnThread || old == null) currentThread.setContextClassLoader(loader); return unmarshallObject(in, refMap); } finally { if (overrideContextClassloaderOnThread || old == null) currentThread.setContextClassLoader(old); } } } protected Object unmarshallObject(ObjectInputStream in, UnmarshalledReferences refMap) throws Exception { byte magicNumber = in.readByte(); int reference = 0; Object retVal; switch (magicNumber) { case MAGICNUMBER_NULL: return null; case MAGICNUMBER_REF: if (useRefs) { reference = readReference(in); return refMap.getReferencedObject(reference); } else break; case MAGICNUMBER_SERIALIZABLE: if (useRefs) reference = readReference(in); retVal = in.readObject(); if (useRefs) refMap.putReferencedObject(reference, retVal); return retVal; case MAGICNUMBER_MARSHALLEDVALUE: MarshalledValue mv = new MarshalledValue(); mv.readExternal(in); return mv; case MAGICNUMBER_METHODCALL: retVal = unmarshallCommand(in, refMap); return retVal; case MAGICNUMBER_FQN: if (useRefs) reference = readReference(in); retVal = unmarshallFqn(in, refMap); if (useRefs) refMap.putReferencedObject(reference, retVal); return retVal; case MAGICNUMBER_GTX: if (useRefs) reference = readReference(in); retVal = unmarshallGlobalTransaction(in, refMap); if (useRefs) refMap.putReferencedObject(reference, retVal); return retVal; case MAGICNUMBER_IPADDRESS: retVal = unmarshallIpAddress(in); return retVal; case MAGICNUMBER_DEFAULT_DATA_VERSION: retVal = unmarshallDefaultDataVersion(in); return retVal; case MAGICNUMBER_ARRAY_LIST: return unmarshallArrayList(in, refMap); case MAGICNUMBER_LINKED_LIST: return unmarshallLinkedList(in, refMap); case MAGICNUMBER_HASH_MAP: return unmarshallHashMap(in, refMap); case MAGICNUMBER_TREE_MAP: return unmarshallTreeMap(in, refMap); case MAGICNUMBER_HASH_SET: return unmarshallHashSet(in, refMap); case MAGICNUMBER_TREE_SET: return unmarshallTreeSet(in, refMap); case MAGICNUMBER_MAPCOPY: return unmarshallMapCopy(in, refMap); case MAGICNUMBER_BOOLEAN: return in.readBoolean() ? Boolean.TRUE : Boolean.FALSE; case MAGICNUMBER_INTEGER: return in.readInt(); case MAGICNUMBER_LONG: return in.readLong(); case MAGICNUMBER_SHORT: return in.readShort(); case MAGICNUMBER_STRING: if (useRefs) reference = readReference(in); retVal = unmarshallString(in); if (useRefs) refMap.putReferencedObject(reference, retVal); return retVal; case MAGICNUMBER_NODEDATA_MARKER: retVal = new NodeDataMarker(); ((NodeDataMarker) retVal).readExternal(in); return retVal; case MAGICNUMBER_NODEDATA_EXCEPTION_MARKER: retVal = new NodeDataExceptionMarker(); ((NodeDataExceptionMarker) retVal).readExternal(in); return retVal; case MAGICNUMBER_NODEDATA: retVal = new NodeData(); ((NodeData) retVal).readExternal(in); return retVal; case MAGICNUMBER_GRAVITATERESULT: return unmarshallGravitateResult(in, refMap); default: if (log.isErrorEnabled()) { log.error("Unknown Magic Number " + magicNumber); } throw new Exception("Unknown magic number " + magicNumber); } throw new Exception("Unknown magic number " + magicNumber); } private GravitateResult unmarshallGravitateResult(ObjectInputStream in, UnmarshalledReferences refMap) throws Exception { Boolean found = (Boolean) unmarshallObject(in, refMap); if (found) { List stuff = (List) unmarshallObject(in, refMap); Fqn fqn = (Fqn) unmarshallObject(in, refMap); return GravitateResult.subtreeResult(stuff, fqn); } else { return GravitateResult.noDataFound(); } } protected String unmarshallString(ObjectInputStream in) throws Exception { return (String) in.readObject(); } private ReplicableCommand unmarshallCommand(ObjectInputStream in, UnmarshalledReferences refMap) throws Exception { short methodId = in.readShort(); byte numArgs = in.readByte(); Object[] args = null; if (numArgs > 0) { args = new Object[numArgs]; for (int i = 0; i < numArgs; i++) { args[i] = unmarshallObject(in, refMap); } } return commandsFactory.fromStream(methodId, args); } private GlobalTransaction unmarshallGlobalTransaction(ObjectInputStream in, UnmarshalledReferences refMap) throws Exception { GlobalTransaction gtx = new GlobalTransaction(); long id = in.readLong(); Object address = unmarshallObject(in, refMap); gtx.setId(id); gtx.setAddress((Address) address); return gtx; } protected Fqn unmarshallFqn(ObjectInputStream in, UnmarshalledReferences refMap) throws Exception { boolean isRoot = in.readBoolean(); Fqn fqn; if (!isRoot) { int numElements = in.readShort(); List elements = new ArrayList(numElements); for (int i = 0; i < numElements; i++) { elements.add(unmarshallObject(in, refMap)); } fqn = Fqn.fromList(elements, true); } else { fqn = Fqn.ROOT; } return fqn; } private IpAddress unmarshallIpAddress(ObjectInputStream in) throws Exception { IpAddress ipAddress = new IpAddress(); ipAddress.readExternal(in); return ipAddress; } private List unmarshallArrayList(ObjectInputStream in, UnmarshalledReferences refMap) throws Exception { int listSize = readUnsignedInt(in); List list = new ArrayList(listSize); populateFromStream(in, refMap, list, listSize); return list; } private List unmarshallLinkedList(ObjectInputStream in, UnmarshalledReferences refMap) throws Exception { List list = new LinkedList(); populateFromStream(in, refMap, list, readUnsignedInt(in)); return list; } private Map unmarshallHashMap(ObjectInputStream in, UnmarshalledReferences refMap) throws Exception { Map map = new HashMap(); populateFromStream(in, refMap, map); return map; } private Map unmarshallMapCopy(ObjectInputStream in, UnmarshalledReferences refMap) throws Exception { // read in as a HashMap first Map m = unmarshallHashMap(in, refMap); return new MapCopy(m); } private Map unmarshallTreeMap(ObjectInputStream in, UnmarshalledReferences refMap) throws Exception { Map map = new TreeMap(); populateFromStream(in, refMap, map); return map; } private Set unmarshallHashSet(ObjectInputStream in, UnmarshalledReferences refMap) throws Exception { Set set = new HashSet(); populateFromStream(in, refMap, set); return set; } private Set unmarshallTreeSet(ObjectInputStream in, UnmarshalledReferences refMap) throws Exception { Set set = new TreeSet(); populateFromStream(in, refMap, set); return set; } private void populateFromStream(ObjectInputStream in, UnmarshalledReferences refMap, Map mapToPopulate) throws Exception { int size = readUnsignedInt(in); for (int i = 0; i < size; i++) { mapToPopulate.put(unmarshallObject(in, refMap), unmarshallObject(in, refMap)); } } private void populateFromStream(ObjectInputStream in, UnmarshalledReferences refMap, Set setToPopulate) throws Exception { int size = readUnsignedInt(in); for (int i = 0; i < size; i++) { setToPopulate.add(unmarshallObject(in, refMap)); } } private void populateFromStream(ObjectInputStream in, UnmarshalledReferences refMap, List listToPopulate, int listSize) throws Exception { for (int i = 0; i < listSize; i++) { listToPopulate.add(unmarshallObject(in, refMap)); } } protected void marshallDefaultDataVersion(DefaultDataVersion ddv, ObjectOutputStream out) throws Exception { writeUnsignedLong(out, ddv.getRawVersion()); } protected DefaultDataVersion unmarshallDefaultDataVersion(ObjectInputStream in) throws Exception { return new DefaultDataVersion(readUnsignedLong(in)); } /** * Reads a reference from a given stream. * * @param in the stream to read from * @return an int representing a reference in RefMap. * @throws IOException propagated from the OIS */ protected int readReference(ObjectInputStream in) throws IOException { return in.readShort(); } /** * Writes a reference to a given object output stream. * * @param out the stream to write to * @param reference the reference to write * @throws java.io.IOException propagated from the OOS */ protected void writeReference(ObjectOutputStream out, int reference) throws IOException { out.writeShort(reference); } protected int readUnsignedInt(ObjectInputStream in) throws IOException { return in.readInt(); } protected void writeUnsignedInt(ObjectOutputStream out, int i) throws IOException { out.writeInt(i); } protected long readUnsignedLong(ObjectInputStream in) throws IOException { return in.readLong(); } protected void writeUnsignedLong(ObjectOutputStream out, long i) throws IOException { out.writeLong(i); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/marshall/RegionNotFoundException.java0000644000175000017500000000072710537345073031643 0ustar twernertwernerpackage org.jboss.cache.marshall; /** * MarshRegion name not found. * * @author Ben Wang 8-2005 */ public class RegionNotFoundException extends Exception { private static final long serialVersionUID = 7321547249627414140L; public RegionNotFoundException() { super(); } public RegionNotFoundException(String msg) { super(msg); } public RegionNotFoundException(String msg, Throwable cause) { super(msg, cause); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/DataContainerImpl.java0000644000175000017500000003646011071367645026625 0ustar twernertwernerpackage org.jboss.cache; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.buddyreplication.BuddyFqnTransformer; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.annotations.Inject; import org.jboss.cache.factories.annotations.NonVolatile; import org.jboss.cache.factories.annotations.Start; import org.jboss.cache.factories.annotations.Stop; import org.jboss.cache.invocation.NodeInvocationDelegate; import org.jboss.cache.lock.LockManager; import org.jboss.cache.marshall.NodeData; import org.jboss.cache.optimistic.DataVersion; import org.jboss.cache.transaction.GlobalTransaction; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; /** * A container for the root node in the cache, which also provides helpers for efficiently accessing nodes, walking trees, etc. * * @author Mircea.Markus@jboss.com * @since 2.2 */ @NonVolatile public class DataContainerImpl implements DataContainer { private static final Log log = LogFactory.getLog(DataContainerImpl.class); private static boolean trace = log.isTraceEnabled(); private Configuration configuration; /** * Root node. */ private NodeSPI root; /** * Set of Fqns of the topmost node of internal regions that should * not included in standard state transfers. */ private final Set internalFqns = new HashSet(); private NodeFactory nodeFactory; private LockManager lockManager; private BuddyFqnTransformer buddyFqnTransformer; @Inject public void injectDependencies(Configuration configuration, NodeFactory nodeFactory, LockManager lockManager, BuddyFqnTransformer transformer) { setDependencies(configuration, nodeFactory, lockManager); // We need to create a root node even at this stage since certain components rely on this being available before // start() is called. // TODO: Investigate which components rely on this being available before start(), and why! //TODO - remove setDependencies method at this point createRootNode(); this.buddyFqnTransformer = transformer; } public void setDependencies(Configuration configuration, NodeFactory nodeFactory, LockManager lockManager) { this.configuration = configuration; this.nodeFactory = nodeFactory; this.lockManager = lockManager; } @Start(priority = 12) public void createRootNode() { if (trace) log.trace("Starting data container"); // create a new root temporarily. NodeSPI tempRoot = nodeFactory.createRootDataNode(); // if we don't already have a root or the new (temp) root is of a different class (optimistic vs pessimistic) to // the current root, then we use the new one. Class currentRootType = root == null ? null : ((NodeInvocationDelegate) root).getDelegationTarget().getClass(); Class tempRootType = ((NodeInvocationDelegate) tempRoot).getDelegationTarget().getClass(); if (!tempRootType.equals(currentRootType)) { if (trace) log.trace("Setting root node to an instance of " + tempRootType); setRoot(tempRoot); } root.setChildrenLoaded(true); root.setLockForChildInsertRemove(configuration.isLockParentForChildInsertRemove()); } @Stop(priority = 100) public void stop() { // empty in-memory state root.clearDataDirect(); root.removeChildrenDirect(); } public NodeSPI getRoot() { return root; } /** * Sets the root node reference to the node passed in. * * @param root node */ public void setRoot(NodeSPI root) { if (root == null || !root.getFqn().isRoot()) throw new CacheException("Attempting to set an invalid node [" + root + "] as a root node!"); this.root = root; } public void registerInternalFqn(Fqn fqn) { internalFqns.add(fqn); } /** * Finds a node given a fully qualified name, directly off the interceptor chain. In the event of an exception, * returns null. Does not include invalid or deleted nodes. * * @param fqn Fully qualified name for the corresponding node. * @return Node referenced by the given Fqn, or null if the node cannot be found or if there is an exception. */ public NodeSPI peek(Fqn fqn) { try { return peekVersioned(fqn, null); } catch (CacheException e) { log.warn("Unexpected error", e); return null; } } public NodeSPI peekStrict(GlobalTransaction gtx, Fqn fqn, boolean includeInvalid) { NodeSPI n = peekVersioned(fqn, null, includeInvalid); if (n == null) { StringBuilder builder = new StringBuilder(); builder.append("Node ").append(fqn).append(" not found"); String errStr = builder.toString(); if (trace) log.trace(errStr); throw new NodeNotExistsException(errStr); } return n; } public NodeSPI peekVersioned(Fqn fqn, DataVersion version) { return peekVersioned(fqn, version, false); } public NodeSPI peekVersioned(Fqn fqn, DataVersion version, boolean includeInvalidNodes) { if (fqn == null) return null; NodeSPI toReturn = peek(fqn, false, includeInvalidNodes); if (toReturn != null && version != null && configuration.isNodeLockingOptimistic()) { // we need to check the version of the data node... DataVersion nodeVersion = toReturn.getVersion(); if (trace) { log.trace("looking for optimistic node [" + fqn + "] with version [" + version + "]. My version is [" + nodeVersion + "]"); } if (nodeVersion.newerThan(version)) { // we have a versioning problem; throw an exception! throw new CacheException("Unable to validate versions."); } } return toReturn; } public NodeSPI peek(Fqn fqn, boolean includeDeletedNodes) { return peek(fqn, includeDeletedNodes, false); } public NodeSPI peek(Fqn fqn, boolean includeDeletedNodes, boolean includeInvalidNodes) { if (fqn == null || fqn.size() == 0) return getRoot(); NodeSPI n = getRoot(); int fqnSize = fqn.size(); for (int i = 0; i < fqnSize; i++) { Object obj = fqn.get(i); n = n.getChildDirect(obj); if (n == null) { return null; } else if (!includeDeletedNodes && n.isDeleted()) { return null; } else if (!includeInvalidNodes && !n.isValid()) { return null; } } return n; } public boolean exists(Fqn fqn) { return peek(fqn, false, false) != null; } public boolean hasChildren(Fqn fqn) { if (fqn == null) return false; NodeSPI n = peek(fqn); return n != null && n.hasChildrenDirect(); } public List buildNodeData(List list, NodeSPI node) { NodeData data = new NodeData(buddyFqnTransformer.getActualFqn(node.getFqn()), node.getDataDirect()); list.add(data); for (Object childNode : node.getChildrenDirect()) { buildNodeData(list, (NodeSPI) childNode); } return list; } public List getNodesForEviction(Fqn fqn, boolean recursive) { List result = new ArrayList(); NodeSPI node = peek(fqn, false); if (recursive) { if (node != null) recursiveAddEvictionNodes(node, result); } else { if (node == null) { result.add(fqn); return result; } if (fqn.isRoot()) { for (Object childName : node.getChildrenNamesDirect()) { if (!node.isResident()) result.add(Fqn.fromRelativeElements(fqn, childName)); } } else if (!node.isResident()) { result.add(fqn); } } return result; } private void recursiveAddEvictionNodes(NodeSPI node, List result) { for (NodeSPI child : node.getChildrenDirect()) { recursiveAddEvictionNodes(child, result); } Fqn fqn = node.getFqn(); if (!fqn.isRoot() && !node.isResident()) { result.add(fqn); } } @Override public String toString() { return toString(false); } public Set getInternalFqns() { return Collections.unmodifiableSet(internalFqns); } /** * Returns a debug string with optional details of contents. * * @param details if true, details are printed * @return detailed contents of the container */ @SuppressWarnings("deprecation") public String toString(boolean details) { StringBuilder sb = new StringBuilder(); int indent = 0; if (!details) { sb.append(getClass().getName()).append(" [").append(getNumberOfNodes()).append(" nodes, "); sb.append(getNumberOfLocksHeld()).append(" locks]"); } else { if (root == null) return sb.toString(); for (Object n : root.getChildrenDirect()) { ((NodeSPI) n).print(sb, indent); sb.append("\n"); } } return sb.toString(); } public int getNumberOfLocksHeld() { return numLocks(root); } private int numLocks(NodeSPI n) { int num = 0; if (n != null) { if (lockManager.isLocked(n)) { num++; } for (Object cn : n.getChildrenDirect(true)) { num += numLocks((NodeSPI) cn); } } return num; } public int getNumberOfNodes() { return numNodes(root) - 1; } private int numNodes(NodeSPI n) { int count = 1;// for n if (n != null) { for (Object child : n.getChildrenDirect()) { count += numNodes((NodeSPI) child); } } return count; } /** * Prints information about the contents of the nodes in the cache's current * in-memory state. Does not load any previously evicted nodes from a * cache loader, so evicted nodes will not be included. * * @return details */ public String printDetails() { StringBuilder sb = new StringBuilder(); root.printDetails(sb, 0); sb.append("\n"); return sb.toString(); } /** * Returns lock information. * * @return lock info */ public String printLockInfo() { return lockManager.printLockInfo(root); } public int getNumberOfAttributes(Fqn fqn) { return numAttributes(peek(fqn)); } private int numAttributes(NodeSPI n) { int count = 0; for (Object child : n.getChildrenDirect()) { count += numAttributes((NodeSPI) child); } count += n.getDataDirect().size(); return count; } /** * Returns an approximation of the total number of attributes in * the cache. Since this method doesn't acquire any locks, the number might * be incorrect, or the method might even throw a * ConcurrentModificationException * * @return number of attribs */ public int getNumberOfAttributes() { return numAttributes(root); } public boolean removeFromDataStructure(Fqn f, boolean skipMarkerCheck) { NodeSPI n = peek(f, true); if (n == null) { return false; } if (trace) log.trace("Performing a real remove for node " + f + ", marked for removal."); if (skipMarkerCheck || n.isDeleted()) { if (n.getFqn().isRoot()) { // do not actually delete; just remove deletion marker n.markAsDeleted(true); // mark the node to be removed (and all children) as invalid so anyone holding a direct reference to it will // be aware that it is no longer valid. n.setValid(false, true); n.setValid(true, false); // but now remove all children, since the call has been to remove("/") n.removeChildrenDirect(); return true; } else { // mark the node to be removed (and all children) as invalid so anyone holding a direct reference to it will // be aware that it is no longer valid. n.setValid(false, true); return n.getParent().removeChildDirect(n.getFqn().getLastElement()); } } else { if (log.isDebugEnabled()) log.debug("Node " + f + " NOT marked for removal as expected, not removing!"); return false; } } public void evict(Fqn fqn, boolean recursive) { List toEvict = getNodesForEviction(fqn, recursive); for (Fqn aFqn : toEvict) { evict(aFqn); } } public boolean evict(Fqn fqn) { if (peek(fqn, false, true) == null) return true; if (hasChildren(fqn)) { if (trace) log.trace("removing DATA as node has children: evict(" + fqn + ")"); removeData(fqn); return false; } else { if (trace) log.trace("removing NODE as it is a leaf: evict(" + fqn + ")"); removeNode(fqn); return true; } } private void removeNode(Fqn fqn) { NodeSPI targetNode = peekVersioned(fqn, null, true); if (targetNode == null) return; NodeSPI parentNode = targetNode.getParent(); targetNode.setValid(false, false); if (parentNode != null) { parentNode.removeChildDirect(fqn.getLastElement()); parentNode.setChildrenLoaded(false); } } protected void removeData(Fqn fqn) { NodeSPI n = peekVersioned(fqn, null); if (n == null) { log.warn("node " + fqn + " not found"); return; } n.clearDataDirect(); n.setDataLoaded(false); } public Object[] createNodes(Fqn fqn) { List result = new ArrayList(fqn.size()); Fqn tmpFqn = Fqn.ROOT; int size = fqn.size(); // root node NodeSPI n = root; for (int i = 0; i < size; i++) { Object childName = fqn.get(i); tmpFqn = Fqn.fromRelativeElements(tmpFqn, childName); NodeSPI childNode; Map children = n.getChildrenMapDirect(); childNode = children == null ? null : (NodeSPI) children.get(childName); if (childNode == null) { childNode = n.addChildDirect(Fqn.fromElements(childName)); result.add(childNode); } n = childNode; } return new Object[]{result, n}; } public void setBuddyFqnTransformer(BuddyFqnTransformer buddyFqnTransformer) { this.buddyFqnTransformer = buddyFqnTransformer; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/RPCManager.java0000644000175000017500000001274511006114744025172 0ustar twernertwernerpackage org.jboss.cache; import org.jboss.cache.commands.ReplicableCommand; import org.jgroups.Address; import org.jgroups.Channel; import org.jgroups.blocks.RspFilter; import java.util.List; import java.util.Vector; /** * Provides a mechanism for communicating with other caches in the cluster. For now this is based on JGroups as an underlying * transport, and in future more transport options may become available. *

        * Implementations have a simple lifecycle: *

          *
        • start() - starts the underlying channel based on configuration options injected, and connects the channel
        • *
        • disconnect() - disconnects the channel
        • *
        • stop() - stops the dispatcher and releases resources
        • *
        * * @author Manik Surtani * @since 2.1.0 */ public interface RPCManager { /** * Disconnects and closes the underlying JGroups channel. */ void disconnect(); /** * Stops the RPCDispatcher and frees resources. Closes and disconnects the underlying JGroups channel if this is * still open/connected. */ void stop(); /** * Starts the RPCManager by connecting the underlying JGroups channel (if configured for replication). Connecting * the channel may also involve state transfer (if configured) so the interceptor chain should be started and * available before this method is called. */ void start(); /** * Invokes an RPC call on other caches in the cluster. * * @param recipients a list of Addresses to invoke the call on. If this is null, the call is broadcast to the entire cluster. * @param cacheCommand the cache command to invoke * @param mode the group request mode to use. See {@link org.jgroups.blocks.GroupRequest}. * @param timeout a timeout after which to throw a replication exception. * @param responseFilter a response filter with which to filter out failed/unwanted/invalid responses. * @param useOutOfBandMessage if true, the message is put on JGroups' OOB queue. See JGroups docs for more info. * @return a list of responses from each member contacted. * @throws Exception in the event of problems. */ List callRemoteMethods(Vector
        recipients, ReplicableCommand cacheCommand, int mode, long timeout, RspFilter responseFilter, boolean useOutOfBandMessage) throws Exception; /** * Invokes an RPC call on other caches in the cluster. * * @param recipients a list of Addresses to invoke the call on. If this is null, the call is broadcast to the entire cluster. * @param cacheCommand the cache command to invoke * @param mode the group request mode to use. See {@link org.jgroups.blocks.GroupRequest}. * @param timeout a timeout after which to throw a replication exception. * @param useOutOfBandMessage if true, the message is put on JGroups' OOB queue. See JGroups docs for more info. * @return a list of responses from each member contacted. * @throws Exception in the event of problems. */ List callRemoteMethods(Vector
        recipients, ReplicableCommand cacheCommand, int mode, long timeout, boolean useOutOfBandMessage) throws Exception; /** * Invokes an RPC call on other caches in the cluster. * * @param recipients a list of Addresses to invoke the call on. If this is null, the call is broadcast to the entire cluster. * @param cacheCommand the cache command to invoke * @param synchronous if true, sets group request mode to {@link org.jgroups.blocks.GroupRequest#GET_ALL}, and if false sets it to {@link org.jgroups.blocks.GroupRequest#GET_NONE}. * @param timeout a timeout after which to throw a replication exception. * @param useOutOfBandMessage if true, the message is put on JGroups' OOB queue. See JGroups docs for more info. * @return a list of responses from each member contacted. * @throws Exception in the event of problems. */ List callRemoteMethods(Vector
        recipients, ReplicableCommand cacheCommand, boolean synchronous, long timeout, boolean useOutOfBandMessage) throws Exception; /** * @return true if the current Channel is the coordinator of the cluster. */ boolean isCoordinator(); /** * @return the Address of the current coordinator. */ Address getCoordinator(); /** * Retrieves the local JGroups channel's address * * @return an Address */ Address getLocalAddress(); /** * Returns a defensively copied list of members in the current cluster view. */ List
        getMembers(); /** * Retrieves partial state from remote instances. * * @param sources sources to consider for a state transfer * @param sourceTarget Fqn on source to retrieve state for * @param integrationTarget integration point on local cache to apply state * @throws Exception in the event of problems */ void fetchPartialState(List
        sources, Fqn sourceTarget, Fqn integrationTarget) throws Exception; /** * Retrieves partial state from remote instances. * * @param sources sources to consider for a state transfer * @param subtree Fqn subtree to retrieve. Will be integrated at the same point. * @throws Exception in the event of problems */ void fetchPartialState(List
        sources, Fqn subtree) throws Exception; /** * Retrieves the Channel * * @return a channel */ Channel getChannel(); } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/CacheSPI.java0000644000175000017500000003356611026046072024636 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache; import net.jcip.annotations.ThreadSafe; import org.jboss.cache.buddyreplication.BuddyManager; import org.jboss.cache.buddyreplication.GravitateResult; import org.jboss.cache.interceptors.base.CommandInterceptor; import org.jboss.cache.loader.CacheLoader; import org.jboss.cache.loader.CacheLoaderManager; import org.jboss.cache.marshall.Marshaller; import org.jboss.cache.notifications.Notifier; import org.jboss.cache.statetransfer.StateTransferManager; import org.jboss.cache.transaction.GlobalTransaction; import org.jboss.cache.transaction.TransactionTable; import javax.transaction.Transaction; import javax.transaction.TransactionManager; import java.util.List; import java.util.Set; /** * A more detailed interface to {@link Cache}, which is used when writing plugins for or extending JBoss Cache. A reference * to this interface should only be obtained when it is passed in to your code, for example when you write an * {@link org.jboss.cache.interceptors.base.CommandInterceptor} or {@link CacheLoader}. *

        * You should NEVER attempt to directly cast a {@link Cache} instance to this interface. In future, the implementation may not allow it. *

        * This interface contains overridden method signatures of some methods from {@link Cache}, overridden to ensure return * types of {@link Node} are replaced with {@link NodeSPI}. *

        * * @author Manik Surtani (manik@jboss.org) * @see NodeSPI * @see Cache * @see org.jboss.cache.loader.CacheLoader * @see org.jboss.cache.interceptors.ChainedInterceptor * @since 2.0.0 */ @ThreadSafe public interface CacheSPI extends Cache { /** * Overrides {@link org.jboss.cache.Cache#getRoot()} to return a {@link org.jboss.cache.NodeSPI} instead of a {@link org.jboss.cache.Node}. */ NodeSPI getRoot(); /** * Overrides {@link Cache#getNode(String)} to return a {@link org.jboss.cache.NodeSPI} instead of a {@link org.jboss.cache.Node}. * * @param s string representation of an Fqn * @return a NodeSPI */ NodeSPI getNode(String s); /** * Overrides {@link Cache#getNode(Fqn)} to return a {@link org.jboss.cache.NodeSPI} instead of a {@link org.jboss.cache.Node}. * * @param f an Fqn * @return a NodeSPI */ NodeSPI getNode(Fqn f); /** * Retrieves a reference to a running {@link javax.transaction.TransactionManager}, if one is configured. *

        * From 2.1.0, Interceptor authors should obtain this by injection rather than this method. See the * {@link org.jboss.cache.factories.annotations.Inject} annotation. * * @return a TransactionManager */ TransactionManager getTransactionManager(); /** * Retrieves the current Interceptor chain. *

        * From 2.1.0, Interceptor authors should obtain this by injection rather than this method. See the * {@link org.jboss.cache.factories.annotations.Inject} annotation. * * @return an immutable {@link List} of {@link org.jboss.cache.interceptors.base.CommandInterceptor}s configured for this cache, or * null if {@link Cache#create() create()} has not been invoked * and the interceptors thus do not exist. */ List getInterceptorChain(); /** * Retrieves an instance of a {@link Marshaller}, which is capable of * converting Java objects to bytestreams and back in an efficient manner, which is * also interoperable with bytestreams produced/consumed by other versions of JBoss * Cache. *

        * The use of this marshaller is the recommended way of creating efficient, * compatible, byte streams from objects. *

        * From 2.1.0, Interceptor authors should obtain this by injection rather than this method. See the * {@link org.jboss.cache.factories.annotations.Inject} annotation. * * @return an instance of {@link Marshaller} */ Marshaller getMarshaller(); /** * Adds a custom interceptor to the interceptor chain, at specified position, where the first interceptor in the chain * is at position 0 and the last one at getInterceptorChain().size() - 1. * * @param i the interceptor to add * @param position the position to add the interceptor */ void addInterceptor(CommandInterceptor i, int position); /** * Adds a custom interceptor to the interceptor chain, after an instance of the specified interceptor type. Throws a * cache exception if it cannot find an interceptor of the specified type. * * @param i interceptor to add * @param afterInterceptor interceptor type after which to place custom interceptor */ void addInterceptor(CommandInterceptor i, Class afterInterceptor); /** * Removes the interceptor at a specified position, where the first interceptor in the chain * is at position 0 and the last one at getInterceptorChain().size() - 1. * * @param position the position at which to remove an interceptor */ void removeInterceptor(int position); /** * Removes the interceptor of specified type. * * @param interceptorType type of interceptor to remove */ void removeInterceptor(Class interceptorType); /** * Retrieves the current CacheCacheLoaderManager instance associated with the current Cache instance. *

        * From 2.1.0, Interceptor authors should obtain this by injection rather than this method. See the * {@link org.jboss.cache.factories.annotations.Inject} annotation. * * @return Retrieves a reference to the currently configured {@link org.jboss.cache.loader.CacheLoaderManager} if one or more cache loaders are configured, null otherwise. */ CacheLoaderManager getCacheLoaderManager(); /** * Retrieves the current BuddyManager instance associated with the current Cache instance. *

        * From 2.1.0, Interceptor authors should obtain this by injection rather than this method. See the * {@link org.jboss.cache.factories.annotations.Inject} annotation. * * @return an instance of {@link BuddyManager} if buddy replication is enabled, null otherwise. */ BuddyManager getBuddyManager(); /** * Retrieves the current TransactionTable instance associated with the current Cache instance. *

        * From 2.1.0, Interceptor authors should obtain this by injection rather than this method. See the * {@link org.jboss.cache.factories.annotations.Inject} annotation. * * @return the current {@link TransactionTable} */ TransactionTable getTransactionTable(); /** * Gets a handle of the RPC manager. *

        * From 2.1.0, Interceptor authors should obtain this by injection rather than this method. See the * {@link org.jboss.cache.factories.annotations.Inject} annotation. * * @return the {@link org.jboss.cache.RPCManager} configured. */ RPCManager getRPCManager(); /** * Retrieves the current StateTransferManager instance associated with the current Cache instance. *

        * From 2.1.0, Interceptor authors should obtain this by injection rather than this method. See the * {@link org.jboss.cache.factories.annotations.Inject} annotation. * * @return the current {@link org.jboss.cache.statetransfer.StateTransferManager} */ StateTransferManager getStateTransferManager(); /** * Retrieves the current RegionManager instance associated with the current Cache instance. *

        * From 2.1.0, Interceptor authors should obtain this by injection rather than this method. See the * {@link org.jboss.cache.factories.annotations.Inject} annotation. * * @return the {@link org.jboss.cache.RegionManager} */ RegionManager getRegionManager(); /** * Retrieves the current Notifier instance associated with the current Cache instance. *

        * From 2.1.0, Interceptor authors should obtain this by injection rather than this method. See the * {@link org.jboss.cache.factories.annotations.Inject} annotation. * * @return the notifier attached with this instance of the cache. See {@link org.jboss.cache.notifications.Notifier}, a class * that is responsible for emitting notifications to registered CacheListeners. */ Notifier getNotifier(); /** * @return the name of the cluster. Null if running in local mode. */ String getClusterName(); /** * @return the number of attributes in the cache. */ int getNumberOfAttributes(); /** * @return the number of nodes in the cache. */ int getNumberOfNodes(); /** * Returns the global transaction for this local transaction. * Optionally creates a new global transaction if it does not exist. * * @param tx the current transaction * @param createIfNotExists if true creates a new transaction if none exists * @return a GlobalTransaction */ GlobalTransaction getCurrentTransaction(Transaction tx, boolean createIfNotExists); /** * Returns the transaction associated with the current thread. * If a local transaction exists, but doesn't yet have a mapping to a * GlobalTransaction, a new GlobalTransaction will be created and mapped to * the local transaction. Note that if a local transaction exists, but is * not ACTIVE or PREPARING, null is returned. * * @return A GlobalTransaction, or null if no (local) transaction was associated with the current thread */ GlobalTransaction getCurrentTransaction(); /** * Returns a node without accessing the interceptor chain. Does not return any nodes marked as invalid. Note that this call works * directly on the cache data structure and will not pass through the interceptor chain. Hence node locking, cache * loading or activation does not take place, and so the results of this call should not be treated as definitive. Concurrent node * removal, passivation, etc. may affect the results of this call. * * @param fqn the Fqn to look up. * @param includeDeletedNodes if you intend to see nodes marked as deleted within the current tx, set this to true * @return a node if one exists or null */ NodeSPI peek(Fqn fqn, boolean includeDeletedNodes); /** * Returns a node without accessing the interceptor chain, optionally returning nodes that are marked as invalid ({@link org.jboss.cache.Node#isValid()} == false). * Note that this call works * directly on the cache data structure and will not pass through the interceptor chain. Hence node locking, cache * loading or activation does not take place, and so the results of this call should not be treated as definitive. Concurrent node * removal, passivation, etc. may affect the results of this call. * * @param fqn the Fqn to look up. * @param includeDeletedNodes if you intend to see nodes marked as deleted within the current tx, set this to true * @param includeInvalidNodes if true, nodes marked as being invalid are also returned. * @return a node if one exists or null */ NodeSPI peek(Fqn fqn, boolean includeDeletedNodes, boolean includeInvalidNodes); /** * Used with buddy replication's data gravitation interceptor. If marshalling is necessary, ensure that the cache is * configured to use {@link org.jboss.cache.config.Configuration#useRegionBasedMarshalling} and the {@link org.jboss.cache.Region} * pertaining to the Fqn passed in is activated, and has an appropriate ClassLoader. * * @param fqn the fqn to gravitate * @param searchBuddyBackupSubtrees if true, buddy backup subtrees are searched and if false, they are not. * @param ctx * @return a GravitateResult which contains the data for the gravitation */ GravitateResult gravitateData(Fqn fqn, boolean searchBuddyBackupSubtrees, InvocationContext ctx); /** * Returns a Set of Fqns of the topmost node of internal regions that * should not included in standard state transfers. Will include * {@link BuddyManager#BUDDY_BACKUP_SUBTREE} if buddy replication is * enabled. * * @return an unmodifiable Set. Will not return null. */ Set getInternalFqns(); int getNumberOfLocksHeld(); /** * Helper method that does a peek and ensures that the result of the peek is not null. Note that this call works * directly on the cache data structure and will not pass through the interceptor chain. Hence node locking, cache * loading or activation does not take place, and so the results of this call should not be treated as definitive. * * @param fqn Fqn to peek * @return true if the peek returns a non-null value. */ boolean exists(Fqn fqn); /** * A convenience method that takes a String representation of an Fqn. Otherwise identical to {@link #exists(Fqn)}. * Note that this call works * directly on the cache data structure and will not pass through the interceptor chain. Hence node locking, cache * loading or activation does not take place, and so the results of this call should not be treated as definitive. */ boolean exists(String fqn); /** * Returns all children of a given node. Returns an empty set if there are no children. * The set is unmodifiable. * * @param fqn The fully qualified name of the node * @return Set an unmodifiable set of children names, Object. */ Set getChildrenNames(Fqn fqn); /** * Convenience method that takes a String representation of an Fqn. Otherwise identical to {@link #getChildrenNames(Fqn)} * * @param fqn as a string * @return Set an unmodifiable set of children names, Object. */ Set getChildrenNames(String fqn); } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/NodeSPI.java0000644000175000017500000004356111031017265024512 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache; import net.jcip.annotations.NotThreadSafe; import org.jboss.cache.lock.NodeLock; import org.jboss.cache.optimistic.DataVersion; import org.jboss.cache.transaction.GlobalTransaction; import java.util.Map; import java.util.Set; /** * A more detailed interface to {@link Node}, which is used when writing plugins for or extending JBoss Cache. References are usually * obtained by calling methods on {@link org.jboss.cache.CacheSPI}. *

        * You should NEVER attempt to directly cast a {@link Node} instance to this interface. In future, the implementation may not allow it. *

        * This interface contains overridden method signatures of some methods from {@link Node}, overridden to ensure return * types of {@link Node} are replaced with {@link NodeSPI}. *

        * An important note on the xxxDirect() methods below. These methods are counterparts to similarly named * methods in {@link Node} - e.g., {@link NodeSPI#getDirect(Object)} is a direct access counterpart to {@link Node#get(Object)}, * the difference being that: *

        *

          *
        • {@link Node#get(Object)} - Passes the call up the interceptor stack, applies all aspects including node locking, cache loading, passivation, etc etc.
        • *
        • {@link NodeSPI#getDirect(Object)} - directly works on the underlying data in the node.
        • *
        *

        * The big difference with the direct access methods are that it is the onus of the caller to ensure proper locks are obtained * prior to the call. A proper call should have gone through a locking-capable interceptor first and based on the cache * configuration's locking policy, an appropriate lock should be obtained prior to the call. These direct access methods will * throw {@link org.jboss.cache.lock.LockingException}s if appropriate locks haven't been obtained by the caller. *

        * It is important to node that the direct read methods, such as getDataDirect(), return unmodifiable collections. * In addition to being unmodifiable, they are also defensively copied from the underlying data map to ensure view consistency. *

        * * @author Manik Surtani (manik@jboss.org) * @see Node * @see org.jboss.cache.CacheSPI * @since 2.0.0 */ @NotThreadSafe public interface NodeSPI extends Node { /** * Returns true if the children of this node were loaded from a cache loader. * * @return true if the children of this node were loaded from a cache loader. */ boolean isChildrenLoaded(); /** * Sets if the children of this node were loaded from a cache loader. * * @param loaded true if loaded, false otherwise */ void setChildrenLoaded(boolean loaded); /** * Returns true if the data was loaded from the cache loader. * * @return true if the data was loaded from the cache loader. */ boolean isDataLoaded(); /** * Sets if the data was loaded from the cache loader. * * @param dataLoaded true if loaded, false otherwise */ void setDataLoaded(boolean dataLoaded); /** * Returns a map to access the raw children. * This method may return a null if the node does not have any children. It is important to note that this method * returns a direct reference to the underlying child map and is intended for internal use only. Incorrect use * may result in very inconsistent state of the cache. * * @return Map, keyed by child name, values Nodes. */ Map> getChildrenMapDirect(); /** * Sets the node's children explictly. * This method will remove all children currently associated with this node and add all the children passed in. * * @param children cannot be null */ void setChildrenMapDirect(Map> children); /** * Returns an existing child or creates a new one using a global transaction. * * @param name name of child to create * @param tx transaction under which to create child * @return newly created node */ NodeSPI getOrCreateChild(Object name, GlobalTransaction tx); /** * Returns a lock for this node. * * @return node lock * @deprecated this will be removed in 3.0.0. Please use methods on the {@link org.jboss.cache.lock.LockManager} to lock and unlock nodes. */ @Deprecated NodeLock getLock(); /** * Sets the FQN of this node and resets the names of all children as well. * * @param f fqn to set */ void setFqn(Fqn f); /** * Returns true if the instance has been deleted in the current transaction. * * @return true if the instance has been deleted in the current transaction. */ boolean isDeleted(); /** * Marks the node as being deleted (or not) in the current transaction. This is not recursive, child nodes are not affected. * * @param marker true if the node has been deleted, false if not. */ void markAsDeleted(boolean marker); /** * Same as {@link #markAsDeleted(boolean)} except that the option to recurse into children is provided. * * @param marker true if the node has been deleted, false if not. * @param recursive if true, child nodes (and their children) are marked as well. */ void markAsDeleted(boolean marker, boolean recursive); /** * Adds or replaces a child by name. * * @param nodeName child node name (not an FQN) * @param nodeToAdd child node */ void addChild(Object nodeName, Node nodeToAdd); /** * Prints details of this node to the StringBuilder passed in. * * @param sb StringBuilder to print to * @param indent depth of this node in the tree. Used to indent details by prepending spaces. */ void printDetails(StringBuilder sb, int indent); /** * Prints basic information of this node to the StringBuilder passed in. * * @param sb StringBuilder to print to * @param indent depth of this node in the tree. Used to indent details by prepending spaces. */ @Deprecated void print(StringBuilder sb, int indent); // versioning /** * Sets the data version of this node if versioning is supported. * * @param version data version to apply * @throws UnsupportedOperationException if versioning is not supported */ void setVersion(DataVersion version); /** * Returns the data version of this node if versioning is supported. * * @return data version * @throws UnsupportedOperationException if versioning is not supported */ DataVersion getVersion(); // ------- these XXXDirect() methods work directly on the node and bypass the interceptor chain. /** * Functionally the same as {@link #getChildren()} except that it operates directly on the node and bypasses the * interceptor chain. *

        * The caller needs to ensure a proper lock has been obtained prior to calling this method, otherwise a * {@link org.jboss.cache.lock.LockingException} will be thrown. *

        * * @return set of child nodes. * @see #getChildren() */ Set> getChildrenDirect(); /** * Directly removes all children for this node. * The only direct method that does not have a non-direct counterpart. */ void removeChildrenDirect(); /** * Retrieves children (directly), optionally including any marked as deleted nodes. *

        * The caller needs to ensure a proper lock has been obtained prior to calling this method. * * @param includeMarkedAsDeleted if true, the returned set will include nodes marked as deleted * @return a set of nodes * @throws org.jboss.cache.lock.LockingException * if locking was not obtained */ Set> getChildrenDirect(boolean includeMarkedAsDeleted); /** * Retrives a child directly by name. * Functionally the same as {@link #getChild(Object)} except that it bypasses the * interceptor chain. *

        * The caller needs to ensure a proper lock has been obtained prior to calling this method. * * @param childName name of child * @return child node * @throws org.jboss.cache.lock.LockingException * if locking was not obtained * @see #getChild(Object) */ NodeSPI getChildDirect(Object childName); /** * Adds a child directly to a Node. * Functionally the same as {@link #addChild(Fqn)} except that it bypasses the * interceptor chain. *

        * The caller needs to ensure a proper lock has been obtained prior to calling this method. * * @param childName name of child * @return child node * @throws org.jboss.cache.lock.LockingException * if locking was not obtained * @see #addChild(Fqn) */ NodeSPI addChildDirect(Fqn childName); /** * Same as {@link #addChildDirect(Fqn)} except that it optionally allows you to suppress notification events for * the creation of this node. * * @param f name of child * @param notify if true, notification events are sent; if false, they are not * @return child node * @throws org.jboss.cache.lock.LockingException * if locking was not obtained * @see #addChild(Fqn) */ NodeSPI addChildDirect(Fqn f, boolean notify); /** * Same as {@link #addChildDirect(Fqn, boolean)} except that it just takes a child name * * @param childName name of child * @param notify if true, notification events are sent; if false, they are not * @return child node * @throws org.jboss.cache.lock.LockingException * if locking was not obtained * @see #addChild(Fqn) */ NodeSPI addChildDirect(Object childName, boolean notify); /** * Directly adds the node passed in to the children map of the current node. Will throw a CacheException if * child.getFqn().getParent().equals(getFqn()) returns false. * * @param child child to add */ void addChildDirect(NodeSPI child); /** * Retrives a child directly by fully qualified name. * Functionally the same as {@link #getChild(Fqn)} except that it bypasses the * interceptor chain. *

        * The caller needs to ensure a proper lock has been obtained prior to calling this method. * * @param childName name of child * @return child node * @throws org.jboss.cache.lock.LockingException * if locking was not obtained * @see #getChild(Fqn) */ NodeSPI getChildDirect(Fqn childName); /** * Removes a child directly from a node. * Functionally the same as {@link #removeChild(Fqn)} except that it bypasses the * interceptor chain. *

        * The caller needs to ensure a proper lock has been obtained prior to calling this method, otherwise a * * @param fqn of child. * @return true if the node was found, false otherwise * @throws org.jboss.cache.lock.LockingException * if locking was not obtained * @see #removeChild(Fqn) */ boolean removeChildDirect(Fqn fqn); /** * Removes a child directly from a node. * Functionally the same as {@link #removeChild(Object)} except that bypasses the * interceptor chain. *

        * The caller needs to ensure a proper lock has been obtained prior to calling this method. * * @param childName of child. * @return true if the node was found, false otherwise * @throws org.jboss.cache.lock.LockingException * if locking was not obtained * @see #removeChild(Object) */ boolean removeChildDirect(Object childName); /** * Removes a data key directly from a node. * Functionally the same as {@link #remove(Object)} except that it bypasses the * interceptor chain. *

        * The caller needs to ensure a proper lock has been obtained prior to calling this method. * * @param key to remove * @return the old data contained under the key * @throws org.jboss.cache.lock.LockingException * if locking was not obtained * @see #remove(Object) */ V removeDirect(K key); /** * Functionally the same as {@link #put(Object,Object)} except that it operates directly on the node and bypasses the * interceptor chain. *

        * The caller needs to ensure a proper lock has been obtained prior to calling this method, otherwise a * {@link org.jboss.cache.lock.LockingException} will be thrown. *

        * * @param key of data * @param value of data * @return the previous value under the key passed in, or null * @see #put(Object,Object) */ V putDirect(K key, V value); /** * Functionally the same as {@link #putAll(Map)} except that it operates directly on the node and bypasses the * interceptor chain. *

        * The caller needs to ensure a proper lock has been obtained prior to calling this method, otherwise a * {@link org.jboss.cache.lock.LockingException} will be thrown. *

        * * @param data to put * @see #putAll(Map) */ void putAllDirect(Map data); /** * Functionally the same as {@link #getData()} except that it operates directly on the node and bypasses the * interceptor chain. *

        * Note that this returns a reference to access the node's data. * This data should only be modified by the cache itself. * This method should never return null. *

        * The caller needs to ensure a proper lock has been obtained prior to calling this method, otherwise a * {@link org.jboss.cache.lock.LockingException} will be thrown. *

        * * @return map containing data * @see #getData() */ Map getDataDirect(); /** * Functionally the same as {@link #get(Object)} except that it operates directly on the node and bypasses the * interceptor chain. *

        * The caller needs to ensure a proper lock has been obtained prior to calling this method, otherwise a * {@link org.jboss.cache.lock.LockingException} will be thrown. *

        * * @param key data to get * @return value under key * @see #get(Object) */ V getDirect(K key); /** * Functionally the same as {@link #clearData()} except that it operates directly on the node and bypasses the * interceptor chain. *

        * The caller needs to ensure a proper lock has been obtained prior to calling this method, otherwise a * {@link org.jboss.cache.lock.LockingException} will be thrown. *

        * * @see #clearData() */ void clearDataDirect(); /** * Functionally the same as {@link #getKeys()} except that it operates directly on the node and bypasses the * interceptor chain. *

        * The caller needs to ensure a proper lock has been obtained prior to calling this method, otherwise a * {@link org.jboss.cache.lock.LockingException} will be thrown. *

        * * @return set of keys * @see #getKeys() */ Set getKeysDirect(); /** * Functionally the same as {@link #getChildrenNames()} except that it operates directly on the node and bypasses the * interceptor chain. *

        * The caller needs to ensure a proper lock has been obtained prior to calling this method, otherwise a * {@link org.jboss.cache.lock.LockingException} will be thrown. *

        * * @return set of children names * @see #getChildrenNames() */ Set getChildrenNamesDirect(); /** * Retrieves a reference to the cache in which this Node resides. * * @return a cache */ CacheSPI getCache(); // ----------- these methods override their corresponding methods in Node, so that the return types are NodeSPI rather than Node. /** * Returns the parent node as a {@link NodeSPI}, instead * of {@link Node} from {@link Node#getParent()}, and is otherwise identical. * * @return parent node * @see Node#getParent() */ NodeSPI getParent(); /** * @return true if the node has one or more child nodes; false otherwise. */ boolean hasChildrenDirect(); /** * Very similar to {@link #getDataDirect()}, except that this method may also encode some internal data as attributes in the map, * using special _JBOSS_INTERNAL_XXX Strings as keys. Designed to be used by {@link org.jboss.cache.statetransfer.StateTransferGenerator} * and {@link org.jboss.cache.interceptors.CacheStoreInterceptor} which attempt to serialize nodes into a stream for storage or transfer. * * @param onlyInternalState if true, the map will only contain internal state and no other data. * @return a map containing data as well as additional information about this node. * @since 2.1.0 */ Map getInternalState(boolean onlyInternalState); /** * Very similar to {@link #putAllDirect(java.util.Map)} except that this method first scans the map for any internal attributes * using special _JBOSS_INTERNAL_XXX Strings as keys, and uses these to set internal attributes before passing * the remaining attributes to {@link #putAllDirect(java.util.Map)}. Designed to be used by {@link org.jboss.cache.statetransfer.StateTransferIntegrator} * and {@link org.jboss.cache.interceptors.CacheLoaderInterceptor} classes which attempt to create nodes based on a data stream. * * @param state state to be applied * @since 2.1.0 */ void setInternalState(Map state); /** * Sets the validity of a node. By default, all nodes are valid unless they are deleted, invalidated or moved, either * locally or remotely. To be used in conjunction with {@link #isValid()}. * * @param valid if true, the node is marked as valid; if false, the node is invalid. * @param recursive if true, the validity flag passed in is applied to all children as well. * @since 2.1.0 */ void setValid(boolean valid, boolean recursive); } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/DefaultCacheFactory.java0000644000175000017500000001225211011031655027077 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.ConfigurationException; import org.jboss.cache.factories.ComponentFactory; import org.jboss.cache.factories.ComponentRegistry; import org.jboss.cache.factories.XmlConfigurationParser; import org.jboss.cache.invocation.CacheInvocationDelegate; import java.io.InputStream; /** * Default implementation of the {@link org.jboss.cache.CacheFactory} interface. *

        * This is a special instance of a {@link ComponentFactory} which contains bootstrap information for the * {@link org.jboss.cache.factories.ComponentRegistry}. *

        * In JBoss Cache 2.0.x, this was a singleton and you had to use {@link #getInstance()} to obtain an instance. From * JBoss Cache 2.1.x onwards, this is no longer a singleton and you can use the default no-arg constructor to obtain * a reference. {@link #getInstance()} has been deprecated and modified to return a new instance of this class. * * @author Manik Surtani (manik@jboss.org) * @see org.jboss.cache.factories.ComponentFactory */ public class DefaultCacheFactory extends ComponentFactory implements CacheFactory { private ClassLoader defaultClassLoader; /** * Note - this method used to return a singleton instance, and since 2.1.0 returns a new instance. The method is * deprecated and you should use the no-arg constructor to create a new instance of this factory. * * @return a NEW instance of this class. */ @SuppressWarnings("unchecked") @Deprecated public static CacheFactory getInstance() { return new DefaultCacheFactory(); } public Cache createCache() throws ConfigurationException { return createCache(true); } public Cache createCache(boolean start) throws ConfigurationException { return createCache(new Configuration(), start); } public Cache createCache(String configFileName) throws ConfigurationException { return createCache(configFileName, true); } public Cache createCache(String configFileName, boolean start) throws ConfigurationException { XmlConfigurationParser parser = new XmlConfigurationParser(); Configuration c = parser.parseFile(configFileName); return createCache(c, start); } /** * This implementation clones the configuration passed in before using it. * * @param configuration to use * @return a cache * @throws ConfigurationException if there are problems with the cfg */ public Cache createCache(Configuration configuration) throws ConfigurationException { return createCache(configuration, true); } /** * This implementation clones the configuration passed in before using it. * * @param configuration to use * @param start whether to start the cache * @return a cache * @throws ConfigurationException if there are problems with the cfg */ public Cache createCache(Configuration configuration, boolean start) throws ConfigurationException { try { CacheSPI cache = createAndWire(configuration); if (start) cache.start(); return cache; } catch (ConfigurationException ce) { throw ce; } catch (RuntimeException re) { throw re; } catch (Exception e) { throw new RuntimeException(e); } } protected CacheSPI createAndWire(Configuration configuration) throws Exception { CacheSPI spi = new CacheInvocationDelegate(); bootstrap(spi, configuration); return spi; } /** * Bootstraps this factory with a Configuration and a ComponentRegistry. */ private void bootstrap(CacheSPI spi, Configuration configuration) { // injection bootstrap stuff componentRegistry = new ComponentRegistry(configuration, spi); componentRegistry.registerDefaultClassLoader(defaultClassLoader); this.configuration = configuration; componentRegistry.registerComponent(spi, CacheSPI.class); } /** * Allows users to specify a default class loader to use for both the construction and running of the cache. * * @param loader class loader to use as a default. */ public void setDefaultClassLoader(ClassLoader loader) { this.defaultClassLoader = loader; } public Cache createCache(InputStream is) throws ConfigurationException { XmlConfigurationParser parser = new XmlConfigurationParser(); Configuration c = parser.parseStream(is); return createCache(c); } public Cache createCache(InputStream is, boolean start) throws ConfigurationException { XmlConfigurationParser parser = new XmlConfigurationParser(); Configuration c = parser.parseStream(is); return createCache(c, start); } @Override protected T construct(Class componentType) { throw new UnsupportedOperationException("Should never be invoked - this is a bootstrap factory."); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/Version.java0000644000175000017500000001100511147506161024670 0ustar twernertwernerpackage org.jboss.cache; import net.jcip.annotations.Immutable; /** * Contains version information about this release of JBoss Cache. * * @author Bela Ban * @version $Id: Version.java 4592 2007-10-10 16:44:36Z manik.surtani@jboss.com $ */ @Immutable public class Version { public static final String version = "2.2.2.GA"; public static final String codename = "Poblano"; //public static final String cvs = "$Id: Version.java 4592 2007-10-10 16:44:36Z manik.surtani@jboss.com $"; static final byte[] version_id = {'0', '2', '2', '2', 'G', 'A'}; private static final int MAJOR_SHIFT = 11; private static final int MINOR_SHIFT = 6; private static final int MAJOR_MASK = 0x00f800; private static final int MINOR_MASK = 0x0007c0; private static final int PATCH_MASK = 0x00003f; private static final short SHORT_1_2_3 = encodeVersion(1, 2, 3); private static final short SHORT_1_2_4_SP2 = encodeVersion(1, 2, 4); /** * Prints version information. */ public static void main(String[] args) { System.out.println("\nVersion: \t" + version); System.out.println("Codename: \t" + codename); //System.out.println("CVS: \t" + cvs); System.out.println("History: \t(see http://jira.jboss.com/jira/browse/JBCACHE for details)\n"); } /** * Returns version information as a string. */ public static String printVersion() { return "JBossCache '" + codename + "' " + version;// + "[ " + cvs + "]"; } public static String printVersionId(byte[] v, int len) { StringBuilder sb = new StringBuilder(); if (v != null) { if (len <= 0) len = v.length; for (int i = 0; i < len; i++) sb.append((char) v[i]); } return sb.toString(); } public static String printVersionId(byte[] v) { StringBuilder sb = new StringBuilder(); if (v != null) { for (byte aV : v) sb.append((char) aV); } return sb.toString(); } public static boolean compareTo(byte[] v) { if (v == null) return false; if (v.length < version_id.length) return false; for (int i = 0; i < version_id.length; i++) { if (version_id[i] != v[i]) return false; } return true; } public static int getLength() { return version_id.length; } public static short getVersionShort() { return getVersionShort(version); } public static short getVersionShort(String versionString) { if (versionString == null) throw new IllegalArgumentException("versionString is null"); // Special cases for version prior to 1.2.4.SP2 if ("1.2.4".equals(versionString)) return 124; else if ("1.2.4.SP1".equals(versionString)) return 1241; String parts[] = versionString.split("[\\.\\-]"); int a = 0; int b = 0; int c = 0; if (parts.length > 0) a = Integer.parseInt(parts[0]); if (parts.length > 1) b = Integer.parseInt(parts[1]); if (parts.length > 2) c = Integer.parseInt(parts[2]); return encodeVersion(a, b, c); } public static String getVersionString(short versionShort) { if (versionShort == SHORT_1_2_4_SP2) return "1.2.4.SP2"; switch (versionShort) { case 124: return "1.2.4"; case 1241: return "1.2.4.SP1"; default: return decodeVersion(versionShort); } } public static short encodeVersion(int major, int minor, int patch) { return (short) ((major << MAJOR_SHIFT) + (minor << MINOR_SHIFT) + patch); } public static String decodeVersion(short version) { int major = (version & MAJOR_MASK) >> MAJOR_SHIFT; int minor = (version & MINOR_MASK) >> MINOR_SHIFT; int patch = (version & PATCH_MASK); return major + "." + minor + "." + patch; } public static boolean isBefore124(short version) { return (version > 1241 && version <= SHORT_1_2_3); } /** * Retroweaver version info. */ public static class Retro { public static void main(String[] args) { System.out.println("\nVersion: \t" + version + " (Retroweaved for JDK 1.4.x compatibility)"); System.out.println("Codename: \t" + codename); //System.out.println("CVS: \t" + cvs); System.out.println("History: \t(see http://jira.jboss.com/jira/browse/JBCACHE for details)\n"); } } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/RegionManager.java0000644000175000017500000007557011032145203025767 0ustar twernertwernerpackage org.jboss.cache; import net.jcip.annotations.ThreadSafe; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import static org.jboss.cache.Region.Type.*; import org.jboss.cache.buddyreplication.BuddyFqnTransformer; import org.jboss.cache.buddyreplication.BuddyManager; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.ConfigurationException; import org.jboss.cache.config.EvictionConfig; import org.jboss.cache.config.EvictionPolicyConfig; import org.jboss.cache.config.EvictionRegionConfig; import org.jboss.cache.eviction.EvictionTimerTask; import org.jboss.cache.factories.annotations.Destroy; import org.jboss.cache.factories.annotations.Inject; import org.jboss.cache.factories.annotations.NonVolatile; import org.jboss.cache.factories.annotations.Start; import org.jboss.cache.factories.annotations.Stop; import org.jboss.cache.lock.LockManager; import static org.jboss.cache.lock.LockType.WRITE; import org.jgroups.Address; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; /** * Manages multiple {@link Region}s for a Cache instance. * * @author Manik Surtani * @since 2.0.0 */ @ThreadSafe @NonVolatile public class RegionManager { /** * The default region used in XML configuration files when defining eviction policies. Any * eviction settings bound under this 'default' Fqn is appplied to {@link org.jboss.cache.Fqn#ROOT} internally so * any region that is not explicitly defined comes under the settings defined for this default. */ public static final Fqn DEFAULT_REGION = Fqn.fromString("/_default_"); /** * A registry of regions that have been defined. */ private final Map regionsRegistry = new ConcurrentHashMap(); private boolean defaultInactive; private final Log log = LogFactory.getLog(RegionManager.class); private CacheSPI cache; private boolean usingEvictions; private EvictionConfig evictionConfig; private final EvictionTimerTask evictionTimerTask = new EvictionTimerTask(); protected final Set activationChangeNodes = Collections.synchronizedSet(new HashSet()); protected Configuration configuration; protected RPCManager rpcManager; private LockManager lockManager; private BuddyFqnTransformer buddyFqnTransformer; private boolean isUsingBR; @Inject void injectDependencies(CacheSPI cache, Configuration configuration, RPCManager rpcManager, LockManager lockManager, BuddyFqnTransformer transformer) { this.cache = cache; this.rpcManager = rpcManager; this.configuration = configuration; this.lockManager = lockManager; this.buddyFqnTransformer = transformer; } @Start protected void start() { log.trace("Starting region manager"); isUsingBR = configuration.getBuddyReplicationConfig() != null && configuration.getBuddyReplicationConfig().isEnabled(); if (configuration.getEvictionConfig() != null && configuration.getEvictionConfig().isValidConfig()) { // validate individual region configs now for (EvictionRegionConfig erc : configuration.getEvictionConfig().getEvictionRegionConfigs()) { EvictionPolicyConfig epc = erc.getEvictionPolicyConfig(); if (epc != null) epc.validate(); } setEvictionConfig(configuration.getEvictionConfig()); setUsingEvictions(true); } else { setUsingEvictions(false); log.debug("Not using an EvictionPolicy"); } setDefaultInactive(configuration.isInactiveOnStartup()); if (isUsingEvictions()) startEvictionThread(); } @Stop protected void stop() { if (isUsingEvictions()) stopEvictionThread(); } @Destroy protected void destroy() { regionsRegistry.clear(); activationChangeNodes.clear(); } /** * @return true if evictions are being processed. */ public boolean isUsingEvictions() { return usingEvictions; } /** * @return true if replication is by default inactive for new {@link Region}s. */ public boolean isDefaultInactive() { return defaultInactive; } /** * Sets if replication for new {@link Region}s is by default inactive. */ public void setDefaultInactive(boolean defaultInactive) { this.defaultInactive = defaultInactive; Region defaultRegion = regionsRegistry.get(Fqn.ROOT); if (defaultRegion != null) defaultRegion.setActive(!defaultInactive); } /** * Helper utility that checks for a {@link ClassLoader} registered for the * given {@link Fqn}, and if found sets it as the TCCL. If the given Fqn is * under the _BUDDY_BACKUP_ region, the equivalent region in the main * cache is used to find the {@link ClassLoader}. * * @param fqn Fqn pointing to a region for which a special classloader * may have been registered. */ public void setContextClassLoaderAsCurrent(Fqn fqn) { if (fqn.isChildOf(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN)) { if (fqn.size() <= 2) { fqn = Fqn.ROOT; } else { fqn = fqn.getSubFqn(2, fqn.size()); } } Region region = getRegion(fqn, false); ClassLoader regionCL = (region == null) ? null : region.getClassLoader(); if (regionCL != null) { Thread.currentThread().setContextClassLoader(regionCL); } } /** * Returns a region by {@link Fqn}, creating it optionally if absent. If the region does not exist and createIfAbsent * is false, a parent region which may apply to the {@link Fqn} is sought. *

        * Note that this takes into account the fact that this may be a Buddy Backup Fqn. If it is, the actual Fqn is calculated * and used instead. */ public Region getRegion(Fqn fqn, boolean createIfAbsent) { return getRegion(fqn, ANY, createIfAbsent); } /** * Retrieves a valid marshalling {@link Region} after taking into account that this may be a Buddy Backup Fqn. * If the fqn passed in is null, the region has been deactivated or if a region cannot be found, this method returns a null. * * @param fqn of the region to locate * @return a region */ public Region getValidMarshallingRegion(Fqn fqn) { if (fqn == null) return null; return getRegion(fqn, Region.Type.MARSHALLING, false); } /** * An overloaded form of {@link #getRegion(Fqn,boolean)} that takes an additional {@link org.jboss.cache.Region.Type} * parameter to force regions of a specific type. *

        * Note that this takes into account the fact that this may be a Buddy Backup Fqn. If it is, the actual Fqn is calculated * and used instead. * * @see org.jboss.cache.Region.Type */ public Region getRegion(Fqn fqn, Region.Type type, boolean createIfAbsent) { if (isUsingBR && fqn != null && buddyFqnTransformer.isBackupFqn(fqn)) { fqn = buddyFqnTransformer.getActualFqn(fqn); } if (log.isTraceEnabled()) log.trace("Contents of RegionsRegistry: " + regionsRegistry); Fqn fqnToUse = fqn; if (DEFAULT_REGION.equals(fqnToUse)) fqnToUse = Fqn.ROOT; // first see if a region for this specific Fqn exists if (regionsRegistry.containsKey(fqnToUse)) { Region r = regionsRegistry.get(fqnToUse); // this is a very poor way of telling whether a region is a marshalling one or an eviction one. :-( // mandates that class loaders be registered for marshalling regions. if (type == ANY || (type == MARSHALLING && r.getClassLoader() != null) || (type == EVICTION && r.getEvictionPolicyConfig() != null)) { return r; } } // if not, attempt to create one ... if (createIfAbsent) { Region r = new RegionImpl(fqnToUse, this); regionsRegistry.put(fqnToUse, r); if (type == MARSHALLING) { // insert current class loader into region so at least it is recognised as a marshalling region r.registerContextClassLoader(getClass().getClassLoader()); } return r; } // else try and find a parent which has a defined region, may return null if nothing is defined. Region nextBestThing = null; Fqn nextFqn = fqnToUse; while (nextBestThing == null) { nextFqn = nextFqn.getParent(); if (regionsRegistry.containsKey(nextFqn)) { Region r = regionsRegistry.get(nextFqn); if (log.isTraceEnabled()) log.trace("Trying next region " + nextFqn + " and got " + r); // this is a very poor way of telling whether a region is a marshalling one or an eviction one. :-( // mandates that class loaders be registered for marshalling regions. if (type == ANY || (type == MARSHALLING && r.getClassLoader() != null) || (type == EVICTION && r.getEvictionPolicyConfig() != null)) { nextBestThing = r; } } if (nextFqn.isRoot()) break; } // test if the default region has been defined. If not, and if the request // is for an eviction region, return null if (type == EVICTION && nextBestThing != null && nextBestThing.getFqn().isRoot() && !regionsRegistry.containsKey(Fqn.ROOT)) { log.trace("No default eviction region; returning null"); nextBestThing = null; } return nextBestThing; } /** * Returns a region using Fqn.fromString(fqn), calling {@link #getRegion(Fqn,boolean)} * * @param fqn * @param createIfAbsent * @see #getRegion(Fqn,boolean) */ public Region getRegion(String fqn, boolean createIfAbsent) { return getRegion(Fqn.fromString(fqn), createIfAbsent); } /** * Removes a {@link org.jboss.cache.Region} identified by the given fqn. * * @param fqn fqn of the region to remove * @return true if such a region existed and was removed. */ public boolean removeRegion(Fqn fqn) { Region r = regionsRegistry.remove(fqn); if (r == null) return false; if (isUsingEvictions() && r.getEvictionPolicy() != null) { evictionTimerTask.removeRegionToProcess(r); } return true; } /** * @return the eviction timer task object associated with this Region Manager. */ protected EvictionTimerTask getEvictionTimerTask() { return evictionTimerTask; } /** * Activates unmarshalling of replication messages for the region * rooted in the given Fqn. *

        * NOTE: This method will cause the creation of a node * in the local cache at subtreeFqn whether or not that * node exists anywhere else in the cluster. If the node does not exist * elsewhere, the local node will be empty. The creation of this node will * not be replicated. *

        * * @param fqn representing the region to be activated. * @throws RegionNotEmptyException if the node fqn * exists and already has either data or children */ public void activate(Fqn fqn) throws RegionNotEmptyException { activate(fqn, false); } /** * Attempts to activate a given region rooted at a given Fqn, similar to {@link #activate(Fqn)} except * that if the fqn is currently already in use (probably already been activated) this method is a no-op. * * @param fqn which represents the region to activate */ public void activateIfEmpty(Fqn fqn) { activate(fqn, true); } private void activate(Fqn fqn, boolean suppressRegionNotEmptyException) { try { if (log.isTraceEnabled()) log.trace("Activating region " + fqn); Region r = getRegion(fqn, false); if (r != null) { if (!defaultInactive && r.getClassLoader() == null) { // This region's state will no match that of a non-existent one // So, there is no reason to keep this region any more // (Brian) We shouldn't do this anymore; now outside code // can have a ref to the region!! removeRegion(fqn); } else { //r.activate(); r.setStatus(Region.Status.ACTIVATING); if (configuration.isFetchInMemoryState()) { activateRegion(r.getFqn(), suppressRegionNotEmptyException); } r.setActive(true); } } else if (defaultInactive) { // "Active" region is not the default, so create a region r = getRegion(fqn, true); // FIXME - persistent state transfer counts too! r.setStatus(Region.Status.ACTIVATING); if (configuration.isFetchInMemoryState()) { activateRegion(r.getFqn(), suppressRegionNotEmptyException); } r.setActive(true); } } catch (RuntimeException re) { throw re; } catch (Exception e) { throw new RuntimeException(e); } } /** * Causes the cache to transfer state for the subtree rooted at * subtreeFqn and to begin accepting replication messages * for that subtree. *

        * NOTE: This method will cause the creation of a node * in the local cache at subtreeFqn whether or not that * node exists anywhere else in the cluster. If the node does not exist * elsewhere, the local node will be empty. The creation of this node will * not be replicated. * * @param fqn Fqn string indicating the uppermost node in the * portion of the cache that should be activated. * @throws RegionNotEmptyException if the node subtreeFqn * exists and has either data or children */ private void activateRegion(Fqn fqn, boolean suppressRegionNotEmptyException) { // Check whether the node already exists and has data Node subtreeRoot = cache.peek(fqn, false, false); /* * Commented out on Nov 16,2006 Manik&Vladimir * * if (!(cache.isNodeEmpty(subtreeRoot))) { throw new RegionNotEmptyException("Node " + subtreeRoot.getFqn() + " already exists and is not empty"); }*/ if (isActivatingDeactivating(fqn)) { throw new CacheException("Region " + subtreeRoot.getFqn() + " is already being activated/deactivated"); } if (log.isDebugEnabled()) { log.debug("activating " + fqn); } try { // Add this fqn to the set of those we are activating // so calls to _getState for the fqn can return quickly activationChangeNodes.add(fqn); BuddyManager buddyManager = cache.getBuddyManager(); // Request partial state from the cluster and integrate it if (buddyManager == null) { // Get the state from any node that has it and put it // in the main cache if (subtreeRoot == null) { // We'll update this node with the state we receive // need to obtain all necessary locks. cache.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); subtreeRoot = cache.getRoot().addChild(fqn); cache.getInvocationContext().getOptionOverrides().setCacheModeLocal(false); } List

        members = cache.getMembers(); // Don't bother trying to fetch state if we are in LOCAL mode if (members != null && !members.isEmpty()) rpcManager.fetchPartialState(members, subtreeRoot.getFqn()); } else if (!buddyFqnTransformer.isBackupFqn(fqn)) { // Get the state from each DataOwner and integrate in their // respective buddy backup cache List
        buddies = buddyManager.getBackupDataOwners(); for (Address buddy : buddies) { List
        sources = new ArrayList
        (1); if (!cache.getMembers().contains(buddy)) continue; sources.add(buddy); Fqn buddyRoot = buddyFqnTransformer.getBackupFqn(buddy, fqn); subtreeRoot = cache.peek(buddyRoot, false, false); if (subtreeRoot == null) { // We'll update this node with the state we receive // need to obtain all necessary locks. // needs to be a LOCAL call! cache.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); subtreeRoot = cache.getRoot().addChild(buddyRoot); cache.getInvocationContext().getOptionOverrides().setCacheModeLocal(false); } rpcManager.fetchPartialState(sources, fqn, subtreeRoot.getFqn()); } } else { log.info("Attempting to activate a backup region. Not attempting to retrieve any state as this will be pushed."); } } catch (Throwable t) { log.error("failed to activate " + fqn, t); // "Re-deactivate" the region try { inactivateRegion(fqn); } catch (Exception e) { log.error("failed inactivating " + fqn, e); // just swallow this one and throw the first one } // Throw the exception on, wrapping if necessary if (t instanceof RegionNotEmptyException) { if (!suppressRegionNotEmptyException) throw (RegionNotEmptyException) t; } else if (t instanceof CacheException) { throw (CacheException) t; } else { throw new CacheException(t.getClass().getName() + " " + t.getLocalizedMessage(), t); } } finally { activationChangeNodes.remove(fqn); } } /** * Convenienve method. If the region defined by fqn does not exist, {@link #isDefaultInactive()} is returned, otherwise * !{@link Region#isActive()} is returned. * * @param fqn fqn to test * @return true if inactive */ public boolean isInactive(Fqn fqn) { Region region = getRegion(fqn, false); return region == null ? defaultInactive : !region.isActive(); } /** * Causes the cache to stop accepting replication events for the subtree * rooted at subtreeFqn and evict all nodes in that subtree. *

        * This is legacy code and should not be called directly. This is a private method for now and will be refactored out. * You should be using {@link #activate(Fqn)} and {@link #deactivate(Fqn)} *

        * * @param fqn Fqn string indicating the uppermost node in the * portion of the cache that should be activated. * @throws RegionNameConflictException if subtreeFqn indicates * a node that is part of another * subtree that is being specially * managed (either by activate/inactiveRegion() * or by registerClassLoader()) * @throws CacheException if there is a problem evicting nodes * @throws IllegalStateException if {@link org.jboss.cache.config.Configuration#isUseRegionBasedMarshalling()} is false */ private void inactivateRegion(Fqn fqn) throws CacheException { if (isActivatingDeactivating(fqn)) { throw new CacheException("Region " + fqn + " is already being activated/deactivated"); } NodeSPI parent = null; NodeSPI subtreeRoot = null; boolean parentLocked = false; boolean subtreeLocked = false; try { // Record that this fqn is in status change, so can't provide state activationChangeNodes.add(fqn); if (!isInactive(fqn)) { deactivate(fqn); } // Create a list with the Fqn in the main cache and any buddy backup trees BuddyManager buddyManager = cache.getBuddyManager(); ArrayList list = new ArrayList(); list.add(fqn); if (buddyManager != null) { Set buddies = cache.peek(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN, false, false).getChildrenNames(); if (buddies != null) { for (Object buddy : buddies) { list.add(buddyFqnTransformer.getBackupFqn((String) buddy, fqn)); } } } long stateFetchTimeout = cache.getConfiguration().getLockAcquisitionTimeout() + 5000; // Remove the subtree from the main cache and any buddy backup trees for (Fqn subtree : list) { subtreeRoot = cache.peek(subtree, false, false); if (subtreeRoot != null) { // Acquire locks Object owner = getOwnerForLock(); subtreeLocked = lockManager.lockAll(subtreeRoot, WRITE, owner, stateFetchTimeout); // Lock the parent, as we're about to write to it parent = subtreeRoot.getParent(); if (parent != null) parentLocked = lockManager.lock(parent.getFqn(), WRITE, owner, stateFetchTimeout); // Remove the subtree cache.evict(subtree, true); //cache._evictSubtree(subtree); // Release locks if (parent != null) { log.debug("forcing release of locks in parent"); lockManager.unlockAll(parent); } parentLocked = false; log.debug("forcing release of all locks in subtree"); lockManager.unlockAll(subtreeRoot); subtreeLocked = false; } } } finally { // If we didn't succeed, undo the marshalling change // NO. Since we inactivated, we may have missed changes //if (!success && !inactive) // marshaller_.activate(subtreeFqn); // If necessary, release locks if (parentLocked) { log.debug("forcing release of locks in parent"); try { if (parent != null) lockManager.unlockAll(parent); } catch (Throwable t) { log.error("failed releasing locks", t); } } if (subtreeLocked) { log.debug("forcing release of all locks in subtree"); try { if (subtreeRoot != null) lockManager.unlockAll(subtreeRoot); } catch (Throwable t) { log.error("failed releasing locks", t); } } activationChangeNodes.remove(fqn); } } private Object getOwnerForLock() { Object owner = cache.getCurrentTransaction(); return owner == null ? Thread.currentThread() : owner; } /** *

        * This is legacy code and should not be called directly. This is a private method for now and will be refactored out. * You should be using {@link #activate(Fqn)} and {@link #deactivate(Fqn)} *

        * * @param fqn fqn of the region * @return true if the region defined by the fqn is in the process of activating/deactivating */ private boolean isActivatingDeactivating(Fqn fqn) { return activationChangeNodes.contains(fqn); } /** * Returns true if the region exists * * @param fqn FQN of the region * @param type type of region to search for * @return true if the region exists */ public boolean hasRegion(Fqn fqn, Region.Type type) { Region r = regionsRegistry.get(fqn); if (r == null) return false; switch (type) { case ANY: return true; case EVICTION: return r.getEvictionPolicy() != null && evictionTimerTask.isRegionRegisteredForProcessing(r); case MARSHALLING: return r.isActive() && r.getClassLoader() != null; } // should never reach here? return false; } /** * Disables unmarshalling of replication messages for the region * rooted in the given Fqn. * * @param fqn */ public void deactivate(Fqn fqn) { try { Region region = getRegion(fqn, false); if (region != null) { if (defaultInactive && region.getClassLoader() == null) { // This region's state will no match that of a non-existent one // So, there is no reason to keep this region any more // FIXME (Brian) We shouldn't do this anymore; now outside code can have a ref to the region!! removeRegion(fqn); } else { //region.deactivate(); region.setActive(false); // FIXME - we should always clean up; otherwise stale data is in memory! if (cache.getConfiguration().isFetchInMemoryState()) { inactivateRegion(fqn); } } } else if (!defaultInactive) { region = getRegion(fqn, true); region.setActive(false); // FIXME - we should always clean up; otherwise stale data is in memory! if (cache.getConfiguration().isFetchInMemoryState()) { inactivateRegion(fqn); } } } catch (RuntimeException re) { throw re; } catch (Exception e) { throw new RuntimeException(e); } } /** * Resets the region manager's regions registry */ public void reset() { regionsRegistry.clear(); } /** * Returns an ordered list of all regions. * Note that the ordered list returned is sorted according to the natural order defined in the {@link Comparable} interface, which {@link org.jboss.cache.Region} extends. * * @param type Type of region to return * @return an ordered list of all regions, based on the type requested. */ public List getAllRegions(Region.Type type) { List regions; if (type != ANY) { regions = new ArrayList(); // we need to loop thru the regions and only select specific regions to rtn. for (Region r : regionsRegistry.values()) { if ((type == EVICTION && r.getEvictionPolicy() != null && evictionTimerTask.isRegionRegisteredForProcessing(r)) || (type == MARSHALLING && r.isActive() && r.getClassLoader() != null)) regions.add(r); } } else { // put all regions regions = new ArrayList(regionsRegistry.values()); } Collections.sort(regions); return regions; } /** * Sets if evictions are processed. */ public void setUsingEvictions(boolean usingEvictions) { this.usingEvictions = usingEvictions; } /** * Sets the eviction configuration. */ public void setEvictionConfig(EvictionConfig evictionConfig) { this.evictionConfig = evictionConfig; // JBAS-1288 // Try to establish a default region if there isn't one already boolean needDefault; List ercs = evictionConfig.getEvictionRegionConfigs(); // APPROACH 1: Scan for a default region, try to add if not there. // This will try to add the region if it is missing but seems to break // some unit tests that configure one or more non-default regions and // no default region (e.g. the eviction.minttl tests). So, doing this // seems to add a new semantic. For now comment this out and use APPROACH 2 // for (EvictionRegionConfig erc : ercs) // { // if (DEFAULT_REGION.equals(erc.getRegionFqn())) // { // needDefault = false; // break; // } // } // APPROACH 2: Only add a default region if there are no regions. This is // contrary to the idea that there *must* be a default region, but some // unit tests fail w/ APPROACH 1, so for now we go with this approach. needDefault = ercs.size() == 0; if (needDefault) { // This may throw ConfigurationException if there is no default // eviction policy class EvictionRegionConfig dflt = evictionConfig.createDefaultEvictionRegionConfig(); ercs.add(0, dflt); // put it first // Need to pass this back into the evictionConfig so it knows // about the new region evictionConfig.setEvictionRegionConfigs(ercs); } // create regions for the regions defined in the evictionConfig. // scan to be sure the _default_ region isn't added twice boolean setDefault = false; for (EvictionRegionConfig erc : ercs) { Fqn fqn = erc.getRegionFqn(); if (log.isTraceEnabled()) log.trace("Creating eviction region " + fqn); if (fqn.equals(DEFAULT_REGION)) { if (setDefault) { throw new ConfigurationException("A default region for evictions has already been set for this cache"); } if (log.isTraceEnabled()) log.trace("Applying settings for " + DEFAULT_REGION + " to Fqn.ROOT"); fqn = Fqn.ROOT; setDefault = true; } Region r = getRegion(fqn, true); r.setEvictionPolicy(erc.getEvictionPolicyConfig()); } } /** * Starts the eviction processing thread. */ public void startEvictionThread() { evictionTimerTask.init(evictionConfig.getWakeupIntervalSeconds()); } /** * Stops the eviction processing thread */ public void stopEvictionThread() { evictionTimerTask.stop(); } /** * Returns a string containing debug information on every region. * * @return Regions as a string */ public String dumpRegions() { StringBuilder sb = new StringBuilder(); for (Region r : regionsRegistry.values()) { sb.append("\tRegion ").append(r); sb.append("\n"); } return sb.toString(); } /** * Returns a string containing debug information on every region. */ @Override public String toString() { return "RegionManager " + dumpRegions(); } public CacheSPI getCache() { return cache; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/RegionImpl.java0000644000175000017500000002001111021223216025271 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.EvictionPolicyConfig; import org.jboss.cache.config.EvictionRegionConfig; import org.jboss.cache.eviction.EvictedEventNode; import org.jboss.cache.eviction.EvictionPolicy; import org.jboss.cache.eviction.NodeEventType; import org.jboss.cache.util.Util; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; /** * Default implementation of a {@link Region} * * @author Manik Surtani (manik@jboss.org) */ public class RegionImpl implements Region { private static final Log log = LogFactory.getLog(RegionImpl.class); private final RegionManager regionManager; private Fqn fqn; private Status status; private ClassLoader classLoader; private BlockingQueue nodeEventQueue = null; private int capacityWarnThreshold = 0; private EvictionRegionConfig configuration = new EvictionRegionConfig(); private EvictionPolicy policy; /** * Constructs a marshalling region from an fqn and region manager. */ public RegionImpl(Fqn fqn, RegionManager regionManager) { this.fqn = fqn; this.regionManager = regionManager; status = !regionManager.isDefaultInactive() ? Status.ACTIVE : Status.INACTIVE; } /** * Constructs an eviction region from a policy and configuration, defined by an fqn and region manager. */ public RegionImpl(EvictionPolicy policy, EvictionRegionConfig config, Fqn fqn, RegionManager regionManager) { this(fqn, regionManager); this.configuration = config; this.policy = policy; createQueue(); } public Configuration getCacheConfiguration() { if (regionManager != null && regionManager.getCache() != null) return regionManager.getCache().getConfiguration(); else return null; } public void registerContextClassLoader(ClassLoader classLoader) { this.classLoader = classLoader; } public void unregisterContextClassLoader() { this.classLoader = null; } public void activate() { regionManager.activate(fqn); status = Status.ACTIVE; } public void activateIfEmpty() { regionManager.activateIfEmpty(fqn); status = Status.ACTIVE; } public void deactivate() { regionManager.deactivate(fqn); status = Status.INACTIVE; } public boolean isActive() { return status == Status.ACTIVE; } public ClassLoader getClassLoader() { return classLoader; } public Fqn getFqn() { return fqn; } public void setStatus(Status status) { this.status = status; } public Status getStatus() { return status; } public void setActive(boolean b) { status = b ? Status.ACTIVE : Status.INACTIVE; } // -------- eviction stuff ----- public void markNodeCurrentlyInUse(Fqn fqn, long timeout) { EvictedEventNode markUse = new EvictedEventNode(fqn, NodeEventType.MARK_IN_USE_EVENT); markUse.setInUseTimeout(timeout); putNodeEvent(markUse); } public void unmarkNodeCurrentlyInUse(Fqn fqn) { EvictedEventNode markNoUse = new EvictedEventNode(fqn, NodeEventType.UNMARK_USE_EVENT); putNodeEvent(markNoUse); } @Override public String toString() { return "RegionImpl{" + "fqn=" + fqn + "; classloader=" + classLoader + "; status=" + status + "; eviction=" + (getEvictionPolicy() != null) + "; timerThreadRegistered=" + (getEvictionPolicy() != null && regionManager.getEvictionTimerTask().isRegionRegisteredForProcessing(this)) + '}'; } public int compareTo(Region other) { return getFqn().compareTo(other.getFqn()); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; RegionImpl region = (RegionImpl) o; if (fqn != null ? !fqn.equals(region.fqn) : region.fqn != null) return false; return true; } @Override public int hashCode() { return (fqn != null ? fqn.hashCode() : 0); } public void putNodeEvent(EvictedEventNode event) { try { if (nodeEventQueue == null) createQueue();// in case the queue does not exist yet. if (nodeEventQueue.size() > capacityWarnThreshold) { log.warn("putNodeEvent(): eviction node event queue size is at 98% threshold value of capacity: " + configuration.getEventQueueSize() + " Region: " + fqn + " You will need to reduce the wakeUpIntervalSeconds parameter."); } nodeEventQueue.put(event); } catch (InterruptedException e) { log.debug("give up put", e); } } public EvictedEventNode takeLastEventNode() { try { return nodeEventQueue.poll(0, TimeUnit.SECONDS); } catch (InterruptedException e) { log.debug("trace", e); } return null; } public int nodeEventQueueSize() { return nodeEventQueue.size(); } public void resetEvictionQueues() { nodeEventQueue.clear(); } private synchronized void createQueue() { if (nodeEventQueue == null) { if (configuration == null) { throw new IllegalArgumentException("null eviction configuration"); } int size = configuration.getEventQueueSize(); capacityWarnThreshold = (98 * size) / 100 - 100; if (capacityWarnThreshold <= 0) { throw new RuntimeException("Capacity warn threshold used in eviction is smaller than 1."); } nodeEventQueue = new LinkedBlockingQueue(size); } } public EvictionRegionConfig getEvictionRegionConfig() { return this.configuration; } public EvictionPolicyConfig getEvictionPolicyConfig() { return configuration == null ? null : configuration.getEvictionPolicyConfig(); } public EvictionPolicy getEvictionPolicy() { return policy; } public void setEvictionPolicy(EvictionPolicyConfig evictionPolicyConfig) { configuration.setEvictionPolicyConfig(evictionPolicyConfig); policy = createPolicy(evictionPolicyConfig.getEvictionPolicyClass()); regionManager.getEvictionTimerTask().addRegionToProcess(this); if (nodeEventQueue == null) createQueue(); } private EvictionPolicy createPolicy(String className) { if (className == null) { throw new IllegalArgumentException("null className"); } try { if (log.isTraceEnabled()) log.trace("Instantiating " + className); EvictionPolicy ep = (EvictionPolicy) Util.loadClass(className).newInstance(); ep.setCache(regionManager.getCache()); return ep; } catch (Exception e) { log.fatal("Unable to instantiate eviction policy class " + className, e); return null; } } public RegionImpl clone(Fqn newRoot) { RegionImpl clone = null; try { clone = (RegionImpl) super.clone(); clone.policy = policy; clone.configuration = configuration; clone.status = status; clone.fqn = Fqn.fromRelativeFqn(newRoot, fqn); // we also need to copy all of the eviction event nodes to the clone's queue clone.createQueue(); for (EvictedEventNode een : this.nodeEventQueue) { EvictedEventNode cloneEEN = een.clone(newRoot); clone.putNodeEvent(cloneEEN); } } catch (CloneNotSupportedException e) { // problems cloning? Should never get here. } return clone; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/Modification.java0000644000175000017500000001311211041076631025646 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache; import java.io.Externalizable; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.util.Map; /** * Represents a modification in the cache. Contains the nature of the modification * (e.g. PUT, REMOVE), the fqn of the node, the new value and the previous value. * A list of modifications will be sent to all nodes in a cluster when a transaction * has been committed (PREPARE phase). A Modification is also used to roll back changes, * e.g. since we know the previous value, we can reconstruct the previous state by * applying the changes in a modification listin reverse order. * * @author Bela Ban Apr 12, 2003 * @version $Revision: 6346 $ */ public class Modification implements Externalizable { private static final long serialVersionUID = 7463314130283897197L; public static enum ModificationType { PUT_KEY_VALUE, PUT_DATA, PUT_DATA_ERASE, REMOVE_NODE, REMOVE_KEY_VALUE, REMOVE_DATA, MOVE, UNKNOWN } private ModificationType type = ModificationType.UNKNOWN; private Fqn fqn = null; private Fqn fqn2 = null; private Object key = null; private Object value = null; private Object old_value = null; private Map data = null; /** * Constructs a new modification. */ public Modification() { } /** * Constructs a new modification with details. */ public Modification(ModificationType type, Fqn fqn, Object key, Object value) { this.type = type; this.fqn = fqn; this.key = key; this.value = value; } /** * Constructs a new modification with key. */ public Modification(ModificationType type, Fqn fqn, Object key) { this.type = type; this.fqn = fqn; this.key = key; } /** * Constructs a new modification with data map. */ public Modification(ModificationType type, Fqn fqn, Map data) { this.type = type; this.fqn = fqn; this.data = data; } /** * Constructs a new modification with fqn only. */ public Modification(ModificationType type, Fqn fqn) { this.type = type; this.fqn = fqn; } /** * Constructs a new modification with fqn only. */ public Modification(ModificationType type, Fqn fqn1, Fqn fqn2) { this.type = type; this.fqn = fqn1; this.fqn2 = fqn2; } /** * Returns the type of modification. */ public ModificationType getType() { return type; } /** * Sets the type of modification. */ public void setType(ModificationType type) { this.type = type; } /** * Returns the modification fqn. */ public Fqn getFqn() { return fqn; } /** * Sets the modification fqn. */ public void setFqn(Fqn fqn) { this.fqn = fqn; } public void setFqn2(Fqn fqn2) { this.fqn2 = fqn2; } public Fqn getFqn2() { return fqn2; } /** * Returns the modification key. */ public Object getKey() { return key; } /** * Sets the modification key. */ public void setKey(Object key) { this.key = key; } /** * Returns the modification value. */ public Object getValue() { return value; } /** * Sets the modification value. */ public void setValue(Object value) { this.value = value; } /** * Returns the post modification old value. */ public Object getOldValue() { return old_value; } /** * Sets the post modification old value. */ public void setOldValue(Object old_value) { this.old_value = old_value; } /** * Returns the modification Map set. */ public Map getData() { return data; } /** * Sets the modification Map set. */ public void setData(Map data) { this.data = data; } /** * Writes data to an external stream. */ public void writeExternal(ObjectOutput out) throws IOException { out.writeObject(type); out.writeBoolean(fqn != null); if (fqn != null) { fqn.writeExternal(out); } out.writeObject(key); out.writeObject(value); out.writeObject(old_value); out.writeBoolean(data != null); if (data != null) { out.writeObject(data); } } /** * Reads data from an external stream. */ public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { type = (ModificationType) in.readObject(); if (in.readBoolean()) { fqn = Fqn.fromExternalStream(in); } key = in.readObject(); value = in.readObject(); old_value = in.readObject(); if (in.readBoolean()) { data = (Map) in.readObject(); } } /** * Returns debug information about this modification. */ @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(type.toString()).append(": ").append(fqn); if (key != null) { sb.append("\nkey=").append(key); } if (value != null) { sb.append("\nvalue=").append(value); } if (old_value != null) { sb.append("\nold_value=").append(old_value); } if (data != null) { sb.append("\ndata=").append(data); } return sb.toString(); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/lock/0000755000175000017500000000000011376174007023337 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/lock/NodeBasedLockManager.java0000644000175000017500000002307211031017377030130 0ustar twernertwernerpackage org.jboss.cache.lock; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.DataContainer; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.NodeSPI; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.annotations.Inject; import org.jboss.cache.factories.annotations.Start; import java.util.ArrayList; import java.util.Collection; import java.util.List; /** * @author Mircea.Markus@jboss.com * @since 2.2 */ @SuppressWarnings("deprecation") public class NodeBasedLockManager implements LockManager { private static final Log log = LogFactory.getLog(NodeBasedLockManager.class); private static final boolean trace = log.isTraceEnabled(); protected Configuration configuration; protected long lockAcquisitionTimeout; protected DataContainer dataContainer; protected NodeSPI rootNode; @Inject public void inject(Configuration configuration, DataContainer dataContainer) { this.configuration = configuration; this.dataContainer = dataContainer; } @Start public void setRootNode() { this.lockAcquisitionTimeout = configuration.getLockAcquisitionTimeout(); rootNode = dataContainer.getRoot(); } /** * Internal method that acquires a lock and returns the lock object. Currently uses {@link IdentityLock} objects; may change in * future to use standard JDK locks. * * @param fqn Fqn to lock * @param lockType type of lock to acquire * @param owner owner to acquire lock for * @param timeout timeout * @return lock if acquired, null otherwise. */ private NodeLock acquireLock(Fqn fqn, LockType lockType, Object owner, long timeout) { return acquireLock(dataContainer.peek(fqn), lockType, owner, timeout); } private NodeLock acquireLock(NodeSPI node, LockType lockType, Object owner, long timeout) { if (node == null) return null; NodeLock lock = node.getLock(); boolean acquired = false; try { acquired = lock.acquire(owner, timeout, lockType); } catch (InterruptedException e) { // interrupted trying to acquire lock! } if (acquired) return lock; else return null; } public Object getLockOwner(InvocationContext ctx) { return ctx.getGlobalTransaction() != null ? ctx.getGlobalTransaction() : Thread.currentThread(); } public boolean lock(Fqn fqn, LockType lockType, Object owner) { return acquireLock(fqn, lockType, owner, lockAcquisitionTimeout) != null; } public boolean lock(Fqn fqn, LockType lockType, Object owner, long timeout) { return acquireLock(fqn, lockType, owner, timeout) != null; } public boolean lock(NodeSPI node, LockType lockType, Object owner) { return acquireLock(node, lockType, owner, lockAcquisitionTimeout) != null; } public boolean lock(NodeSPI node, LockType lockType, Object owner, long timeout) { return acquireLock(node, lockType, owner, timeout) != null; } public boolean lockAndRecord(Fqn fqn, LockType lockType, InvocationContext ctx) { return lockAndRecord(dataContainer.peek(fqn), lockType, ctx); } public boolean lockAndRecord(NodeSPI node, LockType lockType, InvocationContext ctx) { NodeLock lock = acquireLock(node, lockType, getLockOwner(ctx), ctx.getContextLockAcquisitionTimeout(lockAcquisitionTimeout)); if (lock != null) { if (ctx.getTransactionEntry() != null) { ctx.getTransactionEntry().addLock(lock); } else { ctx.addInvocationLockAcquired(lock); } return true; } else { return false; } } public void unlock(InvocationContext ctx) { List locks = ctx.getTransactionEntry() != null ? ctx.getTransactionEntry().getLocks() : ctx.getInvocationLocksAcquired(); if (locks == null || locks.isEmpty()) return; Object owner = getLockOwner(ctx); // Copying out to an array is faster than creating an ArrayList and iterating, // since list creation will just copy out to an array internally NodeLock[] lockArray = locks.toArray(new NodeLock[locks.size()]); for (int i = lockArray.length - 1; i >= 0; i--) { if (trace) log.trace("releasing lock for " + lockArray[i].getFqn() + " (" + lockArray[i] + "), owner " + owner); lockArray[i].release(owner); } locks.clear(); } private void unlock(NodeLock lock, Object owner) { if (trace) log.trace("releasing lock for " + lock.getFqn() + " (" + lock + "), owner " + owner); lock.release(owner); } public void unlock(Fqn fqn, Object owner) { unlock(dataContainer.peek(fqn).getLock(), owner); } public void unlock(NodeSPI node, Object owner) { if (node == null) return; unlock(node.getLock(), owner); } public boolean lockAll(NodeSPI node, LockType lockType, Object owner) { return lockAll(node, lockType, owner, lockAcquisitionTimeout, false); } public boolean lockAll(NodeSPI node, LockType lockType, Object owner, long timeout) { return lockAll(node, lockType, owner, timeout, false); } /** * Locks all nodes, and returns the NodeLocks in a List. Returns null if the locks could not be acquired. * * @param node node to lock * @param lockType type of lock to acquire * @param owner lock owner * @param timeout timeout * @param excludeInternalFqns if true, internal Fqns are excluded. * @return list of locks acquired, or null. */ private List lockAllNodes(NodeSPI node, LockType lockType, Object owner, long timeout, boolean excludeInternalFqns) { if (node == null) return null; List locks = null; try { locks = new ArrayList(node.getLock().acquireAll(owner, timeout, lockType, excludeInternalFqns)); } catch (InterruptedException e) { // interrupted } return locks; } public boolean lockAll(NodeSPI node, LockType lockType, Object owner, long timeout, boolean excludeInternalFqns) { return lockAllNodes(node, lockType, owner, timeout, excludeInternalFqns) != null; } public boolean lockAllAndRecord(Fqn fqn, LockType lockType, InvocationContext ctx) { return lockAllAndRecord(dataContainer.peek(fqn), lockType, ctx); } public boolean lockAllAndRecord(NodeSPI node, LockType lockType, InvocationContext ctx) { List locks = lockAllNodes(node, lockType, getLockOwner(ctx), ctx.getContextLockAcquisitionTimeout(lockAcquisitionTimeout), false); if (locks == null) return false; if (locks.size() > 0) { if (ctx.getGlobalTransaction() != null) { ctx.getTransactionEntry().addLocks(locks); } else { ctx.addInvocationLocksAcquired(locks); } } return true; } public void unlockAll(NodeSPI node, Object owner) { // recursively visit node and all children, and release all locks held by a given owner. node.getLock().releaseAll(owner); } public void unlockAll(NodeSPI node) { // recursively visit node and all children, and release all locks held by a given owner. node.getLock().releaseAll(); } public boolean ownsLock(Fqn fqn, LockType lockType, Object owner) { NodeSPI n = dataContainer.peek(fqn, true, true); if (n == null) return false; NodeLock lock = n.getLock(); switch (lockType) { case READ: return lock.isReadLocked() && lock.isOwner(owner); case WRITE: return lock.isWriteLocked() && lock.isOwner(owner); case NONE: default: return false; } } public boolean ownsLock(Fqn fqn, Object owner) { return ownsLock(dataContainer.peek(fqn, true, true), owner); } public boolean ownsLock(NodeSPI node, Object owner) { return node != null && node.getLock().isOwner(owner); } public boolean isLocked(NodeSPI n) { return n.getLock().isLocked(); } public boolean isLocked(NodeSPI n, LockType type) { switch (type) { case READ: return n.getLock().isReadLocked(); case WRITE: return n.getLock().isWriteLocked(); case NONE: default: return false; } } public Object getWriteOwner(Fqn f) { return getWriteOwner(dataContainer.peek(f)); } public Collection getReadOwners(Fqn f) { return getReadOwners(dataContainer.peek(f)); } public Object getWriteOwner(NodeSPI node) { return node.getLock().getWriterOwner(); } public Collection getReadOwners(NodeSPI node) { return node.getLock().getReaderOwners(); } public String printLockInfo(NodeSPI node) { StringBuilder sb = new StringBuilder("\n"); int indent = 0; for (Object n : node.getChildrenDirect()) { ((NodeSPI) n).getLock().printLockInfo(sb, indent); sb.append("\n"); } return sb.toString(); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/lock/LockStrategy.java0000755000175000017500000000137610536332011026613 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.lock; import java.util.concurrent.locks.Lock; /** * Interface to specify lock strategy, e.g., for different isolation levels. * * @author Ben Wang */ public interface LockStrategy { /** * Return a read lock object. */ Lock readLock(); /** * Return a write lock object. */ Lock writeLock(); /** * Attempt to upgrade the current read lock to write lock with * msecs timeout. * * @param msecs Timeout in milliseconds. * @return Lock object. Will return null if timeout or failed. */ Lock upgradeLockAttempt(long msecs) throws UpgradeException; } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/lock/ThreadLocalMap.java0000644000175000017500000000353411004116445027015 0ustar twernertwernerpackage org.jboss.cache.lock; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Set; /** * Map which reduces concurrency and potential memory leaks for non-static ThreadLocals. * http://www.me.umn.edu/~shivane/blogs/cafefeed/2004/06/of-non-static-threadlocals-and-memory.html * * @author Brian Dueck * @version $Id: ThreadLocalMap.java 5665 2008-04-24 14:45:57Z manik.surtani@jboss.com $ */ public class ThreadLocalMap implements Map { private final ThreadLocal threadLocal = new ThreadLocal() { @Override protected Object initialValue() { return new HashMap(); } }; @SuppressWarnings("unchecked") private Map getThreadLocalMap() { return (Map) threadLocal.get(); } public V put(K key, V value) { return getThreadLocalMap().put(key, value); } public V get(Object key) { return getThreadLocalMap().get(key); } public V remove(Object key) { return getThreadLocalMap().remove(key); } public int size() { return getThreadLocalMap().size(); } public void clear() { getThreadLocalMap().clear(); } public boolean isEmpty() { return getThreadLocalMap().isEmpty(); } public boolean containsKey(Object arg0) { return getThreadLocalMap().containsKey(arg0); } public boolean containsValue(Object arg0) { return getThreadLocalMap().containsValue(arg0); } public Collection values() { return getThreadLocalMap().values(); } public void putAll(Map arg0) { getThreadLocalMap().putAll(arg0); } public Set> entrySet() { return getThreadLocalMap().entrySet(); } public Set keySet() { return getThreadLocalMap().keySet(); } }jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/lock/TimeoutException.java0000644000175000017500000000177511032145203027502 0ustar twernertwerner// $Id: TimeoutException.java 6120 2008-06-30 11:58:59Z manik.surtani@jboss.com $ /* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.lock; import org.jboss.cache.CacheException; /** * Thrown when a timeout occurred. used by operations with timeouts, e.g. lock * acquisition, or waiting for responses from all members. * * @author Bela Ban. * @version $Revision: 6120 $ *

        *

        Revisions: *

        *

        Dec 28 2002 Bela Ban: first implementation */ public class TimeoutException extends CacheException { /** * The serialVersionUID */ private static final long serialVersionUID = -8096787619908687038L; public TimeoutException() { super(); } public TimeoutException(String msg) { super(msg); } public TimeoutException(String msg, Throwable cause) { super(msg, cause); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/lock/LockUtil.java0000644000175000017500000002241611017270377025734 0ustar twernertwernerpackage org.jboss.cache.lock; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.NodeSPI; import org.jboss.cache.commands.write.PutDataMapCommand; import org.jboss.cache.factories.CommandsFactory; import org.jboss.cache.statetransfer.StateTransferManager; import org.jboss.cache.transaction.GlobalTransaction; import org.jboss.cache.transaction.TransactionEntry; import org.jboss.cache.transaction.TransactionTable; import javax.transaction.Status; import javax.transaction.Transaction; import javax.transaction.TransactionManager; import java.util.HashMap; import java.util.List; import java.util.Map; public abstract class LockUtil { private final static Log log = LogFactory.getLog(StateTransferManager.class); private static final boolean trace = log.isTraceEnabled(); private static interface TransactionLockStatus extends Status { int STATUS_BROKEN = Integer.MIN_VALUE; } public static boolean breakTransactionLock(NodeSPI node, LockManager lockManager, GlobalTransaction gtx, boolean localTx, TransactionTable tx_table, TransactionManager tm) { boolean broken = false; int tryCount = 0; int lastStatus = TransactionLockStatus.STATUS_BROKEN; while (!broken && lockManager.ownsLock(node.getFqn(), gtx)) { int status = breakTransactionLock(gtx, node, lockManager, tx_table, tm, localTx, lastStatus, tryCount); if (status == TransactionLockStatus.STATUS_BROKEN) { broken = true; } else if (status != lastStatus) { tryCount = 0; } lastStatus = status; tryCount++; } return broken; } /** * Attempts to release the lock held by gtx by altering the * underlying transaction. Different strategies will be employed * depending on the status of the transaction and param * tryCount. Transaction may be rolled back or marked * rollback-only, or the lock may just be broken, ignoring the tx. Makes an * effort to not affect the tx or break the lock if tx appears to be in * the process of completion; param tryCount is used to help * make decisions about this. *

        * This method doesn't guarantee to have broken the lock unless it returns * {@link TransactionLockStatus#STATUS_BROKEN}. * * @param gtx the gtx holding the lock * @param lastStatus the return value from a previous invocation of this * method for the same lock, or Status.STATUS_UNKNOW * for the first invocation. * @param tryCount number of times this method has been called with * the same gtx, lock and lastStatus arguments. Should * be reset to 0 anytime lastStatus changes. * @return the current status of the Transaction associated with * gtx, or {@link TransactionLockStatus#STATUS_BROKEN} * if the lock held by gtx was forcibly broken. */ private static int breakTransactionLock(GlobalTransaction gtx, NodeSPI node, LockManager lockManager, TransactionTable transactionTable, TransactionManager tm, boolean localTx, int lastStatus, int tryCount) { int status = Status.STATUS_UNKNOWN; Transaction tx = transactionTable.getLocalTransaction(gtx); if (tx != null) { try { status = tx.getStatus(); if (status != lastStatus) { tryCount = 0; } switch (status) { case Status.STATUS_ACTIVE: case Status.STATUS_MARKED_ROLLBACK: case Status.STATUS_PREPARING: case Status.STATUS_UNKNOWN: if (tryCount == 0) { if (trace) { log.trace("Attempting to break transaction lock held " + " by " + gtx + " by rolling back local tx"); } // This thread has to join the tx tm.resume(tx); try { tx.rollback(); } finally { tm.suspend(); } } else if (tryCount > 100) { // Something is wrong; our initial rollback call // didn't generate a valid state change; just force it lockManager.unlock(node.getFqn(), gtx); status = TransactionLockStatus.STATUS_BROKEN; } break; case Status.STATUS_COMMITTING: case Status.STATUS_ROLLING_BACK: // We'll try up to 10 times before just releasing if (tryCount < 10) { break;// let it finish } // fall through and release case Status.STATUS_COMMITTED: case Status.STATUS_ROLLEDBACK: case Status.STATUS_NO_TRANSACTION: lockManager.unlock(node.getFqn(), gtx); status = TransactionLockStatus.STATUS_BROKEN; break; case Status.STATUS_PREPARED: // If the tx was started here, we can still abort the commit, // otherwise we are in the middle of a remote commit() call // and the status is just about to change if (tryCount == 0 && localTx) { // We can still abort the commit if (trace) { log.trace("Attempting to break transaction lock held " + "by " + gtx + " by marking local tx as " + "rollback-only"); } tx.setRollbackOnly(); break; } else if (tryCount < 10) { // EITHER tx was started elsewhere (in which case we'll // wait a bit to allow the commit() call to finish; // same as STATUS_COMMITTING above) // OR we marked the tx rollbackOnly above and are just // waiting a bit for the status to change break; } // fall through and release default: lockManager.unlock(node.getFqn(), gtx); status = TransactionLockStatus.STATUS_BROKEN; } } catch (Exception e) { log.error("Exception breaking locks held by " + gtx, e); lockManager.unlock(node.getFqn(), gtx); status = TransactionLockStatus.STATUS_BROKEN; } } else { // Race condition; globalTransaction was cleared from txTable. // Just double check if globalTransaction still holds a lock if (lockManager.ownsLock(node.getFqn(), gtx)) { // perhaps we should throw an exception? lockManager.unlock(node.getFqn(), gtx); status = TransactionLockStatus.STATUS_BROKEN; } } return status; } /** * Test if this node needs to be 'undeleted' * reverse the "remove" if the node has been previously removed in the same tx, if this operation is a put() */ public static void manageReverseRemove(InvocationContext ctx, NodeSPI childNode, boolean reverseRemoveCheck, List createdNodes, CommandsFactory commandsFactory) { if (ctx.getGlobalTransaction() != null) //if no tx then reverse remove does not make sense { Fqn fqn = childNode.getFqn(); TransactionEntry entry = ctx.getTransactionEntry(); boolean needToReverseRemove = reverseRemoveCheck && childNode.isDeleted() && entry != null && entry.getRemovedNodes().contains(fqn); if (!needToReverseRemove) return; childNode.markAsDeleted(false); //if we'll rollback the tx data should be added to the node again Map oldData = new HashMap(childNode.getDataDirect()); PutDataMapCommand command = commandsFactory.buildPutDataMapCommand(ctx.getGlobalTransaction(), fqn, oldData); // txTable.get(gtx).addUndoOperation(command); --- now need to make sure this is added to the normal mods list instead entry.addModification(command); //we're prepared for rollback, now reset the node childNode.clearDataDirect(); if (createdNodes != null) { createdNodes.add(childNode); } } } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/lock/LockStrategyReadCommitted.java0000644000175000017500000000227610536332011031252 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.lock; import java.util.concurrent.locks.Lock; /** * Transaction isolation level of READ_COMMITTED. Similar to read/write lock, but writers are not blocked on readers * It prevents dirty read only. *

        Dirty read allows (t1) write and then (t2) read within two separate threads, all without * transaction commit.

        * * @author Ben Wang * @version $Revision: 3125 $ */ public class LockStrategyReadCommitted implements LockStrategy { private NonBlockingWriterLock lock_; public LockStrategyReadCommitted() { lock_ = new NonBlockingWriterLock(); } /** * @see org.jboss.cache.lock.LockStrategy#readLock() */ public Lock readLock() { return lock_.readLock(); } /** * @see org.jboss.cache.lock.LockStrategy#upgradeLockAttempt(long) */ public Lock upgradeLockAttempt(long msecs) throws UpgradeException { return lock_.upgradeLockAttempt(msecs); } /** * @see org.jboss.cache.lock.LockStrategy#writeLock() */ public Lock writeLock() { return lock_.writeLock(); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/lock/ReadWriteLockWithUpgrade.java0000644000175000017500000003550511031017377031047 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.lock; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; /** *

        This class is similar to PreferredWriterReadWriteLock except that * the read lock is upgradable to write lock. I.e., when a user calls * upgradeLock(), it will release the read lock, wait for * all the read locks to clear and obtain a write lock afterwards. In * particular, the write lock is obtained with priority to prevent deadlock * situation. The current design is based in part from Doug Lea's * PreferredWriterReadWriteLock. *

        *

        Note that the pre-requisite to use upgrade lock is the pre-existing of a read * lock. Otherwise, a RuntimeException will be thrown.

        *

        Also note that currently lock can only be obtained through attempt * api with specified timeout instead acquire is not supported. *

        * Internally, the upgrade is done through a Semaphore where a thread with * a higher priority will obtain the write lock first. The following scenarios then can * happen: *

          *
        • If there are multiple read locks granted (and no write lock request in waiting), * an upgrade will release one read lock * (decrease the counter), bump up upagrade counter, increase the current thread priority, * set a thread local as upgrade thread, * and place a write lock acquire() call. Upon waken up, it will check if the current * thread is an upgrade. If it is, restore the thread priority, and decrease the * upgrade counter.
        • *
        • If there are mutiple write locks request in waiting (and only one read lock granted), * decrease the read lock counter, * bump up the upgrade counter, and increase the current thread priority. * When one of the writer gets wake up, it will first check * if upgrade counter is zero. If not, it will first release the semaphore so the upgrade * thread can grab it, check the semaphore is gone, do notify, and issue myself another * acquire to grab the nextInterceptor available semaphore.
        • *
        * * @author Ben Wang */ public class ReadWriteLockWithUpgrade implements ReadWriteLock { private long activeReaders_ = 0; protected Thread activeWriter_ = null; private long waitingReaders_ = 0; private long waitingWriters_ = 0; private long waitingUpgrader_ = 0; // Store a default object to signal that we are upgrade thread. //protected final ThreadLocal upgraderLocal_ = new ThreadLocal(); protected static final Map upgraderLocal_ = new ThreadLocalMap(); protected static final Object dummy_ = new Object(); protected final ReaderLock readerLock_ = new ReaderLock(); protected final WriterLock writerLock_ = new WriterLock(); protected static final Log log_ = LogFactory.getLog(ReadWriteLockWithUpgrade.class); @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("activeReaders=").append(activeReaders_).append(", activeWriter=").append(activeWriter_); sb.append(", waitingReaders=").append(waitingReaders_).append(", waitingWriters=").append(waitingWriters_); sb.append(", waitingUpgrader=").append(waitingUpgrader_); return sb.toString(); } public Lock writeLock() { return writerLock_; } public Lock readLock() { return readerLock_; } /** * Attempt to obtain an upgrade to writer lock. If successful, the read lock is upgraded to * write lock. If fails, the owner retains the read lock. * * @param msecs Time to wait in millisecons. * @return Sync object. Null if not successful or timeout. */ public Lock upgradeLockAttempt(long msecs) throws UpgradeException { if (activeReaders_ == 0) throw new RuntimeException("No reader lock available for upgrade"); synchronized (writerLock_) { if (waitingUpgrader_ >= 1) { String errStr = "upgradeLockAttempt(): more than one reader trying to simultaneously upgrade to write lock"; log_.error(errStr); throw new UpgradeException(errStr); } waitingUpgrader_++; upgraderLocal_.put(this, dummy_); } // If there is only one reader left, switch to write lock immediately. // If there is more than one reader, release this one and acquire the write // lock. There is still a chance for deadlock when there are two reader locks // and suddenly the second lock is released when this lock is released and acquired // as is else case. Solution is to let it timeout. if (activeReaders_ == 1) { resetWaitingUpgrader(); return changeLock(); } else { readerLock_.unlock(); try { if (!writerLock_.tryLock(msecs, TimeUnit.MILLISECONDS)) { log_.error("upgradeLock(): failed"); resetWaitingUpgrader(); if (!readerLock_.tryLock(msecs, TimeUnit.MILLISECONDS)) { String errStr = "ReadWriteLockWithUpgrade.upgradeLockAttempt():" + " failed to upgrade to write lock and also failed to re-obtain the read lock"; log_.error(errStr); throw new IllegalStateException(errStr); } return null; } resetWaitingUpgrader(); } catch (InterruptedException ex) { resetWaitingUpgrader(); return null; } return writerLock_; } } private void resetWaitingUpgrader() { synchronized (writerLock_) { waitingUpgrader_--; upgraderLocal_.remove(this); } } protected synchronized Lock changeLock() { --activeReaders_; if (!startWrite()) { // Something is wrong. return null; } return writerLock_; } /* A bunch of small synchronized methods are needed to allow communication from the Lock objects back to this object, that serves as controller */ protected synchronized void cancelledWaitingReader() { --waitingReaders_; } protected synchronized void cancelledWaitingWriter() { --waitingWriters_; } /** * Override this method to change to reader preference * */ protected boolean allowReader() { return activeWriter_ == null && waitingWriters_ == 0 && waitingUpgrader_ == 0; } protected synchronized boolean startRead() { boolean allowRead = allowReader(); if (allowRead) { ++activeReaders_; } return allowRead; } protected synchronized boolean startWrite() { // The allowWrite expression cannot be modified without // also changing startWrite, so is hard-wired boolean allowWrite = activeWriter_ == null && activeReaders_ == 0; if (allowWrite) activeWriter_ = Thread.currentThread(); return allowWrite; } /* Each of these variants is needed to maintain atomicity of wait counts during wait loops. They could be made faster by manually inlining each other. We hope that compilers do this for us though. */ protected synchronized boolean startReadFromNewReader() { boolean pass = startRead(); if (!pass) ++waitingReaders_; return pass; } protected synchronized boolean startWriteFromNewWriter() { boolean pass = startWrite(); if (!pass) ++waitingWriters_; return pass; } protected synchronized boolean startReadFromWaitingReader() { boolean pass = startRead(); if (pass) --waitingReaders_; return pass; } protected synchronized boolean startWriteFromWaitingWriter() { boolean pass = startWrite(); if (pass) --waitingWriters_; return pass; } /** * Called upon termination of a read. * Returns the object to signal to wake up a waiter, or null if no such */ protected synchronized Signaller endRead() { if (activeReaders_ != 0 && --activeReaders_ == 0 && waitingWriters_ > 0) return writerLock_; else return null; } /** * Called upon termination of a write. * Returns the object to signal to wake up a waiter, or null if no such */ protected synchronized Signaller endWrite() { activeWriter_ = null; if (waitingReaders_ > 0 && allowReader()) return readerLock_; else if (waitingWriters_ > 0) return writerLock_; else return null; } /** * Reader and Writer requests are maintained in two different * wait sets, by two different objects. These objects do not * know whether the wait sets need notification since they * don't know preference rules. So, each supports a * method that can be selected by main controlling object * to perform the notifications. This base class simplifies mechanics. */ static interface Signaller { // base for ReaderLock and WriterLock void signalWaiters(); } static abstract class LockBase implements Lock { public void lock() { throw new UnsupportedOperationException(); } public void lockInterruptibly() throws InterruptedException { throw new UnsupportedOperationException(); } public Condition newCondition() { throw new UnsupportedOperationException(); } public boolean tryLock() { throw new UnsupportedOperationException(); } /* public boolean tryLock(long time, TimeUnit unit) throws InterruptedException { throw new UnsupportedOperationException(); } public void unlock() { throw new UnsupportedOperationException(); } */ } protected class ReaderLock extends LockBase implements Signaller { public void unlock() { Signaller s = endRead(); if (s != null) { s.signalWaiters(); } } public synchronized void signalWaiters() { ReaderLock.this.notifyAll(); } public boolean tryLock(long time, TimeUnit unit) throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); long msecs = unit.toMillis(time); InterruptedException ie = null; synchronized (this) { if (msecs <= 0) return startRead(); else if (startReadFromNewReader()) return true; else { long waitTime = msecs; long start = System.currentTimeMillis(); while (true) { try { ReaderLock.this.wait(waitTime); } catch (InterruptedException ex) { cancelledWaitingReader(); ie = ex; break; } if (startReadFromWaitingReader()) return true; else { waitTime = msecs - (System.currentTimeMillis() - start); if (waitTime <= 0) { cancelledWaitingReader(); break; } } } } } // safeguard on interrupt or timeout: writerLock_.signalWaiters(); if (ie != null) throw ie; else return false; // timed out } } protected class WriterLock extends LockBase implements Signaller { public void unlock() { Signaller s = endWrite(); if (s != null) s.signalWaiters(); } // Waking up all thread in waiting now for them to compete. // Thread with higher priority, i.e., upgrading, will win. public synchronized void signalWaiters() { WriterLock.this.notifyAll(); } public boolean tryLock(long time, TimeUnit unit) throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); InterruptedException ie = null; long msecs = unit.toMillis(time); synchronized (WriterLock.this) { if (msecs <= 0) { // Upgrade thread has prioirty. if (waitingUpgrader_ != 0) { if (upgraderLocal_.get(ReadWriteLockWithUpgrade.this) != null) { log_.info("attempt(): upgrade to write lock"); return startWrite(); } else return false; } else return startWrite(); } else if (startWriteFromNewWriter()) return true; else { long waitTime = msecs; long start = System.currentTimeMillis(); while (true) { try { WriterLock.this.wait(waitTime); } catch (InterruptedException ex) { cancelledWaitingWriter(); WriterLock.this.notifyAll(); ie = ex; break; } if (waitingUpgrader_ != 0) { // Has upgrade request if (upgraderLocal_.get(ReadWriteLockWithUpgrade.this) != null) { // Upgrade thread if (startWriteFromWaitingWriter()) return true; } else { // Normal write thread, go back to wait. continue; } } else { // Every one is normal write thread. Compete; if fail go back to wait. if (startWriteFromWaitingWriter()) return true; } waitTime = msecs - (System.currentTimeMillis() - start); if (waitTime <= 0) { cancelledWaitingWriter(); WriterLock.this.notifyAll(); break; } } } } readerLock_.signalWaiters(); if (ie != null) throw ie; else return false; // timed out } } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/lock/LockStrategyRepeatableRead.java0000644000175000017500000000264711004125045031371 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.lock; import java.util.concurrent.locks.Lock; /** * Transaction isolation level of Repeatable_Read. It prevents dirty read and non-repeatable read. *

        Dirty read allows (t1) write and then (t2) read within two separate threads, all without * transaction commit.

        *

        Non-repeatable allows read allows (t1) read, (t2) write, and then (t1) read, all without * transaction commit.

        * * @author Ben Wang * @version $Revision: 5671 $ */ public class LockStrategyRepeatableRead implements LockStrategy { private ReadWriteLockWithUpgrade lock_; // Delegate is ReadWriteLockWithUpgrade public LockStrategyRepeatableRead() { lock_ = new ReadWriteLockWithUpgrade(); } /** * @see org.jboss.cache.lock.LockStrategy#readLock() */ public Lock readLock() { return lock_.readLock(); } /** * @see org.jboss.cache.lock.LockStrategy#upgradeLockAttempt(long) */ public Lock upgradeLockAttempt(long msecs) throws UpgradeException { return lock_.upgradeLockAttempt(msecs); } /** * @see org.jboss.cache.lock.LockStrategy#writeLock() */ public Lock writeLock() { return lock_.writeLock(); } @Override public String toString() { return lock_ != null ? lock_.toString() : "(null)"; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/lock/IsolationLevel.java0000755000175000017500000000073210611075652027135 0ustar twernertwerner/** * * @author Bela Ban Nov 25, 2003 * @version $Id: IsolationLevel.java 3756 2007-04-17 07:47:54Z msurtani $ */ package org.jboss.cache.lock; /** * Various transaction isolation levels as an enumerated class. * * @see Isolation levels */ public enum IsolationLevel { /** * No isolation. */ NONE, SERIALIZABLE, REPEATABLE_READ, READ_COMMITTED, READ_UNCOMMITTED }jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/lock/LockStrategySerializable.java0000644000175000017500000000303010536332011031124 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.lock; import java.util.concurrent.locks.Lock; //import org.jboss.logging.Logger; /** * Lock strategy of Serializable that prevents dirty read, non-repeatable read, and * phantom read. *

        Dirty read allows (t1) write and then (t2) read within two separate threads, all without * transaction commit.

        *

        Non-repeatable read allows (t1) read, (t2) write, and then (t1) read, all without * transaction commit.

        *

        Phantom read allows (t1) read n rows, (t2) insert k rows, and (t1) read n+k rows.

        * * @author Ben Wang July 15, 2003 * @version $Revision: 3125 $ */ public class LockStrategySerializable implements LockStrategy { // Log log=LogFactory.getLog(getClass()); private SemaphoreLock sem_; public LockStrategySerializable() { sem_ = new SemaphoreLock(1); } /** * @see org.jboss.cache.lock.LockStrategy#readLock() */ public Lock readLock() { return sem_; } /** * @see org.jboss.cache.lock.LockStrategy#upgradeLockAttempt(long) */ public Lock upgradeLockAttempt(long msecs) throws UpgradeException { // If we come to this far, that means the thread owns a rl already // so we just return the same lock return sem_; } /** * @see org.jboss.cache.lock.LockStrategy#writeLock() */ public Lock writeLock() { return sem_; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/lock/PessimisticNodeBasedLockManager.java0000644000175000017500000002712211031017377032345 0ustar twernertwernerpackage org.jboss.cache.lock; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.NodeSPI; import org.jboss.cache.factories.CommandsFactory; import org.jboss.cache.factories.annotations.Inject; import static org.jboss.cache.lock.LockType.WRITE; import org.jboss.cache.transaction.GlobalTransaction; import org.jboss.cache.transaction.TransactionTable; import java.util.List; /** * Contains specific methods for the PessimisticLockInterceptor. * * @author Manik Surtani (manik@jboss.org) * @deprecated will be removed with pessimistic locking */ @Deprecated @SuppressWarnings("deprecation") public class PessimisticNodeBasedLockManager extends NodeBasedLockManager { private static final Log log = LogFactory.getLog(PessimisticNodeBasedLockManager.class); private static final boolean trace = log.isTraceEnabled(); private CommandsFactory commandsFactory; @Inject private void injectCommandsFactory(CommandsFactory commandsFactory) { this.commandsFactory = commandsFactory; } /** * A specific lock method for the PessimisticLockInterceptor. It should *not* be used anywhere else as it has very * peculiar and specific characteristics. *

        * For implementations of this LockManager interface that are not intended for use with the PessimisticLockInterceptor, * it is okay not to implement this method (a no-op). *

        * * @param fqn Fqn to lock * @param lockType Type of lock to acquire * @param ctx invocation context * @param createIfNotExists if true, nodes will be created if they do not exist. * @param zeroLockTimeout if true uses 0 as a lock acquisition timeout * @param acquireWriteLockOnParent if true, write locks are acquired on parent nodes when child nodes need write locks. * @param reverseRemoveCheck if true, nodes that have been marked as removed in the current transaction may be reversed. * @param createdNodes a list to which nodes created in this method may be added. * @param skipNotification if true, node creation notifications are suppressed. * @return true if successful; false otherwise. * @throws InterruptedException if interrupted */ public boolean lockPessimistically(InvocationContext ctx, Fqn fqn, LockType lockType, boolean createIfNotExists, boolean zeroLockTimeout, boolean acquireWriteLockOnParent, boolean reverseRemoveCheck, List createdNodes, boolean skipNotification) throws InterruptedException { if (fqn == null || configuration.getIsolationLevel() == IsolationLevel.NONE || ctx.isLockingSuppressed()) return false; boolean created; long timeout = zeroLockTimeout ? 0 : ctx.getContextLockAcquisitionTimeout(lockAcquisitionTimeout); // make sure we can bail out of this loop long cutoffTime = System.currentTimeMillis() + timeout; boolean firstTry = true; do { // this is an additional check to make sure we don't try for too long. if (!firstTry && System.currentTimeMillis() > cutoffTime) { throw new TimeoutException("Unable to acquire lock on Fqn " + fqn + " after " + timeout + " millis"); } created = lock(ctx, fqn, lockType, createIfNotExists, timeout, acquireWriteLockOnParent, reverseRemoveCheck, createdNodes, skipNotification); firstTry = false; } while (createIfNotExists && (dataContainer.peek(fqn, false, false) == null));// keep trying until we have the lock (fixes concurrent remove()) return created; } /** * Acquires locks on the node and on its parrents. Read locks are acquired for exsiting ancestors, with two exceptions: * 1) createIfNotExists is true. If an ancestor is created on the fly, then an WL is acquired by default * 2) acquireWriteLockOnParent is true. If so AND {@link org.jboss.cache.Node#isLockForChildInsertRemove()} then a read * lock will be aquired for the parent of the node. * * @param createIfNotExists if true, then missing nodes will be cretaed on the fly. If false, method returns if we * reach a node that does not exists * @param reverseRemoveCheck if true, will reverse removes if needed. * @param createdNodes a list to which any nodes created can register their Fqns so that calling code is aware of which nodes have been newly created. * @param skipNotification */ private boolean lock(InvocationContext ctx, Fqn fqn, LockType lockType, boolean createIfNotExists, long timeout, boolean acquireWriteLockOnParent, boolean reverseRemoveCheck, List createdNodes, boolean skipNotification) throws TimeoutException, LockingException, InterruptedException { Thread currentThread = Thread.currentThread(); GlobalTransaction gtx = ctx.getGlobalTransaction(); boolean created = false; // if the tx associated with the current thread is rolling back, barf! JBCACHE-923 if (gtx != null) TransactionTable.assertTransactionValid(ctx); Object owner = (gtx != null) ? gtx : currentThread; NodeSPI currentNode; if (trace) log.trace("Attempting to lock node " + fqn + " for owner " + owner); long expiryTime = System.currentTimeMillis() + timeout; currentNode = rootNode; NodeSPI parent = null; Object childName = null; int currentIndex = -1; int targetFqnSize = fqn.size(); do { if (currentNode == null) { if (createIfNotExists) { // if the new node is to be marked as deleted, do not notify! currentNode = parent.addChildDirect(childName, !skipNotification); created = true; if (trace) log.trace("Child node was null, so created child node " + childName); if (createdNodes != null) createdNodes.add(currentNode); } else { if (trace) log.trace("failed to find or create child " + childName + " of node " + parent); return false; } } else { if (!currentNode.isValid() && createIfNotExists) currentNode.setValid(true, false); } LockType lockTypeRequired = LockType.READ; if (created || writeLockNeeded(ctx, lockType, currentIndex, acquireWriteLockOnParent, createIfNotExists, fqn, currentNode)) { lockTypeRequired = WRITE; } Fqn currentNodeFqn = currentNode.getFqn(); // actually acquire the lock we need. This method blocks. acquireNodeLock(ctx, currentNode, owner, gtx, lockTypeRequired, timeout); LockUtil.manageReverseRemove(ctx, currentNode, reverseRemoveCheck, createdNodes, commandsFactory); // make sure the lock we acquired isn't on a deleted node/is an orphan!! // look into invalidated nodes as well NodeSPI repeek = dataContainer.peek(currentNodeFqn, true, true); if (currentNode != repeek) { if (trace) log.trace("Was waiting for and obtained a lock on a node that doesn't exist anymore! Attempting lock acquisition again."); // we have an orphan!! Lose the unnecessary lock and re-acquire the lock (and potentially recreate the node). // check if the parent exists!! // look into invalidated nodes as well currentNode.getLock().releaseAll(owner); if (parent == null || dataContainer.peek(parent.getFqn(), true, true) == null) { // crap! if (trace) log.trace("Parent has been deleted again. Go through the lock method all over again."); currentNode = rootNode; currentIndex = -1; parent = null; } else { currentNode = parent; currentIndex--; parent = null; if (System.currentTimeMillis() > expiryTime) { throw new TimeoutException("Unable to acquire lock on child node " + Fqn.fromRelativeElements(currentNode.getFqn(), childName) + " after " + timeout + " millis."); } if (trace) log.trace("Moving one level up, current node is :" + currentNode); } } else { // we have succeeded in acquiring this lock. Increment the current index since we have gained one level of depth in the tree. currentIndex++; // now test if this is the final level and if we can quit the loop: //if (currentNodeFqn.equals(fqn))//we've just processed the last child if (currentIndex == targetFqnSize) { break; } if (!fqn.isChildOrEquals(currentNode.getFqn())) // Does this ever happen? Perhaps with a move(), I suppose? - MS { String message = new StringBuilder("currentNode instance changed the FQN(").append(currentNode.getFqn()) .append(") and do not match the FQN on which we want to acquire lock(").append(fqn).append(")").toString(); log.trace(message); throw new LockingException(message); } parent = currentNode; childName = fqn.get(currentIndex); currentNode = currentNode.getChildDirect(childName); } } while (true); return created; } /** * Used by lock() * Determins whter an arbitrary node from the supplied fqn needs an write lock. */ private boolean writeLockNeeded(InvocationContext ctx, LockType lockType, int currentNodeIndex, boolean acquireWriteLockOnParent, boolean createIfNotExists, Fqn targetFqn, NodeSPI currentNode) { int treeNodeSize = targetFqn.size(); // write lock forced!! boolean isTargetNode = currentNodeIndex == (treeNodeSize - 1); if (isTargetNode && ctx.getOptionOverrides().isForceWriteLock()) return true; //this can be injected, from the caller as a param named wlParent if (currentNode.isLockForChildInsertRemove()) { if (acquireWriteLockOnParent && currentNodeIndex == treeNodeSize - 2) { return true;// we're doing a remove and we've reached the PARENT node of the target to be removed. } if (!isTargetNode && dataContainer.peek(targetFqn.getAncestor(currentNodeIndex + 2), false, false) == null) { return createIfNotExists;// we're at a node in the tree, not yet at the target node, and we need to create the next node. So we need a WL here. } } return lockType == WRITE && isTargetNode;//write lock explicitly requested and this is the target to be written to. } private void acquireNodeLock(InvocationContext ctx, NodeSPI node, Object owner, GlobalTransaction gtx, LockType lockType, long lockTimeout) throws LockingException, TimeoutException, InterruptedException { NodeLock lock = node.getLock(); boolean acquired = lock.acquire(owner, lockTimeout, lockType); if (acquired) { // Record the lock for release on method return or tx commit/rollback if (gtx != null) { ctx.getTransactionEntry().addLock(lock); } else { ctx.addInvocationLockAcquired(lock); } } } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/lock/SemaphoreLock.java0000755000175000017500000000222110537345073026737 0ustar twernertwernerpackage org.jboss.cache.lock; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; /** * Implements most of the methods of Lock using the {@link Semaphore} implementation. */ public class SemaphoreLock extends Semaphore implements Lock { /** * It's unclear why this class would be serialized. */ private static final long serialVersionUID = -15293253129957476L; public SemaphoreLock(int permits) { super(permits); } public void lock() { try { acquire(); } catch (InterruptedException e) { lock(); // recursive, I know .. } } public void lockInterruptibly() throws InterruptedException { acquire(); } public Condition newCondition() { throw new UnsupportedOperationException(); } public boolean tryLock() { return tryAcquire(); } public boolean tryLock(long arg0, TimeUnit arg1) throws InterruptedException { return tryAcquire(arg0, arg1); } public void unlock() { release(); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/lock/StripedLock.java0000644000175000017500000001217711067143565026437 0ustar twernertwerner/* * JBoss, Home of Professional Open Source. * Copyright 2000 - 2008, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.cache.lock; import net.jcip.annotations.ThreadSafe; import org.jboss.cache.Fqn; import java.util.List; import java.util.concurrent.locks.ReentrantReadWriteLock; /** * A simple implementation of lock striping, using Fqns as the keys to lock on, primarily used to help make * {@link org.jboss.cache.loader.CacheLoader} implemtations thread safe. *

        * Backed by a set of {@link java.util.concurrent.locks.ReentrantReadWriteLock} instances, and using the {@link org.jboss.cache.Fqn} * hashcodes to determine buckets. *

        * Since buckets are used, it doesn't matter that the Fqn in question is not removed from the lock map when no longer in * use, since the Fqn is not referenced in this class. Rather, the hash code is used. *

        * * @author Manik Surtani * @since 2.0.0 */ @ThreadSafe public class StripedLock { private static final int DEFAULT_CONCURRENCY = 20; private final int lockSegmentMask; private final int lockSegmentShift; final ReentrantReadWriteLock[] sharedLocks; /** * This constructor just calls {@link #StripedLock(int)} with a default concurrency value of 20. */ public StripedLock() { this(DEFAULT_CONCURRENCY); } /** * Creates a new StripedLock which uses a certain number of shared locks across all elements that need to be locked. * * @param concurrency number of threads expected to use this class concurrently. */ public StripedLock(int concurrency) { int tempLockSegShift = 0; int numLocks = 1; while (numLocks < concurrency) { ++tempLockSegShift; numLocks <<= 1; } lockSegmentShift = 32 - tempLockSegShift; lockSegmentMask = numLocks - 1; sharedLocks = new ReentrantReadWriteLock[numLocks]; for (int i = 0; i < numLocks; i++) sharedLocks[i] = new ReentrantReadWriteLock(); } /** * Blocks until a lock is acquired. * * @param fqn the Fqn to lock on * @param exclusive if true, a write (exclusive) lock is attempted, otherwise a read (shared) lock is used. */ public void acquireLock(Fqn fqn, boolean exclusive) { ReentrantReadWriteLock lock = getLock(fqn); if (exclusive) { lock.writeLock().lock(); } else { lock.readLock().lock(); } } /** * Releases a lock the caller may be holding. This method is idempotent. * * @param fqn the Fqn to release */ public void releaseLock(Fqn fqn) { ReentrantReadWriteLock lock = getLock(fqn); if (lock.isWriteLockedByCurrentThread()) { lock.writeLock().unlock(); } else { lock.readLock().unlock(); } } final ReentrantReadWriteLock getLock(Object o) { return sharedLocks[hashToIndex(o)]; } final int hashToIndex(Object o) { return (hash(o) >>> lockSegmentShift) & lockSegmentMask; } /** * Returns a hash code for non-null Object x. * Uses the same hash code spreader as most other java.util hash tables, except that this uses the string representation * of the object passed in. * * @param x the object serving as a key * @return the hash code */ final int hash(Object x) { int h = x.toString().hashCode(); h += ~(h << 9); h ^= (h >>> 14); h += (h << 4); h ^= (h >>> 10); return h; } /** * Releases locks on all fqns passed in. Makes multiple calls to {@link #releaseLock(org.jboss.cache.Fqn)}. This method is idempotent. * * @param fqns list of fqns * @see #releaseLock(org.jboss.cache.Fqn) */ public void releaseAllLocks(List fqns) { for (Fqn f : fqns) releaseLock(f); } /** * Acquires locks on all fqns passed in. Makes multiple calls to {@link #acquireLock(org.jboss.cache.Fqn,boolean)} * * @param fqns list of fqns * @param exclusive whether locks are exclusive. * @see #acquireLock(org.jboss.cache.Fqn,boolean) */ public void acquireAllLocks(List fqns, boolean exclusive) { for (Fqn f : fqns) acquireLock(f, exclusive); } }jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/lock/OwnerNotExistedException.java0000755000175000017500000000142010537345073031163 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.lock; /** * @author Ben */ public class OwnerNotExistedException extends Exception { private static final long serialVersionUID = 2837393571654438480L; /** * */ public OwnerNotExistedException() { super(); } /** * @param message */ public OwnerNotExistedException(String message) { super(message); } /** * @param message * @param cause */ public OwnerNotExistedException(String message, Throwable cause) { super(message, cause); } /** * @param cause */ public OwnerNotExistedException(Throwable cause) { super(cause); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/lock/LockManager.java0000644000175000017500000003136611017532335026370 0ustar twernertwernerpackage org.jboss.cache.lock; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.NodeSPI; import java.util.Collection; /** * An interface to deal with all aspects of acquiring and releasing locks for nodes in the cache. * * @author Manik Surtani (manik@jboss.org) * @since 2.2.0 */ public interface LockManager { /** * Determines the owner to be used when obtaining locks, given an invocation context. This is typically a {@link org.jboss.cache.transaction.GlobalTransaction} if one * is present in the context, or {@link Thread#currentThread()} if one is not present. * * @param ctx invocation context * @return owner to be used for acquiring locks. */ Object getLockOwner(InvocationContext ctx); /** * Acquires a lock of type lockType, for a given owner, on a specific Node in the cache, denoted by fqn. This * method will try for {@link org.jboss.cache.config.Configuration#getLockAcquisitionTimeout()} milliseconds and give up if it is unable to acquire the required lock. * * @param fqn Fqn to lock * @param lockType type of lock to acquire * @param owner owner to acquire the lock for * @return true if the lock was acquired, false otherwise. */ boolean lock(Fqn fqn, LockType lockType, Object owner); /** * Acquires a lock of type lockType, for a given owner, on a specific Node in the cache, denoted by fqn. This * method will try for timeout milliseconds and give up if it is unable to acquire the required lock. * * @param fqn Fqn to lock * @param lockType type of lock to acquire * @param owner owner to acquire the lock for * @param timeout maximum length of time to wait for (in millis) * @return true if the lock was acquired, false otherwise. */ boolean lock(Fqn fqn, LockType lockType, Object owner, long timeout); /** * As {@link #lock(org.jboss.cache.Fqn, LockType, Object)} except that a NodeSPI is passed in instead of an Fqn. * * @param node node to lock * @param lockType type of lock to acquire * @param owner owner to acquire the lock for * @return true if the lock was acquired, false otherwise. */ boolean lock(NodeSPI node, LockType lockType, Object owner); /** * As {@link #lock(org.jboss.cache.Fqn, LockType, Object, long)} except that a NodeSPI is passed in instead of an Fqn. * * @param node node to lock * @param lockType type of lock to acquire * @param owner owner to acquire the lock for * @param timeout maximum length of time to wait for (in millis) * @return true if the lock was acquired, false otherwise. */ boolean lock(NodeSPI node, LockType lockType, Object owner, long timeout); /** * Acquires a lock of type lockType, on a specific Node in the cache, denoted by fqn. This * method will try for a period of time and give up if it is unable to acquire the required lock. The period of time * is specified in {@link org.jboss.cache.config.Option#getLockAcquisitionTimeout()} and, if this is unset, the default timeout * set in {@link org.jboss.cache.config.Configuration#getLockAcquisitionTimeout()} is used. *

        * In addition, any locks acquired are added to the context using {@link org.jboss.cache.InvocationContext#addInvocationLockAcquired(NodeLock)} * if you are not running in a transaction, or using {@link org.jboss.cache.transaction.TransactionEntry#addLock(NodeLock)} if you are. *

        * The owner for the lock is determined by passing the invocation context to {@link #getLockOwner(org.jboss.cache.InvocationContext)}. *

        * * @param fqn Fqn to lock * @param lockType type of lock to acquire * @param ctx invocation context associated with this invocation * @return true if the lock was acquired, false otherwise. */ boolean lockAndRecord(Fqn fqn, LockType lockType, InvocationContext ctx); /** * Acquires a lock of type lockType, on a specific Node in the cache, denoted by fqn. This * method will try for a period of time and give up if it is unable to acquire the required lock. The period of time * is specified in {@link org.jboss.cache.config.Option#getLockAcquisitionTimeout()} and, if this is unset, the default timeout * set in {@link org.jboss.cache.config.Configuration#getLockAcquisitionTimeout()} is used. *

        * In addition, any locks acquired are added to the context using {@link org.jboss.cache.InvocationContext#addInvocationLockAcquired(NodeLock)} * if you are not running in a transaction, or using {@link org.jboss.cache.transaction.TransactionEntry#addLock(NodeLock)} if you are. *

        * The owner for the lock is determined by passing the invocation context to {@link #getLockOwner(org.jboss.cache.InvocationContext)}. *

        * * @param node Fqn to lock * @param lockType type of lock to acquire * @param ctx invocation context associated with this invocation * @return true if the lock was acquired, false otherwise. */ boolean lockAndRecord(NodeSPI node, LockType lockType, InvocationContext ctx); /** * Releases the lock passed in, held by the specified owner * * @param fqn Fqn of the node to unlock * @param owner lock owner */ void unlock(Fqn fqn, Object owner); /** * Releases the lock passed in, held by the specified owner * * @param node Node to unlock * @param owner lock owner */ void unlock(NodeSPI node, Object owner); /** * Releases locks present in an invocation context and transaction entry, if one is available. *

        * Locks are released in reverse order of which they are acquired and registered. *

        * Lock owner is determined by passing the invocation context to {@link #getLockOwner(org.jboss.cache.InvocationContext)} *

        * * @param ctx invocation context to inspect */ void unlock(InvocationContext ctx); /** * Locks the node and all child nodes, acquiring lock of type specified for the owner specified. If only some locks are * acquired, all locks are released and the method returns false. *

        * This method will try for {@link org.jboss.cache.config.Configuration#getLockAcquisitionTimeout()} milliseconds and give up if it is unable to acquire the required lock. *

        * * @param node Node to lock * @param lockType type of lock to acquire * @param owner owner to acquire the lock for * @return true if the lock was acquired, false otherwise. */ boolean lockAll(NodeSPI node, LockType lockType, Object owner); /** * Locks the node and all child nodes, acquiring lock of type specified for the owner specified. If only some locks are * acquired, all locks are released and the method returns false. Internal Fqns are included as well. * * @param node Node to lock * @param lockType type of lock to acquire * @param owner owner to acquire the lock for * @param timeout maximum length of time to wait for (in millis) * @return true if all locks were acquired, false otherwise. */ boolean lockAll(NodeSPI node, LockType lockType, Object owner, long timeout); /** * Locks the node and all child nodes, acquiring lock of type specified for the owner specified. If only some locks are * acquired, all locks are released and the method returns false. * * @param node Node to lock * @param lockType type of lock to acquire * @param owner owner to acquire the lock for * @param timeout maximum length of time to wait for (in millis) * @param excludeInternalFqns if true, any Fqns that are internal are excluded. * @return true if all locks were acquired, false otherwise. */ boolean lockAll(NodeSPI node, LockType lockType, Object owner, long timeout, boolean excludeInternalFqns); /** * Locks the node and all child nodes, acquiring lock of type specified for the owner specified. If only some locks are * acquired, all locks are released and the method returns false. *

        * In addition, any locks acquired are added to the context using {@link org.jboss.cache.InvocationContext#addInvocationLockAcquired(NodeLock)} * if you are not running in a transaction, or using {@link org.jboss.cache.transaction.TransactionEntry#addLock(NodeLock)} if you are. *

        * The owner for the lock is determined by passing the invocation context to {@link #getLockOwner(org.jboss.cache.InvocationContext)}. *

        * * @param node Node to lock * @param lockType type of lock to acquire * @param ctx invocation context associated with this invocation * @return true if all locks were acquired, false otherwise. */ boolean lockAllAndRecord(NodeSPI node, LockType lockType, InvocationContext ctx); /** * Locks the node and all child nodes, acquiring lock of type specified for the owner specified. If only some locks are * acquired, all locks are released and the method returns false. *

        * In addition, any locks acquired are added to the context using {@link org.jboss.cache.InvocationContext#addInvocationLockAcquired(NodeLock)} * if you are not running in a transaction, or using {@link org.jboss.cache.transaction.TransactionEntry#addLock(NodeLock)} if you are. *

        * The owner for the lock is determined by passing the invocation context to {@link #getLockOwner(org.jboss.cache.InvocationContext)}. *

        * * @param fqn Node to lock * @param lockType type of lock to acquire * @param ctx invocation context associated with this invocation * @return true if all locks were acquired, false otherwise. */ boolean lockAllAndRecord(Fqn fqn, LockType lockType, InvocationContext ctx); /** * Releases locks on a given node and all children for a given owner. * * @param node node to unlock * @param owner lock owner */ void unlockAll(NodeSPI node, Object owner); /** * Releases locks on a given node and all children for all owners. Use with care. * * @param node node to unlock */ void unlockAll(NodeSPI node); /** * Tests whether a given owner owns a lock of lockType on a particular Fqn. * * @param fqn fqn to test * @param lockType type of lock to test for * @param owner owner * @return true if the owner does own the specified lock type on the specified node, false otherwise. */ boolean ownsLock(Fqn fqn, LockType lockType, Object owner); /** * Tests whether a given owner owns any sort of lock on a particular Fqn. * * @param fqn fqn to test * @param owner owner * @return true if the owner does own the specified lock type on the specified node, false otherwise. */ boolean ownsLock(Fqn fqn, Object owner); /** * Tests whether a given owner owns any sort of lock on a particular Fqn. * * @param node to test * @param owner owner * @return true if the owner does own the specified lock type on the specified node, false otherwise. */ boolean ownsLock(NodeSPI node, Object owner); /** * Returns true if the node is locked (either for reading or writing) by anyone, and false otherwise. * * @param n node to inspect * @return true of locked; false if not. */ boolean isLocked(NodeSPI n); /** * Returns true if the node is locked (either for reading or writing) by anyone, and false otherwise. * * @param n node to inspect * @param lockType lockType to test for * @return true of locked; false if not. */ boolean isLocked(NodeSPI n, LockType lockType); /** * Retrieves the write lock owner, if any, for the current Fqn. * * @param f Fqn to inspect * @return the owner of the lock, or null if not locked. */ Object getWriteOwner(Fqn f); /** * Retrieves the read lock owners, if any, for the current Fqn. * * @param f Fqn to inspect * @return a collection of read lock owners, or an empty collection if not locked. */ Collection getReadOwners(Fqn f); /** * Retrieves the write lock owner, if any, for the current Fqn. * * @param node the node to inspect * @return the owner of the lock, or null if not locked. */ Object getWriteOwner(NodeSPI node); /** * Retrieves the read lock owners, if any, for the current Fqn. * * @param node the node to inspect * @return a collection of read lock owners, or an empty collection if not locked. */ Collection getReadOwners(NodeSPI node); /** * Prints lock information about a node (and it's children) to a String. * * @param node node to inspect */ String printLockInfo(NodeSPI node); } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/lock/NullLock.java0000755000175000017500000000207510536332011025720 0ustar twernertwernerpackage org.jboss.cache.lock; import java.util.Date; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; class NullLock implements Lock, Condition { public void lock() { } public void lockInterruptibly() throws InterruptedException { } public Condition newCondition() { return this; } public boolean tryLock() { return true; } public boolean tryLock(long arg0, TimeUnit arg1) throws InterruptedException { return true; } public void unlock() { } public void await() throws InterruptedException { } public boolean await(long arg0, TimeUnit arg1) throws InterruptedException { return true; } public long awaitNanos(long arg0) throws InterruptedException { return arg0; } public void awaitUninterruptibly() { } public boolean awaitUntil(Date arg0) throws InterruptedException { return true; } public void signal() { } public void signalAll() { } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/lock/UpgradeException.java0000644000175000017500000000133610537345073027454 0ustar twernertwerner// $Id: UpgradeException.java 3152 2006-12-11 21:14:35Z genman $ /* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. * Created on Jan 18, 2003 */ package org.jboss.cache.lock; /** * Used when a read-lock cannot be upgraded to a write-lock * * @author Bela Ban * @version $Revision: 3152 $ */ public class UpgradeException extends LockingException { private static final long serialVersionUID = -5683212651728276028L; /** * Constructor for UpgradeException. * * @param msg */ public UpgradeException(String msg) { super(msg); } public UpgradeException(String msg, Throwable cause) { super(msg, cause); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/lock/LockStrategyFactory.java0000755000175000017500000000353211011035040030126 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.lock; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.annotations.Inject; import org.jboss.cache.factories.annotations.NonVolatile; import org.jboss.cache.factories.annotations.Start; /** * Factory to create LockStragtegy instance. * * @author Ben Wang */ @NonVolatile public class LockStrategyFactory { /** * Transaction locking isolation level. Default. */ private IsolationLevel lockingLevel = IsolationLevel.REPEATABLE_READ; private Configuration configuration; @Inject public void injectDependencies(Configuration configuration) { this.configuration = configuration; } @Start(priority = 1) public void start() { lockingLevel = configuration.isNodeLockingOptimistic() ? IsolationLevel.REPEATABLE_READ : configuration.getIsolationLevel(); } public LockStrategy getLockStrategy() { return getLockStrategy(lockingLevel); } public LockStrategy getLockStrategy(IsolationLevel lockingLevel) { //if(log_.isTraceEnabled()) { // log_.trace("LockStrategy is: " + lockingLevel); //} if (lockingLevel == null) return new LockStrategyNone(); switch (lockingLevel) { case NONE: return new LockStrategyNone(); case SERIALIZABLE: return new LockStrategySerializable(); case READ_UNCOMMITTED: return new LockStrategyReadUncommitted(); case READ_COMMITTED: return new LockStrategyReadCommitted(); case REPEATABLE_READ: default: return new LockStrategyRepeatableRead(); } } public void setIsolationLevel(IsolationLevel level) { lockingLevel = level; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/lock/NodeLock.java0000755000175000017500000000702311031017377025677 0ustar twernertwernerpackage org.jboss.cache.lock; import org.jboss.cache.Fqn; import java.util.Collection; import java.util.Set; /** * Interface for a lock for nodes. */ public interface NodeLock { Fqn getFqn(); /** * Returns a copy of the reader lock owner in List. * Size is zero is not available. Note that this list * is synchronized. * * @return Collection of readers */ Collection getReaderOwners(); /** * Returns the writer lock owner object. Null if not available. * * @return Object owner */ Object getWriterOwner(); /** * Acquires a write lock with a timeout of timeout milliseconds. * Note that if the current owner owns a read lock, it will be upgraded * automatically. However, if upgrade fails, i.e., timeout, the read lock will * be released automatically. * * @param caller Can't be null. * @param timeout * @return boolean True if lock was acquired and was not held before, false if lock was held * @throws LockingException * @throws TimeoutException */ boolean acquireWriteLock(Object caller, long timeout) throws LockingException, TimeoutException, InterruptedException; /** * Acquires a read lock with a timeout period of timeout milliseconds. * * @param caller Can't be null. * @param timeout * @return boolean True if lock was acquired and was not held before, false if lock was held * @throws LockingException * @throws TimeoutException */ boolean acquireReadLock(Object caller, long timeout) throws LockingException, TimeoutException, InterruptedException; /** * Releases the lock held by the owner. * * @param caller Can't be null. */ void release(Object caller); /** * Releases all locks associated with this instance. */ void releaseAll(); /** * Releases all locks with this owner. */ void releaseAll(Object owner); /** * Check if there is a read lock. */ boolean isReadLocked(); /** * Check if there is a write lock. */ boolean isWriteLocked(); /** * Check if there is a read or write lock */ boolean isLocked(); /** * Returns true if the object is the lock owner. */ boolean isOwner(Object o); boolean acquire(Object caller, long timeout, LockType lock_type) throws LockingException, TimeoutException, InterruptedException; /** * Recursively acquire locks for this node and all subnodes, including internal Fqns such as buddy backup subtrees. * * @param caller lock owner * @param timeout time to wait * @param lock_type type of lock * @return locks acquired */ Set acquireAll(Object caller, long timeout, LockType lock_type) throws LockingException, TimeoutException, InterruptedException; /** * Same as the overloaded {@link #acquire(Object, long, LockType)} except that you can * optionally specify that internal Fqns - such as buddy backup subtrees - can be excluded when acquiring locks. * * @param caller lock owner * @param timeout time to wait * @param lock_type type of lock * @param excludeInternalFqns if true, locks on internal fqns are not acquired. * @return locks acquired */ Set acquireAll(Object caller, long timeout, LockType lock_type, boolean excludeInternalFqns) throws LockingException, TimeoutException, InterruptedException; void printLockInfo(StringBuilder sb, int indent); }jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/lock/LockStrategyNone.java0000644000175000017500000000162110536332011027421 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.lock; import java.util.concurrent.locks.Lock; /** * Transaction isolation level of None. * * @author Lari Hotari * @version $Revision: 3125 $ */ public class LockStrategyNone implements LockStrategy { private Lock nullLock_; public LockStrategyNone() { nullLock_ = new NullLock(); } /** * @see org.jboss.cache.lock.LockStrategy#readLock() */ public Lock readLock() { return nullLock_; } /** * @see org.jboss.cache.lock.LockStrategy#upgradeLockAttempt(long) */ public Lock upgradeLockAttempt(long msecs) throws UpgradeException { return nullLock_; } /** * @see org.jboss.cache.lock.LockStrategy#writeLock() */ public Lock writeLock() { return nullLock_; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/lock/LockStrategyReadUncommitted.java0000644000175000017500000000276410536332011031617 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.lock; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; /** * Transaction isolation level of READ-UNCOMMITTED. Reads always succeed (NullLock), whereas writes are exclusive. * It prevents none of the dirty read, non-repeatable read, or phantom read. * * @author Ben Wang * @version $Revision: 3125 $ */ public class LockStrategyReadUncommitted implements LockStrategy { private SemaphoreLock wLock_; private Lock rLock_; // Null lock will always return true public LockStrategyReadUncommitted() { wLock_ = new SemaphoreLock(1); rLock_ = new NullLock(); } /** * @see org.jboss.cache.lock.LockStrategy#readLock() */ public Lock readLock() { return rLock_; } /** * @see org.jboss.cache.lock.LockStrategy#upgradeLockAttempt(long) */ public Lock upgradeLockAttempt(long msecs) throws UpgradeException { // Since write is exclusive, we need to obtain the write lock first // before we can return the upgrade try { wLock_.tryLock(msecs, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { throw new UpgradeException("Upgrade failed in " + msecs + " msecs", e); } return wLock_; } /** * @see org.jboss.cache.lock.LockStrategy#writeLock() */ public Lock writeLock() { return wLock_; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/lock/SimpleReadWriteLock.java0000755000175000017500000000062710537345073030064 0ustar twernertwernerpackage org.jboss.cache.lock; import java.util.concurrent.locks.ReentrantReadWriteLock; /** * @author Bela Ban * @version $Id: SimpleReadWriteLock.java 3152 2006-12-11 21:14:35Z genman $ */ public class SimpleReadWriteLock extends ReentrantReadWriteLock { /** * It's unclear why this class should be serializable. */ private static final long serialVersionUID = 6317491956891614462L; } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/lock/IdentityLock.java0000755000175000017500000004216511031017377026611 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.lock; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.Fqn; import org.jboss.cache.Node; import org.jboss.cache.NodeSPI; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; /** * Lock object which grants and releases locks, and associates locks with * owners. The methods to acquire and release a lock require an owner * (Object). When a lock is acquired, we store the current owner with the lock. * When the same owner (but possibly running in a different thread) * wants to acquire the lock, it is immediately granted. When an owner * different from the one currently owning the lock wants to release the lock, * we do nothing (no-op). *

        * Consider the following example: *

         * FIFOSemaphore lock=new FIFOSemaphore(1);
         * lock.acquire();
         * lock.acquire();
         * lock.release();
         * 
        * This would block on the second acquire() (although we currently already hold * the lock) because the lock only has 1 permit. So IdentityLock will allow the * following code to work properly: *
         * IdentityLock lock=new IdentityLock();
         * lock.readLock().acquire(this, timeout);
         * lock.writeLock().acquire(this, timeout);
         * lock.release(this);
         * 
        * If the owner is null, then the current thread is taken by default. This allow the following * code to work: *
         * IdentityLock lock=new IdentityLock();
         * lock.readLock().attempt(null, timeout);
         * lock.writeLock().attempt(null, timeout);
         * lock.release(null);
         * 
        *
        * Note that the Object given as owner is required to implement {@link Object#equals}. For * a use case see the examples in the testsuite. * * @author Bela Ban Apr 11, 2003 * @author Ben Wang July 2003 * @version $Revision: 6073 $ */ @SuppressWarnings("deprecation") public class IdentityLock implements NodeLock { /** * Initialized property for debugging "print_lock_details" */ private boolean PRINT_LOCK_DETAILS = Boolean.getBoolean("print_lock_details"); private static final Log log = LogFactory.getLog(IdentityLock.class); private static final boolean trace = log.isTraceEnabled(); private final LockStrategy lock; private final LockMap map; private final boolean mustReacquireRead; private NodeSPI node; /** * Creates a new IdentityLock using the LockFactory passed in. * * @param factory to create lock strategies * @param node to lock */ public IdentityLock(LockStrategyFactory factory, NodeSPI node) { lock = factory.getLockStrategy(); mustReacquireRead = lock instanceof LockStrategyReadCommitted; map = new LockMap(); this.node = node; } /** * Returns the node for this lock, may be null. */ public Node getNode() { return node; } /** * Returns the FQN this lock, may be null. */ public Fqn getFqn() { if (node == null) { return null; } return node.getFqn(); } /** * Return a copy of the reader lock owner in List. Size is zero is not available. Note that this list * is synchronized. * * @return Collection of readers */ public Collection getReaderOwners() { return map.readerOwners(); } /** * Return the writer lock owner object. Null if not available. * * @return Object owner */ public Object getWriterOwner() { return map.writerOwner(); } public LockMap getLockMap() { return map; } /** * Acquire a write lock with a timeout of timeout milliseconds. * Note that if the current owner owns a read lock, it will be upgraded * automatically. However, if upgrade fails, i.e., timeout, the read lock will * be released automatically. * * @param caller Can't be null. * @param timeout * @return boolean True if lock was acquired and was not held before, false if lock was held * @throws LockingException * @throws TimeoutException */ public boolean acquireWriteLock(Object caller, long timeout) throws LockingException, TimeoutException, InterruptedException { if (trace) { log.trace(new StringBuilder("acquiring WL: fqn=").append(getFqn()).append(", caller=").append(caller). append(", lock=").append(toString(PRINT_LOCK_DETAILS))); } boolean flag = acquireWriteLock0(caller, timeout); if (trace) { log.trace(new StringBuilder("acquired WL: fqn=").append(getFqn()).append(", caller=").append(caller). append(", lock=").append(toString(PRINT_LOCK_DETAILS))); } return flag; } private boolean acquireWriteLock0(Object caller, long timeout) throws LockingException, TimeoutException, InterruptedException { if (caller == null) { throw new IllegalArgumentException("acquireWriteLock(): null caller"); } if (map.isOwner(caller, LockMap.OWNER_WRITE)) { if (trace) { log.trace("acquireWriteLock(): caller already owns lock for " + getFqn() + " (caller=" + caller + ')'); } return false;// owner already has the lock } // Check first if we need to upgrade if (map.isOwner(caller, LockMap.OWNER_READ)) { // Currently is a reader owner. Obtain the writer ownership then. Lock wLock; try { if (trace) { log.trace("upgrading RL to WL for " + caller + ", timeout=" + timeout + ", locks: " + map.printInfo()); } wLock = lock.upgradeLockAttempt(timeout); } catch (UpgradeException ue) { String errStr = "acquireWriteLock(): lock upgrade failed for " + getFqn() + " (caller=" + caller + ", lock info: " + toString(true) + ')'; log.trace(errStr, ue); throw new UpgradeException(errStr, ue); } if (wLock == null) { release(caller);// bug fix: remember to release the read lock before throwing the exception map.removeReader(caller); String errStr = "upgrade lock for " + getFqn() + " could not be acquired after " + timeout + " ms." + " Lock map ownership " + map.printInfo() + " (caller=" + caller + ", lock info: " + toString(true) + ')'; log.trace(errStr); throw new UpgradeException(errStr); } try { if (trace) { log.trace("upgrading lock for " + getFqn()); } map.upgrade(caller); } catch (OwnerNotExistedException ex) { throw new UpgradeException("Can't upgrade lock to WL for " + getFqn() + ", error in LockMap.upgrade()", ex); } } else { // Not a current reader owner. Obtain the writer ownership then. boolean rc = lock.writeLock().tryLock(timeout, TimeUnit.MILLISECONDS); // we don't need to synchronize from here on because we own the semaphore if (!rc) { String errStr = "write lock for " + getFqn() + " could not be acquired after " + timeout + " ms. " + "Locks: " + map.printInfo() + " (caller=" + caller + ", lock info: " + toString(true) + ')'; log.trace(errStr); throw new TimeoutException(errStr); } map.setWriterIfNotNull(caller); } return true; } /** * Acquire a read lock with a timeout period of timeout milliseconds. * * @param caller Can't be null. * @param timeout * @return boolean True if lock was acquired and was not held before, false if lock was held * @throws LockingException * @throws TimeoutException */ public boolean acquireReadLock(Object caller, long timeout) throws LockingException, TimeoutException, InterruptedException { if (trace) { log.trace(new StringBuilder("acquiring RL: fqn=").append(getFqn()).append(", caller=").append(caller). append(", lock=").append(toString(PRINT_LOCK_DETAILS))); } boolean flag = acquireReadLock0(caller, timeout); if (trace) { log.trace(new StringBuilder("acquired RL: fqn=").append(getFqn()).append(", caller=").append(caller). append(", lock=").append(toString(PRINT_LOCK_DETAILS))); } return flag; } private boolean acquireReadLock0(Object caller, long timeout) throws LockingException, TimeoutException, InterruptedException { boolean rc; if (caller == null) { throw new IllegalArgumentException("owner is null"); } boolean hasRead = false; boolean hasRequired = false; if (mustReacquireRead) { hasRequired = map.isOwner(caller, LockMap.OWNER_WRITE); if (!hasRequired) { hasRead = map.isOwner(caller, LockMap.OWNER_READ); } } else if (map.isOwner(caller, LockMap.OWNER_ANY)) { hasRequired = true; } if (hasRequired) { if (trace) { StringBuilder sb = new StringBuilder(64); sb.append("acquireReadLock(): caller ").append(caller).append(" already owns lock for ").append(getFqn()); log.trace(sb.toString()); } return false;// owner already has the lock } rc = lock.readLock().tryLock(timeout, TimeUnit.MILLISECONDS); // we don't need to synchronize from here on because we own the semaphore if (!rc) { StringBuilder sb = new StringBuilder(); sb.append("read lock for ").append(getFqn()).append(" could not be acquired by ").append(caller); sb.append(" after ").append(timeout).append(" ms. " + "Locks: ").append(map.printInfo()); sb.append(", lock info: ").append(toString(true)); String errMsg = sb.toString(); log.trace(errMsg); throw new TimeoutException(errMsg); } // Only add to the map if we didn't already have the lock if (!hasRead) { map.addReader(caller);// this is synchronized internally, we don't need to synchronize here } return true; } /** * Release the lock held by the owner. * * @param caller Can't be null. */ public void release(Object caller) { if (caller == null) { throw new IllegalArgumentException("IdentityLock.release(): null owner object."); } // Check whether to release reader or writer lock. if (map.isOwner(caller, LockMap.OWNER_READ)) { map.removeReader(caller); lock.readLock().unlock(); } else if (map.isOwner(caller, LockMap.OWNER_WRITE)) { map.removeWriter(); lock.writeLock().unlock(); } } /** * Release all locks associated with this instance. */ public void releaseAll() { try { if ((map.writerOwner()) != null) { // lock_.readLock().release(); lock.writeLock().unlock(); } map.releaseReaderOwners(lock); } finally { map.removeAll(); } } /** * Same as releaseAll now. */ public void releaseForce() { releaseAll(); } /** * Check if there is a read lock. */ public boolean isReadLocked() { return map.isReadLocked(); } /** * Check if there is a write lock. */ public boolean isWriteLocked() { return map.writerOwner() != null; } /** * Check if there is a read or write lock */ public boolean isLocked() { return isReadLocked() || isWriteLocked(); } /** * Am I a lock owner? * * @param o */ public boolean isOwner(Object o) { return map.isOwner(o, LockMap.OWNER_ANY); } @Override public String toString() { return toString(false); } public String toString(boolean print_lock_details) { StringBuilder sb = new StringBuilder(); toString(sb, print_lock_details); return sb.toString(); } public void toString(StringBuilder sb) { toString(sb, false); } public void toString(StringBuilder sb, boolean print_lock_details) { boolean printed_read_owners = false; Collection read_owners = lock != null ? getReaderOwners() : null; if (read_owners != null && read_owners.size() > 0) { // Fix for JBCACHE-310 -- can't just call new ArrayList(read_owners) :( // Creating the ArrayList and calling addAll doesn't work either // Looking at the details of how this is implemented vs. the 2 prev // options, this doesn't look like it should be slower Iterator iter = read_owners.iterator(); read_owners = new ArrayList(read_owners.size()); while (iter.hasNext()) { read_owners.add(iter.next()); } sb.append("read owners=").append(read_owners); printed_read_owners = true; } Object write_owner = lock != null ? getWriterOwner() : null; if (write_owner != null) { if (printed_read_owners) { sb.append(", "); } sb.append("write owner=").append(write_owner); } if (read_owners == null && write_owner == null) { sb.append(""); } if (print_lock_details) { sb.append(" (").append(lock.toString()).append(')'); } } public boolean acquire(Object caller, long timeout, LockType lock_type) throws LockingException, TimeoutException, InterruptedException { try { if (lock_type == LockType.NONE) { return true; } else if (lock_type == LockType.READ) { return acquireReadLock(caller, timeout); } else { return acquireWriteLock(caller, timeout); } } catch (UpgradeException e) { StringBuilder buf = new StringBuilder("failure upgrading lock: fqn=").append(getFqn()).append(", caller=").append(caller). append(", lock=").append(toString(true)); if (trace) { log.trace(buf.toString()); } throw new UpgradeException(buf.toString(), e); } catch (LockingException e) { StringBuilder buf = new StringBuilder("failure acquiring lock: fqn=").append(getFqn()).append(", caller=").append(caller). append(", lock=").append(toString(true)); if (trace) { log.trace(buf.toString()); } throw new LockingException(buf.toString(), e); } catch (TimeoutException e) { StringBuilder buf = new StringBuilder("failure acquiring lock: fqn=").append(getFqn()).append(", caller=").append(caller). append(", lock=").append(toString(true)); if (trace) { log.trace(buf.toString()); } throw new TimeoutException(buf.toString(), e); } } public Set acquireAll(Object caller, long timeout, LockType lock_type) throws LockingException, TimeoutException, InterruptedException { return acquireAll(caller, timeout, lock_type, false); } public Set acquireAll(Object caller, long timeout, LockType lock_type, boolean excludeInternalFqns) throws LockingException, TimeoutException, InterruptedException { boolean acquired; if (lock_type == LockType.NONE || (excludeInternalFqns && node.getCache().getInternalFqns().contains(getFqn()))) { return Collections.emptySet(); } Set retval = new HashSet(); acquired = acquire(caller, timeout, lock_type); if (acquired) { retval.add(this); } for (NodeSPI n : node.getChildrenDirect()) { retval.addAll(n.getLock().acquireAll(caller, timeout, lock_type, excludeInternalFqns)); } return retval; } public void releaseAll(Object owner) { for (NodeSPI n : node.getChildrenDirect()) { n.getLock().releaseAll(owner); } release(owner); } private void printIndent(StringBuilder sb, int indent) { if (sb != null) { for (int i = 0; i < indent; i++) { sb.append(" "); } } } public void printLockInfo(StringBuilder sb, int indent) { boolean locked = isLocked(); printIndent(sb, indent); sb.append(Fqn.SEPARATOR).append(node.getFqn().getLastElement()); if (locked) { sb.append("\t("); toString(sb); sb.append(")"); } for (NodeSPI n : node.getChildrenDirect()) { sb.append("\n"); n.getLock().printLockInfo(sb, indent + 4); } } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/lock/LockType.java0000644000175000017500000000035611015575710025734 0ustar twernertwernerpackage org.jboss.cache.lock; /** * An enumeration to define different types of locks. * * @author Manik Surtani (manik@jboss.org) * @since 2.2.0 */ public enum LockType { NONE, READ, WRITE } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/lock/SimpleLock.java0000755000175000017500000000210011004116445026226 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.lock; import java.util.concurrent.locks.Lock; /** * Simple lock that does not differentiate read and write lock. All locks are obtained FIFO. * This class is used as a delegate for LockStrategy * is transaction isolation level. * * @author Ben Wang July 15, 2003 * @version $Revision: 5665 $ */ public class SimpleLock { // Log log=LogFactory.getLog(getClass()); private final SemaphoreLock sem_; public SimpleLock() { sem_ = new SemaphoreLock(1); } /** * @see org.jboss.cache.lock.LockStrategy#readLock() */ public Lock readLock() { return sem_; } /** * @see org.jboss.cache.lock.LockStrategy#upgradeLockAttempt(long) */ public Lock upgradeLockAttempt(long msecs) { return sem_; } /** * @see org.jboss.cache.lock.LockStrategy#writeLock() */ public Lock writeLock() { return sem_; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/lock/DeadlockException.java0000644000175000017500000000143610537345073027574 0ustar twernertwerner// $Id: DeadlockException.java 3152 2006-12-11 21:14:35Z genman $ /* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. * Created on Jan 18, 2003 */ package org.jboss.cache.lock; /** * Used when a lock acquisition would cause a deadlock. This will only be used * once deadlock detection is in place. * * @author Bela Ban * @version $Revision: 3152 $ */ public class DeadlockException extends LockingException { private static final long serialVersionUID = 8651993450626741020L; /** * Constructor for DeadlockException. * * @param msg */ public DeadlockException(String msg) { super(msg); } public DeadlockException(String msg, Throwable cause) { super(msg, cause); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/lock/LockMap.java0000755000175000017500000001140211033367610025523 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.lock; import org.jboss.cache.util.concurrent.ConcurrentHashSet; import java.util.Collection; /** * Provide lock ownership mapping. * * @author Ben Wang * @version $Id: LockMap.java 6173 2008-07-04 09:38:16Z manik.surtani@jboss.com $ */ public class LockMap { public static final int OWNER_ANY = 0; public static final int OWNER_READ = 1; public static final int OWNER_WRITE = 2; private Object writeOwner_ = null; // This is more efficient (lower CPU utilisation and better concurrency) than a CopyOnWriteArraySet or ConcurrentHashSet. // for some reason this barfs with concurrent mod exceptions. Need to see why. private final Collection readOwners; // private final Set readOwners = new ConcurrentHashSet(); public LockMap() { this(new ConcurrentHashSet(4)); } /** * This constructor is made available for testing with different collection types for the readOwners collection. * * @param readOwners */ public LockMap(Collection readOwners) { this.readOwners = readOwners; } /** * Check whether this owner has reader or writer ownership. * * @param caller the potential owner. Cannot be null. * @param ownership Either OWNER_ANY, OWNER_READ, * or OWNER_WRITE. * @return * @throws NullPointerException if caller is null. */ public boolean isOwner(Object caller, int ownership) { /* This method doesn't need to be synchronized; the thread is doing a simple read access (writer, readers) and only the current thread can *change* the writer or readers, so this cannot happen while we read. */ switch (ownership) { case OWNER_ANY: return ((writeOwner_ != null && caller.equals(writeOwner_)) || readOwners.contains(caller)); case OWNER_READ: return (readOwners.contains(caller)); case OWNER_WRITE: return (writeOwner_ != null && caller.equals(writeOwner_)); default: return false; } } /** * Adding a reader owner. * * @param owner */ public void addReader(Object owner) { readOwners.add(owner); } /** * Adding a writer owner. * * @param owner */ public void setWriterIfNotNull(Object owner) { synchronized (this) { if (writeOwner_ != null) throw new IllegalStateException("there is already a writer holding the lock: " + writeOwner_); writeOwner_ = owner; } } private Object setWriter(Object owner) { Object old; synchronized (this) { old = writeOwner_; writeOwner_ = owner; } return old; } /** * Upgrading current reader ownership to writer one. * * @param owner * @return True if successful. */ public boolean upgrade(Object owner) throws OwnerNotExistedException { boolean old_value = readOwners.remove(owner); if (!old_value) // didn't exist in the list throw new OwnerNotExistedException("Can't upgrade lock. Read lock owner did not exist"); setWriter(owner); return true; } /** * Returns an unmodifiable set of reader owner objects. */ public Collection readerOwners() { // not necessary if the collection is a CHS. Saves on overhead. // return Collections.unmodifiableCollection(readOwners); return readOwners; } public void releaseReaderOwners(LockStrategy lock) { int size = readOwners.size(); for (int i = 0; i < size; i++) lock.readLock().unlock(); } /** * @return Writer owner object. Null if none. */ public Object writerOwner() { return writeOwner_; } /** * Remove reader ownership. */ public void removeReader(Object owner) { readOwners.remove(owner); } /** * Remove writer ownership. */ public void removeWriter() { synchronized (this) { writeOwner_ = null; } } /** * Remove all ownership. */ public void removeAll() { removeWriter(); readOwners.clear(); } /** * Debugging information. * * @return */ public String printInfo() { StringBuilder buf = new StringBuilder(64); buf.append("Read lock owners: ").append(readOwners).append('\n'); buf.append("Write lock owner: ").append(writeOwner_).append('\n'); return buf.toString(); } public boolean isReadLocked() { return !readOwners.isEmpty(); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/lock/LockingException.java0000644000175000017500000000312411004125045027431 0ustar twernertwerner// $Id: LockingException.java 5671 2008-04-24 15:41:25Z manik.surtani@jboss.com $ /* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.lock; import org.jboss.cache.CacheException; import java.util.Map; /** * Used for all locking-related exceptions, e.g. when a lock could not be * acquired within the timeout, or when a deadlock was detected or an upgrade failed. * * @author Bela Ban. */ public class LockingException extends CacheException { private static final long serialVersionUID = 5551396474793902133L; /** * A list of all nodes that failed to acquire a lock */ Map failed_lockers = null; public LockingException() { super(); } public LockingException(Map failed_lockers) { super(); this.failed_lockers = failed_lockers; } public LockingException(String msg) { super(msg); } public LockingException(String msg, Map failed_lockers) { super(msg); this.failed_lockers = failed_lockers; } public LockingException(String msg, Throwable cause) { super(msg, cause); } public LockingException(String msg, Throwable cause, Map failed_lockers) { super(msg, cause); this.failed_lockers = failed_lockers; } @Override public String toString() { String retval = super.toString(); if (failed_lockers != null && failed_lockers.size() > 0) retval = retval + ", failed lockers: " + failed_lockers; return retval; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/lock/NonBlockingWriterLock.java0000644000175000017500000000237311004125045030402 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.lock; /** * NonBlockingWriterLock is a read/write lock (with upgrade) that has * non-blocking write lock acquisition on existing read lock(s). *

        Note that the write lock is exclusive among write locks, e.g., * only one write lock can be granted at one time, but the write lock * is independent of the read locks. For example, * a read lock to be acquired will be blocked if there is existing write lock, but * will not be blocked if there are mutiple read locks already granted to other * owners. On the other hand, a write lock can be acquired as long as there * is no existing write lock, regardless how many read locks have been * granted. * * @author Ben Wang * @version $Id: NonBlockingWriterLock.java 5671 2008-04-24 15:41:25Z manik.surtani@jboss.com $ */ public class NonBlockingWriterLock extends ReadWriteLockWithUpgrade { // Only need to overwrite this method so WL is not blocked on RL. @Override protected synchronized boolean startWrite() { boolean allowWrite = (activeWriter_ == null); if (allowWrite) activeWriter_ = Thread.currentThread(); return allowWrite; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/xml/0000755000175000017500000000000011376174021023203 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/xml/XmlHelper.java0000644000175000017500000003510511032145203025737 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.xml; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.util.StringPropertyReplacer; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.w3c.dom.Text; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.util.Properties; /** * A simple XML utility class for reading configuration elements * * @author Manik Surtani (manik@jboss.org) */ public class XmlHelper { private static final Log log = LogFactory.getLog(XmlHelper.class); /** * The root of a JBoss Cache configuration XML file. This is the <mbean> tag. */ public static final String ROOT = "mbean"; /** * The <attribute> tag which forms the bulk of JBoss Cache configuration elements */ public static final String ATTR = "attribute"; /** * The <config> tag may be embedded in the contents of an <attribute>, to specify more * complex configuration for certain parameters. */ public static final String CONFIG_ATTR = "config"; /** * The <name> attribute to an <attribute> tag. */ public static final String NAME = "name"; /** * Returns the contents of a specific node of given element name, provided a certain attribute exists and is set to value. * E.g., if you have a {@link Element} which represents the following XML snippet: *

            *   <ItemQuantity Colour="Red">100</ItemQuantity>
            *   <ItemQuantity Colour="Blue">30</ItemQuantity>
            *   <ItemQuantity Colour="Black">10</ItemQuantity>
            * 
            * 

        * The following results could be expected: *

        *
            *    getTagContents(element, "Red", "ItemQuantity", "Colour"); // 100
            *    getTagContents(element, "Black", "ItemQuantity", "Colour"); // 10
            *    getTagContents(element, "Blah", "ItemQuantity", "Colour"); // null
            *    getTagContents(element, "Red", "Blah", "Colour"); // null
            *    getTagContents(element, "Black", "ItemQuantity", "Blah"); // null
            * 
        *

        * None of the parameters should be null - otherwise the method may throw a NullPointerException. *

        * * @param elem - element to search through. * @param value - expected value to match against * @param elementName - element name * @param attributeName - attribute name of the element that would contain the expected value. * @return the contents of the matched element, or null if not found/matched */ public static String getTagContents(Element elem, String value, String elementName, String attributeName) { NodeList list = elem.getElementsByTagName(elementName); for (int s = 0; s < list.getLength(); s++) { org.w3c.dom.Node node = list.item(s); if (node.getNodeType() != org.w3c.dom.Node.ELEMENT_NODE) continue; Element element = (Element) node; String name = element.getAttribute(attributeName); if (name.equals(value)) { return getElementContent(element, true); } } return null; } /** * Retrieves the value of a given attribute for the first encountered instance of a tag in an element. *

        * E.g., if you have a {@link Element} which represents the following XML snippet: *

        *
            *   <ItemQuantity Colour="Red">100</ItemQuantity>
            *   <ItemQuantity Colour="Blue">30</ItemQuantity>
            *   <ItemQuantity Colour="Black">10</ItemQuantity>
            * 
            * 

        * The following results could be expected: *

        *
            *    getAttributeValue(element, "ItemQuantity", "Colour"); // "Red"
            *    getTagContents(element, "Blah", "Colour"); // null
            *    getTagContents(element, "ItemQuantity", "Blah"); // null
            * 
        * None of the parameters should be null - otherwise the method may throw a NullPointerException. * * @param elem - element to search through. * @param elementName - element name * @param attributeName - attribute name of the element that would contain the expected value. * @return the contents of the matched attribute, or null if not found/matched */ public static String getAttributeValue(Element elem, String elementName, String attributeName) { NodeList list = elem.getElementsByTagName(elementName); for (int s = 0; s < list.getLength(); s++) { org.w3c.dom.Node node = list.item(s); if (node.getNodeType() != org.w3c.dom.Node.ELEMENT_NODE) continue; Element element = (Element) node; String value = element.getAttribute(attributeName); return value == null ? null : StringPropertyReplacer.replaceProperties(value); } return null; } /** * Convenience method, equivalent to calling getSubElement(element, "config"); */ public static Element getConfigSubElement(Element element) { return getSubElement(element, CONFIG_ATTR); } /** * Returns a named sub-element of the current element passed in. *

        * None of the parameters should be null - otherwise the method may throw a NullPointerException. * * @param element - element to search through. * @param subElementName - the name of a sub element to look for * @return the first matching sub element, if found, or null otherwise. */ public static Element getSubElement(Element element, String subElementName) { NodeList nl = element.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if (node.getNodeType() == Node.ELEMENT_NODE && subElementName.equals(((Element) node).getTagName())) { return (Element) node; } } if (log.isDebugEnabled()) log.debug("getSubElement(): Does not exist for " + subElementName); return null; } /** * Reads the contents of the element passed in. *

        * None of the parameters should be null - otherwise the method may throw a NullPointerException. * * @param element - element to search through. * @param trim - if true, whitespace is trimmed before returning * @return the contents of the element passed in. Will return an empty String if the element is empty. */ public static String getElementContent(Element element, boolean trim) { NodeList nl = element.getChildNodes(); String attributeText = ""; for (int i = 0; i < nl.getLength(); i++) { Node n = nl.item(i); if (n instanceof Text) { attributeText += StringPropertyReplacer.replaceProperties(((Text) n).getData()); } } // end of for () if (trim) attributeText = attributeText.trim(); return attributeText; } /** * Reads the contents of the first occurence of elementName under the given element, trimming results of whitespace. *

        * None of the parameters should be null - otherwise the method may throw a NullPointerException. * * @param element - element to search through. * @param elementName - name of the element to find within the element passed in * @return may return an empty String of not found. */ public static String readStringContents(Element element, String elementName) { NodeList nodes = element.getElementsByTagName(elementName); if (nodes.getLength() > 0) { Node node = nodes.item(0); Element ne = (Element) node; NodeList nl2 = ne.getChildNodes(); Node node2 = nl2.item(0); if (node2 != null) { String value = node2.getNodeValue(); if (value == null) return ""; return StringPropertyReplacer.replaceProperties(value.trim()); } else { return ""; } } else { return ""; } } /** * Escapes backslashes ('\') with additional backslashes in a given String, returning a new, escaped String. * * @param value String to escape. Cannot be null. * @return escaped String. Never is null. */ public static String escapeBackslashes(String value) { StringBuilder buf = new StringBuilder(value); for (int looper = 0; looper < buf.length(); looper++) { char curr = buf.charAt(looper); char next = 0; if (looper + 1 < buf.length()) next = buf.charAt(looper + 1); if (curr == '\\') { if (next != '\\') { // only if not already escaped buf.insert(looper, '\\'); // escape backslash } looper++; // skip past extra backslash (either the one we added or existing) } } return buf.toString(); } /** * Reads the contents of a named sub element within a given element, and attempts to parse the contents as a Java * properties file. *

        * E.g., if you have a {@link Element} which represents the following XML snippet: *

        *

            *   <props>
            *       my.attrib.1 = blah
            *       my.attrib.2 = blahblah
            *   </props>
            * 
            * 

        * The following results could be expected: *

        *

            *    Properties p = readPropertiesContents(element, "props");
            *    p.getProperty("my.attrib.1"); // blah
            *    p.getProperty("my.attrib.2"); // blahblah
            * 
        * None of the parameters should be null - otherwise the method may throw a NullPointerException. * * @param element - element to search through. * @param elementName - name of the element to find within the element passed in * @return a {@link Properties} object, never null. * @throws IOException if unable to parse the contents of the element */ public static Properties readPropertiesContents(Element element, String elementName) throws IOException { String stringContents = readStringContents(element, elementName); if (stringContents == null) return new Properties(); // JBCACHE-531: escape all backslash characters stringContents = escapeBackslashes(stringContents); ByteArrayInputStream is = new ByteArrayInputStream(stringContents.trim().getBytes("ISO8859_1")); Properties properties = new Properties(); properties.load(is); is.close(); return properties; } /** * Similar to {@link #readStringContents(org.w3c.dom.Element,String)} except that it returns a boolean. * * @param element - element to search through. * @param elementName - name of the element to find within the element passed in * @return the contents of the element as a boolean, or false if not found. */ public static boolean readBooleanContents(Element element, String elementName) { return readBooleanContents(element, elementName, false); } /** * Similar to {@link #readStringContents(org.w3c.dom.Element,String)} except that it returns a boolean. * * @param element - element to search through. * @param elementName - name of the element to find within the element passed in * @param defaultValue - value to return if the element is not found or cannot be parsed. * @return the contents of the element as a boolean */ public static boolean readBooleanContents(Element element, String elementName, boolean defaultValue) { String val = readStringContents(element, elementName); if (val.equalsIgnoreCase("true") || val.equalsIgnoreCase("false")) { // needs to be done this way because of JBBUILD-351 return Boolean.valueOf(val); //return Boolean.parseBoolean(val); } return defaultValue; } /** * Converts a String representing an XML snippet into an {@link org.w3c.dom.Element}. * * @param xml snippet as a string * @return a DOM Element * @throws Exception if unable to parse the String or if it doesn't contain valid XML. */ public static Element stringToElement(String xml) throws Exception { ByteArrayInputStream bais = new ByteArrayInputStream(xml.getBytes("utf8")); DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); Document d = builder.parse(bais); bais.close(); return d.getDocumentElement(); } /** * Returns the root element of a given input stream * * @param is stream to parse * @return XML DOM element, or null if unable to parse stream */ public static Element getDocumentRoot(InputStream is) { Document doc; try { InputSource xmlInp = new InputSource(is); DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder parser = docBuilderFactory.newDocumentBuilder(); doc = parser.parse(xmlInp); Element root = doc.getDocumentElement(); root.normalize(); return root; } catch (SAXParseException err) { log.error("Configurator SAXParse error", err); } catch (SAXException e) { log.error("Configurator SAX error", e); } catch (Exception pce) { log.error("Configurator general error", pce); } return null; } /** * Retrieves the boolean value of a given attribute for the first encountered instance of elementName * * @param elem - element to search * @param elementName - name of element to find * @param attributeName - name of attribute to retrieve the value of * @param defaultValue - default value to return if not found */ public static boolean readBooleanAttribute(Element elem, String elementName, String attributeName, boolean defaultValue) { String val = getAttributeValue(elem, elementName, attributeName); if (val != null && (val.equalsIgnoreCase("true") || val.equalsIgnoreCase("false"))) { //return Boolean.parseBoolean(val); // needs to be done this way because of JBBUILD-351 return Boolean.valueOf(val); } return defaultValue; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/InvocationContext.java0000644000175000017500000003023311017060615026720 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.commands.VisitableCommand; import org.jboss.cache.config.Option; import org.jboss.cache.lock.NodeLock; import org.jboss.cache.marshall.MethodCall; import org.jboss.cache.transaction.GlobalTransaction; import org.jboss.cache.transaction.TransactionEntry; import org.jboss.cache.transaction.TransactionTable; import javax.transaction.Transaction; import java.util.ArrayList; import java.util.Collection; import java.util.List; /** * This context holds information specific to a method invocation. * * @author Manik Surtani (manik@jboss.org) */ @SuppressWarnings("deprecation") public class InvocationContext implements Cloneable { private static final Log log = LogFactory.getLog(InvocationContext.class); private static final boolean trace = log.isTraceEnabled(); private Transaction transaction; private GlobalTransaction globalTransaction; private TransactionEntry transactionEntry; private Option optionOverrides; // defaults to true. private boolean originLocal = true; private boolean txHasMods; private boolean localRollbackOnly; @Deprecated private MethodCall methodCall; @Deprecated private VisitableCommand command; List invocationLocks; // used to store cache peeks within the scope of a single context. Performing a cache peek can be a huge bottle neck. // See JBCACHE-811 // backing out for now //private Map peekedNodes = new HashMap(); /** * Retrieves a node that may have previously been "peeked" within the scope of the same invocation. * * @param f fqn of node to find * @return node, if previously peeked, or null if not. * @since 2.1.0 */ // public NodeSPI getPeekedNode(Fqn f) // { // return peekedNodes.get(f); // } /** * Adds a node to the previously peeked list. * * @param n node to add * @param f fqn of node * @since 2.1.0 */ // public void savePeekedNode(NodeSPI n, Fqn f) // { // peekedNodes.put(f, n); // } /** * Wipe list of previously peeked nodes. * * @since 2.1.0 */ // public void wipePeekedNodes() // { // peekedNodes.clear(); // } public void setLocalRollbackOnly(boolean localRollbackOnly) { this.localRollbackOnly = localRollbackOnly; } /** * Retrieves the transaction associated with this invocation * * @return The transaction associated with this invocation */ public Transaction getTransaction() { return transaction; } /** * Sets the transaction associated with this invocation * * @param transaction */ public void setTransaction(Transaction transaction) { this.transaction = transaction; } /** * @return the transaction entry associated with the current transaction, or null if the current thread is not associated with a transaction. * @since 2.2.0 */ public TransactionEntry getTransactionEntry() { return transactionEntry; } /** * Sets the transaction entry to be associated with the current thread. * * @param transactionEntry transaction entry to set * @since 2.2.0 */ public void setTransactionEntry(TransactionEntry transactionEntry) { this.transactionEntry = transactionEntry; } /** * Retrieves the global transaction associated with this invocation * * @return the global transaction associated with this invocation */ public GlobalTransaction getGlobalTransaction() { return globalTransaction; } /** * Sets the global transaction associated with this invocation * * @param globalTransaction */ public void setGlobalTransaction(GlobalTransaction globalTransaction) { this.globalTransaction = globalTransaction; } /** * Retrieves the option overrides associated with this invocation * * @return the option overrides associated with this invocation */ public Option getOptionOverrides() { if (optionOverrides == null) { optionOverrides = new Option(); } return optionOverrides; } public boolean isOptionsUninitialised() { return optionOverrides == null; } /** * Sets the option overrides associated with this invocation * * @param optionOverrides */ public void setOptionOverrides(Option optionOverrides) { this.optionOverrides = optionOverrides; } /** * Tests if this invocation originated locally or from a remote cache. * * @return true if the invocation originated locally. */ public boolean isOriginLocal() { return originLocal; } public List getInvocationLocksAcquired() { return invocationLocks; } public void addInvocationLocksAcquired(Collection locks) { // no need to worry about concurrency here - a context is only valid for a single thread. if (invocationLocks == null) invocationLocks = new ArrayList(5); invocationLocks.addAll(locks); } public void addInvocationLockAcquired(NodeLock l) { // no need to worry about concurrency here - a context is only valid for a single thread. if (invocationLocks == null) invocationLocks = new ArrayList(5); invocationLocks.add(l); } /** * @return true if options exist to suppress locking - false otherwise. Note that this is only used by the {@link org.jboss.cache.interceptors.PessimisticLockInterceptor}. */ public boolean isLockingSuppressed() { return getOptionOverrides() != null && getOptionOverrides().isSuppressLocking(); } /** * If set to true, the invocation is assumed to have originated locally. If set to false, * assumed to have originated from a remote cache. * * @param originLocal */ public void setOriginLocal(boolean originLocal) { this.originLocal = originLocal; } @Override public String toString() { return "InvocationContext{" + "transaction=" + transaction + ", globalTransaction=" + globalTransaction + ", optionOverrides=" + optionOverrides + ", originLocal=" + originLocal + ", txHasMods=" + txHasMods + '}'; } public boolean isTxHasMods() { return txHasMods; } public void setTxHasMods(boolean b) { txHasMods = b; } public boolean isLocalRollbackOnly() { return localRollbackOnly; } /** * Resets this to the defaults used when constructing an invocation context object */ public void reset() { transaction = null; globalTransaction = null; optionOverrides = null; originLocal = true; txHasMods = false; invocationLocks = null; methodCall = null; command = null; } @Override public InvocationContext clone() throws CloneNotSupportedException { InvocationContext clone = (InvocationContext) super.clone(); clone.setOptionOverrides(getOptionOverrides().clone()); return clone; } /** * Sets the state of the InvocationContext based on the template context passed in * * @param template */ public void setState(InvocationContext template) { if (template == null) { throw new NullPointerException("Template InvocationContext passed in to InvocationContext.setState() passed in is null"); } this.setGlobalTransaction(template.getGlobalTransaction()); this.setLocalRollbackOnly(template.isLocalRollbackOnly()); this.setOptionOverrides(template.getOptionOverrides()); this.setOriginLocal(template.isOriginLocal()); this.setTransaction(template.getTransaction()); this.setTxHasMods(template.isTxHasMods()); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; final InvocationContext that = (InvocationContext) o; if (localRollbackOnly != that.localRollbackOnly) return false; if (originLocal != that.originLocal) return false; if (txHasMods != that.txHasMods) return false; if (globalTransaction != null ? !globalTransaction.equals(that.globalTransaction) : that.globalTransaction != null) { return false; } if (optionOverrides != null ? !optionOverrides.equals(that.optionOverrides) : that.optionOverrides != null) { return false; } if (transaction != null ? !transaction.equals(that.transaction) : that.transaction != null) return false; return true; } @Override public int hashCode() { int result; result = (transaction != null ? transaction.hashCode() : 0); result = 29 * result + (globalTransaction != null ? globalTransaction.hashCode() : 0); result = 29 * result + (optionOverrides != null ? optionOverrides.hashCode() : 0); result = 29 * result + (originLocal ? 1 : 0); result = 29 * result + (txHasMods ? 1 : 0); result = 29 * result + (localRollbackOnly ? 1 : 0); return result; } /** * @return the method call associated with this invocation */ @Deprecated @SuppressWarnings("deprecation") public MethodCall getMethodCall() { if (methodCall == null) { methodCall = createMethodCall(); } return methodCall; } @SuppressWarnings("deprecation") private MethodCall createMethodCall() { if (command == null) return null; MethodCall call = new MethodCall(); call.setMethodId(command.getCommandId()); call.setArgs(command.getParameters()); return call; } /** * Sets the method call associated with this invocation. * * @param methodCall methodcall to set * @deprecated not used anymore. Interceptors will get a {@link org.jboss.cache.commands.ReplicableCommand} instance passed in along with an InvocationContext. */ @Deprecated public void setMethodCall(MethodCall methodCall) { this.methodCall = methodCall; } /** * Factory method that creates a context with a given method call. * * @param methodCall methodcall to use * @return invocation context */ public static InvocationContext fromMethodCall(MethodCall methodCall) { InvocationContext ctx = new InvocationContext(); ctx.methodCall = methodCall; return ctx; } /** * If the acq timeout if overwritten for current call, then return that one. * If not overwritten return default value. */ public long getContextLockAcquisitionTimeout(long timeout) { if (getOptionOverrides() != null && getOptionOverrides().getLockAcquisitionTimeout() >= 0) { timeout = getOptionOverrides().getLockAcquisitionTimeout(); } return timeout; } /** * This is only used for backward compatibility with old interceptors implementation and should NOT be * use by any new custom interceptors. The commands is now passed in as the second param in each implementing * handlers (handler = method in ChainedInterceptor class) */ @Deprecated @SuppressWarnings("deprecation") public void setCommand(VisitableCommand cacheCommand) { this.command = cacheCommand; } /** * @see #setCommand(org.jboss.cache.commands.VisitableCommand) */ @Deprecated @SuppressWarnings("deprecation") public VisitableCommand getCommand() { return command; } public boolean isValidTransaction() { return transaction != null && TransactionTable.isValid(transaction); } public void throwIfNeeded(Throwable e) throws Throwable { Option optionOverride = getOptionOverrides(); boolean shouldRethtrow = optionOverride == null || !optionOverride.isFailSilently(); if (!shouldRethtrow) { if (trace) log.trace("There was a problem handling this request, but failSilently was set, so suppressing exception", e); return; } throw e; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/invocation/0000755000175000017500000000000011376174010024552 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/invocation/AbstractInvocationDelegate.java0000644000175000017500000000356711010541256032653 0ustar twernertwernerpackage org.jboss.cache.invocation; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.ComponentRegistry; import org.jboss.cache.factories.annotations.Inject; import org.jboss.cache.interceptors.InterceptorChain; /** * The JBoss Cache hand-wired interceptor stack. A "minimal" AOP framework which uses delegation through an * interceptor chain rather than any bytecode manipulation. *

        * This class provides some generic behaviour such as the construction of an {@link org.jboss.cache.InvocationContext} * which is passed up the interceptor chain. *

        * * @author Manik Surtani (manik@jboss.org) * @see org.jboss.cache.interceptors.base.CommandInterceptor * @see org.jboss.cache.InvocationContext * @since 2.1.0 */ public abstract class AbstractInvocationDelegate { protected Log log = LogFactory.getLog(getClass()); protected Configuration configuration; protected InvocationContextContainer invocationContextContainer; protected ComponentRegistry componentRegistry; protected InterceptorChain invoker; protected boolean originLocal = true; /** * Used by the interceptor chain factory to inject dependencies. */ @Inject public void initialize(Configuration configuration, InvocationContextContainer invocationContextContainer, ComponentRegistry componentRegistry, InterceptorChain interceptorChain) { this.configuration = configuration; this.invocationContextContainer = invocationContextContainer; this.invoker = interceptorChain; this.componentRegistry = componentRegistry; } protected void assertIsConstructed() { if (invocationContextContainer == null) throw new IllegalStateException("The cache has been destroyed!"); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/invocation/InvocationContextContainer.java0000644000175000017500000000102611010537324032731 0ustar twernertwernerpackage org.jboss.cache.invocation; import org.jboss.cache.InvocationContext; import org.jboss.cache.factories.annotations.NonVolatile; /** * Container and factory for thread locals * * @author Manik Surtani (manik@jboss.org) * @since 2.1.0 */ @NonVolatile public class InvocationContextContainer extends ThreadLocal { @Override protected InvocationContext initialValue() { // create if this is initially unset return new InvocationContext(); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/invocation/NodeInvocationDelegate.java0000644000175000017500000002653011031017377031775 0ustar twernertwernerpackage org.jboss.cache.invocation; import org.jboss.cache.CacheException; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.Node; import org.jboss.cache.NodeNotValidException; import org.jboss.cache.NodeSPI; import org.jboss.cache.UnversionedNode; import org.jboss.cache.config.Option; import org.jboss.cache.lock.NodeLock; import org.jboss.cache.optimistic.DataVersion; import org.jboss.cache.transaction.GlobalTransaction; import java.util.Collections; import java.util.HashSet; import java.util.Map; import java.util.Set; /** * The delegate that users (and interceptor authors) interact with when they obtain a node from the cache or another node. * This wrapper delegates calls down the interceptor chain. * * @author Manik Surtani (manik@jboss.org) * @since 2.1.0 */ @SuppressWarnings("unchecked") public class NodeInvocationDelegate extends AbstractInvocationDelegate implements NodeSPI { private final UnversionedNode node; private CacheSPI spi; public NodeInvocationDelegate(UnversionedNode node) { this.node = node; } public Object getDelegationTarget() { return node; } public void injectDependencies(CacheSPI spi) { this.spi = spi; } public boolean isChildrenLoaded() { return node.isChildrenLoaded(); } public void setChildrenLoaded(boolean loaded) { node.setChildrenLoaded(loaded); } public boolean isDataLoaded() { return node.isDataLoaded(); } public void setDataLoaded(boolean dataLoaded) { node.setDataLoaded(dataLoaded); } public Map> getChildrenMapDirect() { return node.getChildrenMapDirect(); } public void setChildrenMapDirect(Map> children) { node.setChildrenMapDirect(children); } public NodeSPI getOrCreateChild(Object name, GlobalTransaction tx) { return node.getOrCreateChild(name, tx, true); } public NodeLock getLock() { return node.getLock(); } public void setFqn(Fqn f) { node.setFqn(f); } public boolean isDeleted() { return node.isDeleted(); } public void markAsDeleted(boolean marker) { node.markAsDeleted(marker); } public void markAsDeleted(boolean marker, boolean recursive) { node.markAsDeleted(marker, recursive); } public void addChild(Object nodeName, Node nodeToAdd) { node.addChild(nodeName, nodeToAdd); } public void printDetails(StringBuilder sb, int indent) { node.printDetails(sb, indent); } public void print(StringBuilder sb, int indent) { throw new CacheException("This method is deprecated!"); } public void setVersion(DataVersion version) { node.setVersion(version); } public DataVersion getVersion() { return node.getVersion(); } public Set> getChildrenDirect() { return node.getChildrenDirect(); } public void removeChildrenDirect() { node.removeChildrenDirect(); } public Set> getChildrenDirect(boolean includeMarkedAsDeleted) { return node.getChildrenDirect(includeMarkedAsDeleted); } public NodeSPI getChildDirect(Object childName) { return node.getChildDirect(childName); } public NodeSPI addChildDirect(Fqn childName) { return node.addChildDirect(childName); } public NodeSPI addChildDirect(Fqn f, boolean notify) { return node.addChildDirect(f, notify); } public NodeSPI addChildDirect(Object childName, boolean notify) { return node.addChildDirect(childName, notify); } public void addChildDirect(NodeSPI child) { node.addChildDirect(child); } public NodeSPI getChildDirect(Fqn childName) { return node.getChildDirect(childName); } public boolean removeChildDirect(Fqn fqn) { return node.removeChildDirect(fqn); } public boolean removeChildDirect(Object childName) { return node.removeChildDirect(childName); } public V removeDirect(K key) { return (V) node.removeDirect(key); } public V putDirect(K key, V value) { return (V) node.putDirect(key, value); } public void putAllDirect(Map data) { node.putAllDirect(data); } public Map getDataDirect() { return node.getDataDirect(); } public V getDirect(K key) { return (V) node.getDirect(key); } public void clearDataDirect() { node.clearDataDirect(); } public Set getKeysDirect() { return node.getKeysDirect(); } public Set getChildrenNamesDirect() { return node.getChildrenNamesDirect(); } public CacheSPI getCache() { return spi; } public NodeSPI getParent() { return node.getParent(); } public Set> getChildren() { assertValid(); if (spi == null) return Collections.emptySet(); Set> children = new HashSet>(); for (Object c : spi.getChildrenNames(getFqn())) { Node n = spi.getNode(Fqn.fromRelativeElements(getFqn(), c)); if (n != null) children.add(n); } return Collections.unmodifiableSet(children); } public Set getChildrenNames() { assertValid(); return spi.getChildrenNames(getFqn()); } public Map getData() { assertValid(); if (spi == null) return Collections.emptyMap(); return spi.getData(getFqn()); } public Set getKeys() { assertValid(); Set keys = spi.getKeys(getFqn()); return keys == null ? Collections.emptySet() : Collections.unmodifiableSet(keys); } public Fqn getFqn() { return node.getFqn(); } public Node addChild(Fqn f) { // TODO: Revisit. Is this really threadsafe? See comment in putIfAbsent() - same solution should be applied here too. assertValid(); Fqn nf = Fqn.fromRelativeFqn(getFqn(), f); Option o1; try { o1 = spi.getInvocationContext().getOptionOverrides().clone(); } catch (CloneNotSupportedException e) { // should never happen throw new RuntimeException(e); } Node child = getChild(f); if (child == null) { Option o2; try { o2 = o1.clone(); } catch (CloneNotSupportedException e) { // should never happen throw new RuntimeException(e); } spi.getInvocationContext().setOptionOverrides(o1); spi.put(nf, null); spi.getInvocationContext().setOptionOverrides(o2); child = getChild(f); } return child; } public boolean removeChild(Fqn f) { assertValid(); return spi.removeNode(Fqn.fromRelativeFqn(getFqn(), f)); } public boolean removeChild(Object childName) { assertValid(); return spi.removeNode(Fqn.fromRelativeElements(getFqn(), childName)); } public Node getChild(Fqn f) { assertValid(); return spi.getNode(Fqn.fromRelativeFqn(getFqn(), f)); } public Node getChild(Object name) { assertValid(); return spi.getNode(Fqn.fromRelativeElements(getFqn(), name)); } public V put(K key, V value) { assertValid(); return spi.put(getFqn(), key, value); } public V putIfAbsent(K k, V v) { assertValid(); // TODO: Refactor this!! Synchronized block here sucks, this could lead to a deadlock since the locking interceptors will not use the same mutex. // will only work once we make calls directly on the UnversionedNode in the CallInterceptor rather than multiple calls via the CacheImpl. synchronized (this) { if (!getKeys().contains(k)) return put(k, v); else return get(k); } } public V replace(K key, V value) { assertValid(); // TODO: Refactor this!! Synchronized block here sucks, this could lead to a deadlock since the locking interceptors will not use the same mutex. // will only work once we make calls directly on the UnversionedNode in the CallInterceptor rather than multiple calls via the CacheImpl. synchronized (this) { if (getKeys().contains(key)) { return put(key, value); } else return null; } } public boolean replace(K key, V oldValue, V newValue) { assertValid(); // TODO: Refactor this!! Synchronized block here sucks, this could lead to a deadlock since the locking interceptors will not use the same mutex. // will only work once we make calls directly on the UnversionedNode in the CallInterceptor rather than multiple calls via the CacheImpl. synchronized (this) { if (oldValue.equals(get(key))) { put(key, newValue); return true; } else return false; } } public void putAll(Map data) { assertValid(); spi.put(getFqn(), data); } public void replaceAll(Map data) { assertValid(); spi.clearData(getFqn()); spi.put(getFqn(), data); } public V get(K key) { assertValid(); return spi.get(getFqn(), key); } public V remove(K key) { assertValid(); return spi.remove(getFqn(), key); } public void clearData() { assertValid(); spi.clearData(getFqn()); } public int dataSize() { assertValid(); return spi.getKeys(getFqn()).size(); } public boolean hasChild(Fqn f) { // TODO: This could be made more efficient when calls are made directly on the node assertValid(); return getChild(f) != null; } public boolean hasChild(Object o) { // TODO: This could be made more efficient when calls are made directly on the node assertValid(); return getChild(o) != null; } public boolean isValid() { return node.isValid(); } public boolean isResident() { return node.isResident(); } public void setResident(boolean resident) { node.setResident(resident); } public boolean isLockForChildInsertRemove() { return node.isLockForChildInsertRemove(); } public void setLockForChildInsertRemove(boolean lockForChildInsertRemove) { node.setLockForChildInsertRemove(lockForChildInsertRemove); } public void releaseObjectReferences(boolean recursive) { node.releaseObjectReferences(recursive); } public boolean hasChildrenDirect() { return node.hasChildrenDirect(); } public Map getInternalState(boolean onlyInternalState) { return node.getInternalState(onlyInternalState); } public void setInternalState(Map state) { node.setInternalState(state); } public void setValid(boolean valid, boolean recursive) { node.setValid(valid, recursive); } protected void assertValid() { if (!getFqn().isRoot() && !node.isValid()) throw new NodeNotValidException("Node " + getFqn() + " is not valid. Perhaps it has been moved or removed."); } @Override public String toString() { return node == null ? "null" : node.toString(); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/invocation/CacheInvocationDelegate.java0000644000175000017500000004204511042316612032106 0ustar twernertwernerpackage org.jboss.cache.invocation; import org.jboss.cache.CacheException; import org.jboss.cache.CacheSPI; import org.jboss.cache.CacheStatus; import org.jboss.cache.DataContainer; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.NodeNotExistsException; import org.jboss.cache.NodeSPI; import org.jboss.cache.RPCManager; import org.jboss.cache.Region; import org.jboss.cache.RegionManager; import org.jboss.cache.Version; import org.jboss.cache.buddyreplication.BuddyManager; import org.jboss.cache.buddyreplication.GravitateResult; import org.jboss.cache.commands.read.GetChildrenNamesCommand; import org.jboss.cache.commands.read.GetDataMapCommand; import org.jboss.cache.commands.read.GetKeyValueCommand; import org.jboss.cache.commands.read.GetKeysCommand; import org.jboss.cache.commands.read.GetNodeCommand; import org.jboss.cache.commands.read.GravitateDataCommand; import org.jboss.cache.commands.write.EvictCommand; import org.jboss.cache.commands.write.MoveCommand; import org.jboss.cache.commands.write.PutDataMapCommand; import org.jboss.cache.commands.write.PutForExternalReadCommand; import org.jboss.cache.commands.write.PutKeyValueCommand; import org.jboss.cache.commands.write.RemoveKeyCommand; import org.jboss.cache.commands.write.RemoveNodeCommand; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Option; import org.jboss.cache.factories.CommandsFactory; import org.jboss.cache.factories.annotations.Inject; import org.jboss.cache.factories.annotations.NonVolatile; import org.jboss.cache.interceptors.base.CommandInterceptor; import org.jboss.cache.loader.CacheLoaderManager; import org.jboss.cache.marshall.Marshaller; import org.jboss.cache.notifications.Notifier; import org.jboss.cache.statetransfer.StateTransferManager; import org.jboss.cache.transaction.GlobalTransaction; import org.jboss.cache.transaction.TransactionTable; import org.jgroups.Address; import javax.transaction.Transaction; import javax.transaction.TransactionManager; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; /** * The delegate that users (and ChainedInterceptor authors) interact with when they create a cache by using a cache factory. * This wrapper delegates calls down the interceptor chain. * * @author Manik Surtani (manik@jboss.org) * @since 2.1.0 */ @SuppressWarnings("unchecked") @NonVolatile public class CacheInvocationDelegate extends AbstractInvocationDelegate implements CacheSPI { // this stuff is needed since the SPI has methods to retrieve these. private StateTransferManager stateTransferManager; private CacheLoaderManager cacheLoaderManager; private Notifier notifier; private TransactionManager transactionManager; private BuddyManager buddyManager; private TransactionTable transactionTable; private RPCManager rpcManager; private RegionManager regionManager; private Marshaller marshaller; private DataContainer dataContainer; private CommandsFactory commandsFactory; @Inject public void initialize(StateTransferManager stateTransferManager, CacheLoaderManager cacheLoaderManager, Notifier notifier, TransactionManager transactionManager, BuddyManager buddyManager, TransactionTable transactionTable, RPCManager rpcManager, RegionManager regionManager, Marshaller marshaller, CommandsFactory commandsFactory, DataContainer dataContainer) { this.stateTransferManager = stateTransferManager; this.cacheLoaderManager = cacheLoaderManager; this.notifier = notifier; this.transactionManager = transactionManager; this.buddyManager = buddyManager; this.transactionTable = transactionTable; this.rpcManager = rpcManager; this.regionManager = regionManager; this.marshaller = marshaller; this.dataContainer = dataContainer; this.commandsFactory = commandsFactory; } private void reset() { this.stateTransferManager = null; this.cacheLoaderManager = null; this.transactionManager = null; this.buddyManager = null; this.transactionTable = null; this.rpcManager = null; this.marshaller = null; this.dataContainer = null; this.commandsFactory = null; } @Override public String toString() { return dataContainer == null ? super.toString() : dataContainer.toString(); } public Configuration getConfiguration() { return configuration; } public NodeSPI getRoot() { return (NodeSPI) dataContainer.getRoot(); } public TransactionManager getTransactionManager() { return transactionManager; } public void addInterceptor(CommandInterceptor i, int position) { invoker.addInterceptor(i, position); } public void addInterceptor(CommandInterceptor i, Class afterInterceptor) { invoker.addInterceptor(i, afterInterceptor); } public List getInterceptorChain() { return invoker.asList(); } public void removeInterceptor(int position) { invoker.removeInterceptor(position); } public void removeInterceptor(Class interceptorType) { invoker.removeInterceptor(interceptorType); } public CacheLoaderManager getCacheLoaderManager() { return cacheLoaderManager; } public BuddyManager getBuddyManager() { return buddyManager; } public TransactionTable getTransactionTable() { return transactionTable; } public RPCManager getRPCManager() { return rpcManager; } public StateTransferManager getStateTransferManager() { return stateTransferManager; } public String getClusterName() { return configuration.getClusterName(); } public int getNumberOfAttributes() { return dataContainer.getNumberOfAttributes(); } public int getNumberOfNodes() { return dataContainer.getNumberOfNodes(); } public RegionManager getRegionManager() { return regionManager; } public GlobalTransaction getCurrentTransaction(Transaction tx, boolean createIfNotExists) { return transactionTable.getCurrentTransaction(tx, createIfNotExists); } public GlobalTransaction getCurrentTransaction() { return transactionTable.getCurrentTransaction(); } public Set getInternalFqns() { return dataContainer.getInternalFqns(); } public int getNumberOfLocksHeld() { return dataContainer.getNumberOfLocksHeld(); } public boolean exists(String fqn) { return exists(Fqn.fromString(fqn)); } public boolean exists(Fqn fqn) { return peek(fqn, false, false) != null; } public Notifier getNotifier() { return notifier; } public Marshaller getMarshaller() { return marshaller; } public GravitateResult gravitateData(Fqn fqn, boolean searchBuddyBackupSubtrees, InvocationContext ctx) { cacheStatusCheck(ctx); GravitateDataCommand command = commandsFactory.buildGravitateDataCommand(fqn, searchBuddyBackupSubtrees); return (GravitateResult) invoker.invoke(ctx, command); // return (GravitateResult) command.perform(null); } public NodeSPI peek(Fqn fqn, boolean includeDeletedNodes, boolean includeInvalidNodes) { return (NodeSPI) dataContainer.peek(fqn, includeDeletedNodes, includeInvalidNodes); } public NodeSPI peek(Fqn fqn, boolean includeDeletedNodes) { return (NodeSPI) dataContainer.peek(fqn, includeDeletedNodes); } public void addCacheListener(Object listener) { notifier.addCacheListener(listener); } public void removeCacheListener(Object listener) { notifier.removeCacheListener(listener); } public Set getCacheListeners() { return notifier.getCacheListeners(); } public void create() throws CacheException { componentRegistry.create(); } public void start() throws CacheException { componentRegistry.start(); } public void stop() { componentRegistry.stop(); } public void destroy() { reset(); componentRegistry.destroy(); } public CacheStatus getCacheStatus() { return componentRegistry.getState(); } public InvocationContext getInvocationContext() { assertIsConstructed(); return invocationContextContainer.get(); } public void setInvocationContext(InvocationContext ctx) { assertIsConstructed(); // assume a null ctx is meant to "un-set" the context? if (ctx == null) invocationContextContainer.remove(); else invocationContextContainer.set(ctx); } public Address getLocalAddress() { if (rpcManager == null) return null; return rpcManager.getLocalAddress(); } public List
        getMembers() { if (rpcManager == null) return null; return rpcManager.getMembers(); } public String getVersion() { return Version.printVersion(); } public void move(Fqn nodeToMove, Fqn newParent) throws NodeNotExistsException { InvocationContext ctx = invocationContextContainer.get(); cacheStatusCheck(ctx); MoveCommand command = commandsFactory.buildMoveCommand(nodeToMove, newParent); invoker.invoke(ctx, command); } public void move(String nodeToMove, String newParent) throws NodeNotExistsException { move(Fqn.fromString(nodeToMove), Fqn.fromString(newParent)); } public boolean removeRegion(Fqn fqn) { return regionManager.removeRegion(fqn); } public Region getRegion(Fqn fqn, boolean createIfAbsent) { return regionManager.getRegion(fqn, createIfAbsent); } public void evict(Fqn fqn, boolean recursive) { InvocationContext ctx = invocationContextContainer.get(); cacheStatusCheck(ctx); EvictCommand c = commandsFactory.buildEvictFqnCommand(fqn); c.setRecursive(recursive); invoker.invoke(ctx, c); } public void evict(Fqn fqn) { evict(fqn, false); } public V get(Fqn fqn, K key) { InvocationContext ctx = invocationContextContainer.get(); cacheStatusCheck(ctx); GetKeyValueCommand command = commandsFactory.buildGetKeyValueCommand(fqn, key, true); return (V) invoker.invoke(ctx, command); } public V get(String fqn, K key) { return get(Fqn.fromString(fqn), key); } public boolean removeNode(Fqn fqn) { // special case if we are removing the root. Remove all children instead. if (fqn.isRoot()) { boolean result = true; // we need to preserve options InvocationContext ctx = getInvocationContext(); Option o = ctx.getOptionOverrides(); Set internalFqns = getInternalFqns(); for (Object childName : peek(fqn, false, false).getChildrenNames()) { if (!internalFqns.contains(Fqn.fromElements(childName))) { ctx.setOptionOverrides(o); result = removeNode(Fqn.fromRelativeElements(fqn, childName)) && result; } } return result; } else { InvocationContext ctx = invocationContextContainer.get(); cacheStatusCheck(ctx); GlobalTransaction tx = transactionTable.getCurrentTransaction(); RemoveNodeCommand command = commandsFactory.buildRemoveNodeCommand(tx, fqn); Object retval = invoker.invoke(ctx, command); return retval != null && (Boolean) retval; } } public boolean removeNode(String fqn) { return removeNode(Fqn.fromString(fqn)); } public NodeSPI getNode(Fqn fqn) { InvocationContext ctx = invocationContextContainer.get(); cacheStatusCheck(ctx); GetNodeCommand command = commandsFactory.buildGetNodeCommand(fqn); return (NodeSPI) invoker.invoke(ctx, command); } public NodeSPI getNode(String fqn) { return getNode(Fqn.fromString(fqn)); } public V remove(Fqn fqn, K key) throws CacheException { InvocationContext ctx = invocationContextContainer.get(); cacheStatusCheck(ctx); GlobalTransaction tx = transactionTable.getCurrentTransaction(); RemoveKeyCommand command = commandsFactory.buildRemoveKeyCommand(tx, fqn, key); return (V) invoker.invoke(ctx, command); } public V remove(String fqn, K key) { return remove(Fqn.fromString(fqn), key); } public void put(Fqn fqn, Map data) { InvocationContext ctx = invocationContextContainer.get(); cacheStatusCheck(ctx); PutDataMapCommand command = commandsFactory.buildPutDataMapCommand(null, fqn, data); invoker.invoke(ctx, command); } public void put(String fqn, Map data) { put(Fqn.fromString(fqn), data); } public void putForExternalRead(Fqn fqn, K key, V value) { InvocationContext ctx = invocationContextContainer.get(); cacheStatusCheck(ctx); // if the node exists then this should be a no-op. if (peek(fqn, false, false) == null) { getInvocationContext().getOptionOverrides().setFailSilently(true); getInvocationContext().getOptionOverrides().setForceAsynchronous(true); PutForExternalReadCommand command = commandsFactory.buildPutForExternalReadCommand(null, fqn, key, value); invoker.invoke(ctx, command); } else { if (log.isDebugEnabled()) log.debug("putForExternalRead() called with Fqn " + fqn + " and this node already exists. This method is hence a no op."); } } public V put(Fqn fqn, K key, V value) { InvocationContext ctx = invocationContextContainer.get(); cacheStatusCheck(ctx); GlobalTransaction tx = transactionTable.getCurrentTransaction(); PutKeyValueCommand command = commandsFactory.buildPutKeyValueCommand(tx, fqn, key, value); return (V) invoker.invoke(ctx, command); } public V put(String fqn, K key, V value) { return put(Fqn.fromString(fqn), key, value); } /** * Retrieves a defensively copied data map of the underlying node. * * @param fqn * @return map of data, or an empty map * @throws CacheException */ public Map getData(Fqn fqn) { InvocationContext ctx = invocationContextContainer.get(); cacheStatusCheck(ctx); GetDataMapCommand command = commandsFactory.buildGetDataMapCommand(fqn); return (Map) invoker.invoke(ctx, command); } /** * Returns a set of attribute keys for the Fqn. * Returns null if the node is not found, otherwise a Set. * The set is a copy of the actual keys for this node. * * @param fqn name of the node */ public Set getKeys(String fqn) { return getKeys(Fqn.fromString(fqn)); } /** * Returns a set of attribute keys for the Fqn. * Returns null if the node is not found, otherwise a Set. * The set is a copy of the actual keys for this node. * * @param fqn name of the node */ public Set getKeys(Fqn fqn) { InvocationContext ctx = invocationContextContainer.get(); cacheStatusCheck(ctx); GetKeysCommand command = commandsFactory.buildGetKeysCommand(fqn); return (Set) invoker.invoke(ctx, command); } /** * Removes the keys and properties from a node. */ public void clearData(String fqn) throws CacheException { clearData(Fqn.fromString(fqn)); } /** * Removes the keys and properties from a named node. */ public void clearData(Fqn fqn) { InvocationContext ctx = invocationContextContainer.get(); cacheStatusCheck(ctx); GlobalTransaction tx = getCurrentTransaction(); invoker.invoke(ctx, commandsFactory.buildClearDataCommand(tx, fqn)); } /** * Returns all children of a given node. Returns an empty set if there are no children. * The set is unmodifiable. * * @param fqn The fully qualified name of the node * @return Set an unmodifiable set of children names, Object. */ public Set getChildrenNames(Fqn fqn) { InvocationContext ctx = invocationContextContainer.get(); cacheStatusCheck(ctx); GetChildrenNamesCommand command = commandsFactory.buildGetChildrenNamesCommand(fqn); Set retval = (Set) invoker.invoke(ctx, command); if (retval != null) retval = Collections.unmodifiableSet(new HashSet(retval)); else retval = Collections.emptySet(); return retval; } public Set getChildrenNames(String fqn) { return getChildrenNames(Fqn.fromString(fqn)); } public DataContainer getDataContainer() { return dataContainer; } protected void cacheStatusCheck(InvocationContext ctx) { assertIsConstructed(); if (!ctx.getOptionOverrides().isSkipCacheStatusCheck() && !componentRegistry.invocationsAllowed(true)) { throw new IllegalStateException("Cache not in STARTED state!"); } } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/Cache.java0000644000175000017500000004632411021741174024256 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache; import net.jcip.annotations.ThreadSafe; import org.jboss.cache.config.Configuration; import org.jgroups.Address; import java.util.List; import java.util.Map; import java.util.Set; /** * Interface for a Cache where data mappings are grouped and stored in a tree data * structure consisting of {@link Node}s. *

        * This is the central construct and basic client API of JBoss Cache and is used for * cache-wide operations. *

        * The cache is constructed using a {@link CacheFactory} and is started * using {@link #start}, if not already started by the CacheFactory. *

        * Once constructed, the Cache interface can be used to create or access {@link Node}s, which contain data. Once references * to {@link Node}s are obtained, data can be stored in them, *

        * As a convenience (and mainly to provide a familiar API to the older JBoss Cache 1.x.x releases) methods are provided that * operate directly on nodes. *

          *
        • {@link #put(Fqn,Object,Object)}
        • *
        • {@link #put(Fqn,java.util.Map)}
        • *
        • {@link #get(Fqn,Object)}
        • *
        • {@link #remove(Fqn,Object)}
        • *
        • {@link #removeNode(Fqn)}
        • *
        *

        * A simple example of usage: *

         *    // creates with default settings and starts the cache
         *    Cache cache = DefaultCacheFactory.getInstance().createCache();
         *    Fqn personRecords = Fqn.fromString("/org/mycompany/personRecords");
         * 

        * Node rootNode = cache.getRoot(); * Node personRecordsNode = rootNode.addChild(personRecords); *

        * // now add some person records. * Fqn peterGriffin = Fqn.fromString("/peterGriffin"); * Fqn stewieGriffin = Fqn.fromString("/stewieGriffin"); *

        * // the addChild() API uses relative Fqns * Node peter = personRecordsNode.addChild(peterGriffin); * Node stewie = personRecordsNode.addChild(stewieGriffin); *

        * peter.put("name", "Peter Griffin"); * peter.put("ageGroup", "MidLifeCrisis"); * peter.put("homicidal", Boolean.FALSE); *

        * stewie.put("name", "Stewie Griffin"); * stewie.put("ageGroup", "Infant"); * stewie.put("homicidal", Boolean.TRUE); *

        * peter.getFqn().toString(); // will print out /org/mycompany/personRecords/peterGriffin * stewie.getFqn().toString(); // will print out /org/mycompany/personRecords/stewieGriffin *

        * peter.getFqn().getParent().equals(stewie.getFqn().getParent()); // will return true *

        *

        *

        * For more information, please read the JBoss Cache user guide and tutorial, available on the JBoss Cache documentation site, * and look through the examples shipped with the JBoss Cache distribution. * * @author Manik Surtani (manik@jboss.org) * @see Node * @see CacheFactory * @since 2.0.0 */ @ThreadSafe public interface Cache { /** * Retrieves the configuration of this cache. * * @return the configuration. */ Configuration getConfiguration(); /** * Returns the root node of this cache. * * @return the root node */ Node getRoot(); /** * Adds a {@link org.jboss.cache.notifications.annotation.CacheListener}-annotated object to the entire cache. The object passed in needs to be properly annotated with the * {@link org.jboss.cache.notifications.annotation.CacheListener} annotation otherwise an {@link org.jboss.cache.notifications.IncorrectCacheListenerException} will be thrown. * * @param listener listener to add */ void addCacheListener(Object listener); /** * Removes a {@link org.jboss.cache.notifications.annotation.CacheListener}-annotated object from the cache. The object passed in needs to be properly annotated with the * {@link org.jboss.cache.notifications.annotation.CacheListener} annotation otherwise an {@link org.jboss.cache.notifications.IncorrectCacheListenerException} will be thrown. * * @param listener listener to remove */ void removeCacheListener(Object listener); /** * Retrieves an immutable {@link List} of objects annotated as {@link org.jboss.cache.notifications.annotation.CacheListener}s attached to the cache. * * @return an immutable {@link List} of objects annotated as {@link org.jboss.cache.notifications.annotation.CacheListener}s attached to the cache. */ Set getCacheListeners(); /** * Associates the specified value with the specified key for a {@link Node} in this cache. * If the {@link Node} previously contained a mapping for this key, the old value is replaced by the specified value. * * @param fqn absolute {@link Fqn} to the {@link Node} to be accessed. * @param key key with which the specified value is to be associated. * @param value value to be associated with the specified key. * @return previous value associated with specified key, or null if there was no mapping for key. * A null return can also indicate that the Node previously associated null with the specified key, if the implementation supports null values. * @throws IllegalStateException if the cache is not in a started state. */ V put(Fqn fqn, K key, V value); /** * Convenience method that takes a string representation of an Fqn. Otherwise identical to {@link #put(Fqn, Object, Object)} * * @param fqn String representation of the Fqn * @param key key with which the specified value is to be associated. * @param value value to be associated with the specified key. * @return previous value associated with specified key, or null if there was no mapping for key. * A null return can also indicate that the Node previously associated null with the specified key, if the implementation supports null values. * @throws IllegalStateException if the cache is not in a started state */ V put(String fqn, K key, V value); /** * Under special operating behavior, associates the value with the specified key for a node identified by the Fqn passed in. *
          *
        • Only goes through if the node specified does not exist; no-op otherwise. *
        • Force asynchronous mode for replication to prevent any blocking.
        • *
        • invalidation does not take place.
        • *
        • 0ms lock timeout to prevent any blocking here either. If the lock is not acquired, this method is a no-op, and swallows the timeout exception.
        • *
        • Ongoing transactions are suspended before this call, so failures here will not affect any ongoing transactions.
        • *
        • Errors and exceptions are 'silent' - logged at a much lower level than normal, and this method does not throw exceptions
        • *
        * This method is for caching data that has an external representation in storage, where, concurrent modification and * transactions are not a consideration, and failure to put the data in the cache should be treated as a 'suboptimal outcome' * rather than a 'failing outcome'. *

        * An example of when this method is useful is when data is read from, for example, a legacy datastore, and is cached before * returning the data to the caller. Subsequent calls would prefer to get the data from the cache and if the data doesn't exist * in the cache, fetch again from the legacy datastore. *

        * See JBCACHE-848 for details around this feature. *

        * * @param fqn absolute {@link Fqn} to the {@link Node} to be accessed. * @param key key with which the specified value is to be associated. * @param value value to be associated with the specified key. * @throws IllegalStateException if {@link #getCacheStatus()} would not return {@link org.jboss.cache.CacheStatus#STARTED}. */ void putForExternalRead(Fqn fqn, K key, V value); /** * Copies all of the mappings from the specified map to a {@link Node}. * * @param fqn absolute {@link Fqn} to the {@link Node} to copy the data to * @param data mappings to copy * @throws IllegalStateException if the cache is not in a started state */ void put(Fqn fqn, Map data); /** * Convenience method that takes a string representation of an Fqn. Otherwise identical to {@link #put(Fqn, java.util.Map)} * * @param fqn String representation of the Fqn * @param data data map to insert * @throws IllegalStateException if the cache is not in a started state */ void put(String fqn, Map data); /** * Removes the mapping for this key from a Node. * Returns the value to which the Node previously associated the key, or * null if the Node contained no mapping for this key. * * @param fqn absolute {@link Fqn} to the {@link Node} to be accessed. * @param key key whose mapping is to be removed from the Node * @return previous value associated with specified Node's key * @throws IllegalStateException if the cache is not in a started state */ V remove(Fqn fqn, K key); /** * Convenience method that takes a string representation of an Fqn. Otherwise identical to {@link #remove(Fqn, Object)} * * @param fqn string representation of the Fqn to retrieve * @param key key to remove * @return old value removed, or null if the fqn does not exist * @throws IllegalStateException if the cache is not in a started state */ V remove(String fqn, K key); /** * Removes a {@link Node} indicated by absolute {@link Fqn}. * * @param fqn {@link Node} to remove * @return true if the node was removed, false if the node was not found * @throws IllegalStateException if the cache is not in a started state */ boolean removeNode(Fqn fqn); /** * Convenience method that takes a string representation of an Fqn. Otherwise identical to {@link #removeNode(Fqn)} * * @param fqn string representation of the Fqn to retrieve * @return true if the node was found and removed, false otherwise * @throws IllegalStateException if the cache is not in a started state */ boolean removeNode(String fqn); /** * A convenience method to retrieve a node directly from the cache. Equivalent to calling cache.getRoot().getChild(fqn). * * @param fqn fqn of the node to retrieve * @return a Node object, or a null if the node does not exist. * @throws IllegalStateException if the cache is not in a started state */ Node getNode(Fqn fqn); /** * Convenience method that takes a string representation of an Fqn. Otherwise identical to {@link #getNode(Fqn)} * * @param fqn string representation of the Fqn to retrieve * @return node, or null if the node does not exist * @throws IllegalStateException if the cache is not in a started state */ Node getNode(String fqn); /** * Convenience method that allows for direct access to the data in a {@link Node}. * * @param fqn absolute {@link Fqn} to the {@link Node} to be accessed. * @param key key under which value is to be retrieved. * @return returns data held under specified key in {@link Node} denoted by specified Fqn. * @throws IllegalStateException if the cache is not in a started state */ V get(Fqn fqn, K key); /** * Convenience method that takes a string representation of an Fqn. Otherwise identical to {@link #get(Fqn, Object)} * * @param fqn string representation of the Fqn to retrieve * @param key key to fetch * @return value, or null if the fqn does not exist. * @throws IllegalStateException if the cache is not in a started state */ V get(String fqn, K key); /** * Eviction call that evicts the specified {@link Node} from memory. * * @param fqn absolute {@link Fqn} to the {@link Node} to be evicted. * @param recursive evicts children as well * @throws IllegalStateException if the cache is not in a started state */ void evict(Fqn fqn, boolean recursive); /** * Eviction call that evicts the specified {@link Node} from memory. Not recursive. * * @param fqn absolute {@link Fqn} to the {@link Node} to be evicted. * @throws IllegalStateException if the cache is not in a started state */ void evict(Fqn fqn); /** * Retrieves a {@link Region} for a given {@link Fqn}. If the region does not exist, * and

      • createIfAbsent
      • is true, then one is created. *

        * If not, parent Fqns will be consulted in turn for registered regions, gradually working up to * Fqn.ROOT. If no regions are defined in any of the parents either, a null is returned. * * @param fqn Fqn that is contained in a region. * @param createIfAbsent If true, will create a new associated region if not found. * @return a MarshRegion. Null if none is found. * @throws UnsupportedOperationException if the region cannot be defined. * @see Region */ Region getRegion(Fqn fqn, boolean createIfAbsent); /** * Removes a region denoted by the Fqn passed in. * * @param fqn of the region to remove * @return true if a region did exist and was removed; false otherwise. */ boolean removeRegion(Fqn fqn); /** * Lifecycle method that initializes configuration state, the root node, etc. * * @throws CacheException if there are creation problems */ void create() throws CacheException; /** * Lifecycle method that starts the cache loader, * starts cache replication, starts the region manager, etc., and (if configured) warms the cache using a * state transfer or cache loader preload. * * @throws CacheException if there are startup problems */ void start() throws CacheException; /** * Lifecycle method that stops the cache, including replication, * clustering, cache loading, notifications, etc., and clears all cache in-memory state. *

        * State can be reconstituted by using either a cache loader or state transfer when the cache starts again. */ void stop(); /** * Lifecycle method that destroys the cache and removes any interceptors/configuration elements. * Cache can then be restarted (potentially after reconfiguring) using {@link #create()} and {@link #start()}. */ void destroy(); /** * Gets where the cache currently is its lifecycle transitions. * * @return the CacheStatus. Will not return null. */ CacheStatus getCacheStatus(); /** * @return the current invocation context for the current invocation and cache instance. * @throws IllegalStateException if the cache has been destroyed. */ InvocationContext getInvocationContext(); /** * Sets the passed in {@link org.jboss.cache.InvocationContext} as current. * * @param ctx invocation context to use * @throws IllegalStateException if the cache has been destroyed. */ void setInvocationContext(InvocationContext ctx); /** * Returns the local address of this cache in a cluster, or null * if running in local mode. * * @return the local address of this cache in a cluster, or null * if running in local mode. */ Address getLocalAddress(); /** * Returns a list of members in the cluster, or null * if running in local mode. * * @return a {@link List} of members in the cluster, or null * if running in local mode. */ List

        getMembers(); /** * Moves a part of the cache to a different subtree. *

        * E.g.: *

        * assume a cache structure such as: *

        *

            *  /a/b/c
            *  /a/b/d
            *  /a/b/e
            * 

        *

        * Fqn f1 = Fqn.fromString("/a/b/c"); * Fqn f2 = Fqn.fromString("/a/b/d"); *

        * cache.move(f1, f2); *

        *

        * Will result in: *

            * 

        * /a/b/d/c * /a/b/e *

        *

        *

        * and now *

        *

            *  Fqn f3 = Fqn.fromString("/a/b/e");
            *  Fqn f4 = Fqn.fromString("/a");
            *  cache.move(f3, f4);
            * 
        *

        * will result in: *

            * /a/b/d/c
            * /a/e
            * 
        * No-op if the node to be moved is the root node. * * @param nodeToMove the Fqn of the node to move. * @param newParent new location under which to attach the node being moved. * @throws NodeNotExistsException may throw one of these if the target node does not exist or if a different thread has moved this node elsewhere already. * @throws IllegalStateException if {@link #getCacheStatus()} would not return {@link CacheStatus#STARTED}. */ void move(Fqn nodeToMove, Fqn newParent) throws NodeNotExistsException; /** * Convenience method that takes in string representations of Fqns. Otherwise identical to {@link #move(Fqn, Fqn)} * * @throws IllegalStateException if {@link #getCacheStatus()} would not return {@link CacheStatus#STARTED}. */ void move(String nodeToMove, String newParent) throws NodeNotExistsException; /** * Returns the version of the cache as a string. * * @return the version string of the cache. * @see Version#printVersion */ String getVersion(); /** * Retrieves a defensively copied data map of the underlying node. A convenience method to retrieving a node and * getting data from the node directly. * * @param fqn * @return map of data, or an empty map * @throws CacheException * @throws IllegalStateException if {@link #getCacheStatus()} would not return {@link CacheStatus#STARTED}. */ Map getData(Fqn fqn); /** * Convenience method that takes in a String represenation of the Fqn. Otherwise identical to {@link #getKeys(Fqn)}. */ Set getKeys(String fqn); /** * Returns a set of attribute keys for the Fqn. * Returns null if the node is not found, otherwise a Set. * The set is a copy of the actual keys for this node. *

        * A convenience method to retrieving a node and * getting keys from the node directly. * * @param fqn name of the node * @throws IllegalStateException if {@link #getCacheStatus()} would not return {@link CacheStatus#STARTED}. */ Set getKeys(Fqn fqn); /** * Convenience method that takes in a String represenation of the Fqn. Otherwise identical to {@link #clearData(Fqn)}. * * @throws IllegalStateException if {@link #getCacheStatus()} would not return {@link CacheStatus#STARTED}. */ void clearData(String fqn); /** * Removes the keys and properties from a named node. *

        * A convenience method to retrieving a node and * getting keys from the node directly. * * @param fqn name of the node * @throws IllegalStateException if {@link #getCacheStatus()} would not return {@link CacheStatus#STARTED}. */ void clearData(Fqn fqn); } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/optimistic/0000755000175000017500000000000011376174006024572 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/optimistic/WorkspaceNode.java0000644000175000017500000001441710763550161030207 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.optimistic; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.Node; import org.jboss.cache.NodeSPI; import java.util.List; import java.util.Map; import java.util.Set; /** * Represents a type of {@link org.jboss.cache.Node} that is to be copied into a {@link TransactionWorkspace} for optimistically locked * nodes. Adds versioning and dirty flags over conventional Nodes. *

        * Typically used when the cache mode configured is {@link org.jboss.cache.config.Configuration.NodeLockingScheme#OPTIMISTIC} * * @author Manik Surtani (manik@jboss.org) * @author Steve Woodcock (stevew@jofti.com) * @since 1.3.0 */ public interface WorkspaceNode extends Node { /** * Returns 2 Sets - a set of children added (first set) and a set of children removed. * * @return a merged map of child names and Nodes */ List> getMergedChildren(); /** * Retrieves the data version of the in-memory node. * * @return A data version */ DataVersion getVersion(); /** * Sets the data version of this workspace node. * * @param version a {@link org.jboss.cache.optimistic.DataVersion} implementation. */ void setVersion(DataVersion version); /** * A node is considered modified if it's data map has changed. If children are added or removed, the node is not * considered modified - instead, see {@link #isChildrenModified()}. * * @return true if the data map has been modified */ boolean isModified(); /** * A convenience method that returns whether a node is dirty, i.e., it has been created, deleted or modified. * Noe that adding or removing children does not constitute a node being dirty - see {@link #isChildrenModified()} * * @return true if the node has been either created, deleted or modified. */ boolean isDirty(); /** * Attempts to merge data changed during the current transaction with the data in the underlying tree. * * @return a merged map of key/value pairs. */ Map getMergedData(); /** * Retrieves a reference to the underlying {@link NodeSPI} instance. * * @return a node */ NodeSPI getNode(); /** * Retrieves a TransactionWorkspace instance associated with the current transaction, which the current WorkspaceNode instance * lives in. * * @return a TransactionWorkspace. */ TransactionWorkspace getTransactionWorkspace(); /** * @return true if the instance was created in the current transaction; i.e., it did not exist in the underlying data tree. */ boolean isCreated(); /** * Marks the instance as having been created in the current transaction. */ void markAsCreated(); /** * Creates a child node. * * @param child_name Object name of the child to create * @param parent A reference to the parent node * @param cache CacheSPI instance to create this node in * @param version DataVersion to apply to the child. If null, {@link DefaultDataVersion#ZERO} will be used. * @return a NodeSPI pointing to the newly created child. */ NodeSPI createChild(Object child_name, NodeSPI parent, CacheSPI cache, DataVersion version); /** * Tests whether versioning for the WorkspaceNode instance in the current transaction is implicit (i.e., using {@link org.jboss.cache.optimistic.DefaultDataVersion} * rather than a custom {@link org.jboss.cache.optimistic.DataVersion} passed in using {@link org.jboss.cache.config.Option#setDataVersion(DataVersion)}) * * @return true if versioning is implicit. */ boolean isVersioningImplicit(); /** * Sets whether versioning for the WorkspaceNode instance in the current transaction is implicit (i.e., using {@link org.jboss.cache.optimistic.DefaultDataVersion} * rather than a custom {@link org.jboss.cache.optimistic.DataVersion} passed in using {@link org.jboss.cache.config.Option#setDataVersion(DataVersion)}) * * @param b set to true if versioning is implicit, false otherwise. */ void setVersioningImplicit(boolean b); /** * @return true if the instance has been deleted in the current transaction. */ boolean isDeleted(); /** * Marks the node as being deleted (or not) in the current transaction. This is not recursive, child nodes are not affected. * * @param marker true if the node has been deleted, false if not. */ void markAsDeleted(boolean marker); /** * Same as {@link #markAsDeleted(boolean)} except that the option to recurse into children is provided. * * @param marker true if the node has been deleted, false if not. * @param recursive if true, child nodes (and their children) are marked as well. */ void markAsDeleted(boolean marker, boolean recursive); /** * Overrides {@link Node#getChild(Object)} to return a {@link org.jboss.cache.NodeSPI} rather than a {@link org.jboss.cache.Node} * * @param o node name * @return a child node */ NodeSPI getChild(Object o); /** * Overrides {@link Node#getChild(Fqn)} to return a {@link org.jboss.cache.NodeSPI} rather than a {@link org.jboss.cache.Node} * * @param f node fqn * @return a child node */ NodeSPI getChild(Fqn f); /** * Adds a given WorkspaceNode to the current node's child map * * @param workspaceNode */ void addChild(WorkspaceNode workspaceNode); /** * @return true if children have been added to or removed from this node. Not the same as 'dirty'. */ boolean isChildrenModified(); /** * @return true if the child map has been loaded from the underlying data structure into the workspace. */ boolean isChildrenLoaded(); /** * @return teur if this node has been resurrected, i.e., it was deleted and re-added within the same transaction */ boolean isResurrected(); /** * Marks a node as resurrected, i.e., deleted and created again within the same transaction * * @param resurrected */ void markAsResurrected(boolean resurrected); } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/optimistic/DataVersion.java0000644000175000017500000000230211005374170027642 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.optimistic; import java.io.Serializable; /** * When versioning data nodes in optimistic locking, a DataVersion is assigned * to each node. Versions need to implement the {@link #newerThan} method so * they can be compared during the validation phase upon commit. *

        * It is recommended that implementations implement {@link java.io.Externalizable} and make use * of a good marshalling/unmarshalling mechanism for the sake of efficiency, as these objects are * frequently serialized to be replicated across the wire. * * @author Manik Surtani (manik@jboss.org) */ public interface DataVersion extends Serializable { /** * Returns true if this is a newer version than other. There is no guarantee that the DataVersion passed * in is of the same implementation as the current instance. The implementation will have to check for this (if necessary) * and (if necessary) throw a {@link org.jboss.cache.optimistic.DataVersioningException}. */ boolean newerThan(DataVersion other); } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/optimistic/WorkspaceNodeImpl.java0000644000175000017500000003116211040106777031024 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.optimistic; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.AbstractNode; import static org.jboss.cache.AbstractNode.NodeFlags.*; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.Node; import org.jboss.cache.NodeFactory; import org.jboss.cache.NodeSPI; import org.jboss.cache.VersionedNode; import org.jboss.cache.invocation.NodeInvocationDelegate; import org.jboss.cache.transaction.GlobalTransaction; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; /** * Wraps an ordinary {@link Node} and adds versioning and other meta data to it. * * @author Manik Surtani (manik@jboss.org) * @author Steve Woodcock (stevew@jofti.com) */ public class WorkspaceNodeImpl extends AbstractNode implements WorkspaceNode { private static final Log log = LogFactory.getLog(WorkspaceNodeImpl.class); private static final boolean trace = log.isTraceEnabled(); private NodeSPI node; private TransactionWorkspace workspace; private DataVersion version = DefaultDataVersion.ZERO; private Map> optimisticChildNodeMap; private Set childrenAdded; private Set childrenRemoved; private Map optimisticDataMap; /** * Constructs with a node and workspace. */ public WorkspaceNodeImpl(NodeSPI node, TransactionWorkspace workspace) { NodeInvocationDelegate delegate = (NodeInvocationDelegate) node; if (!(delegate.getDelegationTarget() instanceof VersionedNode)) { throw new IllegalArgumentException("node " + delegate.getDelegationTarget() + " not VersionedNode"); } this.node = node; this.workspace = workspace; Map nodeData = node.getDataDirect(); if (!nodeData.isEmpty()) optimisticDataMap = new HashMap(nodeData); this.version = node.getVersion(); if (version == null) { throw new IllegalStateException("VersionedNode version null"); } initFlags(); } protected void initFlags() { setFlag(VERSIONING_IMPLICIT); } protected Set getChildrenAddedSet() { if (childrenAdded == null) childrenAdded = new HashSet(); return childrenAdded; } protected Set getChildrenRemovedSet() { if (childrenRemoved == null) childrenRemoved = new HashSet(); return childrenRemoved; } public boolean isChildrenModified() { return isFlagSet(CHILDREN_MODIFIED_IN_WORKSPACE); } public boolean isChildrenLoaded() { return optimisticChildNodeMap != null; } public boolean isResurrected() { return isFlagSet(RESURRECTED_IN_WORKSPACE); } public void markAsResurrected(boolean resurrected) { setFlag(RESURRECTED_IN_WORKSPACE, resurrected); } @Override public void markAsDeleted(boolean marker, boolean recursive) { super.markAsDeleted(marker, recursive); if (marker) { if (childrenAdded != null) childrenAdded.clear(); if (childrenRemoved != null) childrenRemoved.clear(); } } /** * Returns true if this node is modified. */ public boolean isModified() { return isFlagSet(MODIFIED_IN_WORKSPACE); } /** * A convenience method that returns whether a node is dirty, i.e., it has been created, deleted or modified. * * @return true if the node has been either created, deleted or modified. */ public boolean isDirty() { return isModified() || isCreated() || isDeleted(); } public Fqn getFqn() { return node.getFqn(); } public void putAll(Map data) { realPut(data, false); setFlag(MODIFIED_IN_WORKSPACE); } public void replaceAll(Map data) { clearData(); putAll(data); } public V put(K key, V value) { setFlag(MODIFIED_IN_WORKSPACE); if (optimisticDataMap == null) optimisticDataMap = new HashMap(); return optimisticDataMap.put(key, value); } public V remove(K key) { setFlag(MODIFIED_IN_WORKSPACE); if (optimisticDataMap == null) return null; return optimisticDataMap.remove(key); } public V get(K key) { return optimisticDataMap == null ? null : optimisticDataMap.get(key); } public Set getKeys() { if (optimisticDataMap == null) return Collections.emptySet(); return optimisticDataMap.keySet(); } //not able to delete from this public Set getChildrenNames() { if (optimisticChildNodeMap == null) { initialiseChildMap(); } Set names = new HashSet(optimisticChildNodeMap.keySet()); // invoke deltas if (childrenAdded != null) for (Fqn child : childrenAdded) names.add(child.getLastElement()); if (childrenRemoved != null) for (Fqn child : childrenRemoved) names.remove(child.getLastElement()); return names; } @SuppressWarnings("unchecked") private void initialiseChildMap() { Map> childrenMap = node.getChildrenMapDirect(); this.optimisticChildNodeMap = childrenMap == null ? new HashMap() : new HashMap(childrenMap); } private void realPut(Map data, boolean eraseData) { realPut(data, eraseData, true); } private void realPut(Map data, boolean eraseData, boolean forceDirtyFlag) { if (forceDirtyFlag) setFlag(MODIFIED_IN_WORKSPACE); if (eraseData && optimisticDataMap != null) { optimisticDataMap.clear(); } if (data != null) { if (optimisticDataMap == null) optimisticDataMap = new HashMap(); optimisticDataMap.putAll(data); } } public Node getParent() { return node.getParent(); } @SuppressWarnings("unchecked") public NodeSPI createChild(Object child_name, NodeSPI parent, CacheSPI cache, DataVersion version) { if (child_name == null) { return null; } NodeFactory factory = cache.getConfiguration().getRuntimeConfig().getNodeFactory(); NodeSPI child = (NodeSPI) factory.createNodeOfType(parent, child_name, parent, null); getChildrenAddedSet().add(child.getFqn()); if (childrenRemoved != null) childrenRemoved.remove(child.getFqn()); setFlag(CHILDREN_MODIFIED_IN_WORKSPACE); return child; } public boolean isVersioningImplicit() { return isFlagSet(VERSIONING_IMPLICIT); } public void setVersioningImplicit(boolean versioningImplicit) { setFlag(VERSIONING_IMPLICIT, versioningImplicit); } public NodeSPI getChild(Object childName) { //see if in the the transaction map // return optimisticChildNodeMap.get(childName); throw new UnsupportedOperationException("This call should be intercepted by the optimisticNodeInterceptor and the child node should be retrieved from the workspace."); } public NodeSPI getNode() { return node; } public DataVersion getVersion() { return version; } public void setVersion(DataVersion version) { this.version = version; } @SuppressWarnings("unchecked") public List> getMergedChildren() { List l = new ArrayList(2); if (childrenAdded != null) l.add(childrenAdded); else l.add(Collections.emptySet()); if (childrenRemoved != null) l.add(childrenRemoved); else l.add(Collections.emptySet()); return l; } public Map getMergedData() { if (optimisticDataMap == null) return Collections.emptyMap(); return optimisticDataMap; } public TransactionWorkspace getTransactionWorkspace() { return workspace; } public boolean isCreated() { return isFlagSet(CREATED_IN_WORKSPACE); } public void markAsCreated() { setFlag(CREATED_IN_WORKSPACE); // created != modified!!! } public Map getData() { if (optimisticDataMap == null) { return Collections.emptyMap(); } else { return Collections.unmodifiableMap(optimisticDataMap); } } @Override public String toString() { StringBuilder sb = new StringBuilder(); if (isDeleted()) sb.append("del "); if (isModified()) sb.append("modified "); if (isCreated()) sb.append("new "); return getClass().getSimpleName() + " [ fqn=" + getFqn() + " " + sb + "ver=" + version + " " + (isVersioningImplicit() ? "implicit" : "explicit") + "]"; } public Node addChild(Fqn f) { CacheSPI cache = getCache(); Node newNode = this; GlobalTransaction gtx = cache.getInvocationContext().getGlobalTransaction(); if (f.size() == 1) { newNode = createChild(f.get(0), node, getCache(), version); } else { // recursively create children Node currentParent = this; for (Object o : f.peekElements()) { if (currentParent instanceof WorkspaceNode) { newNode = ((WorkspaceNode) currentParent).getNode().getOrCreateChild(o, gtx); } else { if (currentParent instanceof WorkspaceNode) { newNode = ((WorkspaceNode) currentParent).getNode().getOrCreateChild(o, gtx); } else { newNode = ((NodeSPI) currentParent).getOrCreateChild(o, gtx); } } currentParent = newNode; } } return newNode; } public void addChild(WorkspaceNode child) { getChildrenAddedSet().add(child.getFqn()); if (childrenRemoved != null) childrenRemoved.remove(child.getFqn()); if (trace) log.trace("Adding child " + child.getFqn()); } public void clearData() { if (optimisticDataMap != null) { optimisticDataMap.clear(); setFlag(MODIFIED_IN_WORKSPACE); } } public int dataSize() { return optimisticDataMap == null ? 0 : optimisticDataMap.size(); } public boolean hasChild(Object o) { throw new UnsupportedOperationException(); } public boolean isValid() { throw new UnsupportedOperationException(); } public boolean isLockForChildInsertRemove() { throw new UnsupportedOperationException(); } public void setLockForChildInsertRemove(boolean lockForChildInsertRemove) { throw new UnsupportedOperationException(); } public void releaseObjectReferences(boolean recursive) { throw new UnsupportedOperationException(); } public NodeSPI getChild(Fqn f) { if (f.size() > 1) { throw new UnsupportedOperationException("Workspace node does not support fetching indirect children"); } return getChild(f.getLastElement()); } public Set> getChildren() { throw new UnsupportedOperationException(); } public boolean hasChild(Fqn f) { throw new UnsupportedOperationException(); } public NodeSPI getNodeSPI() { throw new UnsupportedOperationException("WorkspaceNode has no access to a NodeSPI"); } public V putIfAbsent(K k, V v) { throw new UnsupportedOperationException(); } public V replace(K key, V value) { throw new UnsupportedOperationException(); } public boolean replace(K key, V oldValue, V newValue) { throw new UnsupportedOperationException(); } public boolean removeChild(Fqn f) { if (f.size() > 1) throw new UnsupportedOperationException("Workspace nodes can only remove direct children!"); Object key = f.getLastElement(); return removeChild(key); } public boolean removeChild(Object childName) { //NodeSPI n = node.getChildDirect(childName); Fqn childFqn = Fqn.fromRelativeElements(getFqn(), childName); /*if (n != null) {*/ getChildrenRemovedSet().add(childFqn); if (childrenAdded != null) childrenAdded.remove(childFqn); setFlag(CHILDREN_MODIFIED_IN_WORKSPACE); return node.getChildDirect(childName) != null; /*} else { return false; }*/ } protected CacheSPI getCache() { return node.getCache(); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/optimistic/TransactionWorkspace.java0000755000175000017500000000445211005374170031602 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.optimistic; import org.jboss.cache.Fqn; import java.util.Map; import java.util.SortedMap; /** * Used to contain a copy of the tree being worked on within the scope of a given transaction. * Maintains {@link WorkspaceNode}s rather than conventional *

        * Also see {@link org.jboss.cache.transaction.OptimisticTransactionEntry}, which creates and maintains * an instance of TransactionWorkspace for each * transaction running. * * @author Manik Surtani (manik@jboss.org) * @author Steve Woodcock (stevew@jofti.com) */ public interface TransactionWorkspace { /** * @return Returns a map of {@link WorkspaceNode}s, keyed on {@link Fqn} */ Map> getNodes(); /** * @param nodes The nodes to set. Takes {@link WorkspaceNode}s. */ void setNodes(Map> nodes); WorkspaceNode getNode(Fqn fqn); /** * Is thread safe so you dont need to deal with synchronising access to this method. * * @param node */ void addNode(WorkspaceNode node); /** * Is thread safe so you dont need to deal with synchronising access to this method. */ WorkspaceNode removeNode(Fqn fqn); /** * Returns all nodes equal to or after the given node. */ SortedMap> getNodesAfter(Fqn fqn); /** * Tests if versioning is implicit for a given tx. * If set to true, the interceptor chain will handle versioning (implicit to JBossCache). * If set to false, DataVersions will have to come from the caller. */ boolean isVersioningImplicit(); /** * Sets if versioning is implicit for a given tx. * If set to true, the interceptor chain will handle versioning (implicit to JBossCache). * If set to false, DataVersions will have to come from the caller. */ void setVersioningImplicit(boolean versioningImplicit); /** * returns true if the workspace contains a node with specified Fqn */ boolean hasNode(Fqn fqn); /** * Empties the collection of nodes held by this workspace. */ void clearNodes(); } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/optimistic/DefaultDataVersion.java0000644000175000017500000000554311004125045031153 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.optimistic; /** * The default implementation of a DataVersion, uses a long to * compare versions. * This class is immutable. *

        * Also note that this is meant to control implicit, internal versioning. Do not attempt to instantiate or use instances * of this class explicitly, via the {@link org.jboss.cache.config.Option#setDataVersion(DataVersion)} API, as it WILL * break things. * * @author Manik Surtani (manik@jboss.org) */ public class DefaultDataVersion implements DataVersion { private static final long serialVersionUID = -6896315742831861046L; /** * Version zero. * Assign this as the first version to your data. */ public static final DataVersion ZERO = new DefaultDataVersion(0L); /** * Version one. */ private static final DataVersion ONE = new DefaultDataVersion(1L); /** * Version two. */ private static final DataVersion TWO = new DefaultDataVersion(2L); private long version; /** * Constructs with version 0. */ public DefaultDataVersion() { } /** * Constructs with a version number. */ public DefaultDataVersion(long version) { this.version = version; } /** * Returns a new DataVersion with a newer version number. */ public DataVersion increment() { if (this == ZERO) { return ONE; } if (this == ONE) { return TWO; } return new DefaultDataVersion(version + 1); } public boolean newerThan(DataVersion other) { if (other instanceof DefaultDataVersion) { DefaultDataVersion dvOther = (DefaultDataVersion) other; return version > dvOther.version; } else { // now try and swap things around to see if the other implementation knows how to compare against DefaultDataVersion. // could cause a problem if 'this' and 'other' have the same value, in which case other.newerThan() will return false // and this will return true, which is not strictly true at all. So we try calling other.equals() first to test // this, assuming that other will be able to effectively test equality if they are equal. return !other.equals(this) && !other.newerThan(this); } } @Override public String toString() { return "Ver=" + version; } @Override public boolean equals(Object other) { if (other instanceof DefaultDataVersion) { return version == ((DefaultDataVersion) other).version; } return false; } @Override public int hashCode() { return (int) version; } public long getRawVersion() { return version; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/optimistic/DataVersioningException.java0000644000175000017500000000112110537345073032226 0ustar twernertwernerpackage org.jboss.cache.optimistic; import org.jboss.cache.CacheException; /** * Denotes exceptions to do with data versioning in optimistic locking * * @author Manik Surtani * @since 1.4.1 */ public class DataVersioningException extends CacheException { private static final long serialVersionUID = 7234086816998964356L; public DataVersioningException() { } public DataVersioningException(String msg) { super(msg); } public DataVersioningException(String msg, Throwable cause) { super(msg, cause); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/optimistic/TransactionWorkspaceImpl.java0000755000175000017500000000413311014275236032423 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.optimistic; import org.jboss.cache.Fqn; import java.util.HashMap; import java.util.Map; import java.util.SortedMap; import java.util.TreeMap; /** * Contains a mapping of Fqn to {@link WorkspaceNode}s. * Each entry corresponds to a series of changed nodes within the transaction. * * @author Manik Surtani (manik@jboss.org) * @author Steve Woodcock (stevew@jofti.com) */ public class TransactionWorkspaceImpl implements TransactionWorkspace { private Map> nodes; private boolean versioningImplicit = true; public TransactionWorkspaceImpl() { nodes = new HashMap>(); } /** * Returns the nodes. */ public Map> getNodes() { return nodes; } public void clearNodes() { nodes.clear(); } /** * Sets the nodes. */ public void setNodes(Map> nodes) { this.nodes = nodes; } public WorkspaceNode getNode(Fqn fqn) { return nodes.get(fqn); } public void addNode(WorkspaceNode node) { nodes.put(node.getFqn(), node); } public WorkspaceNode removeNode(Fqn fqn) { return nodes.remove(fqn); } public SortedMap> getNodesAfter(Fqn fqn) { SortedMap> sm = new TreeMap>(); sm.putAll(nodes); return sm.tailMap(fqn); } public boolean isVersioningImplicit() { return versioningImplicit; } public void setVersioningImplicit(boolean versioningImplicit) { this.versioningImplicit = versioningImplicit; } public boolean hasNode(Fqn fqn) { return nodes.containsKey(fqn); } /** * Returns debug information. */ @Override public String toString() { return "Workspace nodes=" + nodes; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/loader/0000755000175000017500000000000011376174032023653 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/loader/LocalDelegatingCacheLoader.java0000755000175000017500000001106511014275262031611 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.loader; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.Node; import org.jboss.cache.NodeSPI; import org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.Collections; import java.util.Map; import java.util.Set; /** * DelegatingCacheLoader implementation which delegates to a local (in the same VM) CacheImpl. Sample code: *

         * CacheImpl firstLevel=new CacheImpl();
         * CacheImpl secondLevel=new CacheImpl();
         * DelegatingCacheLoader l=new DelegatingCacheLoader(secondLevel);
         * l.setCache(firstLevel);
         * firstLevel.setCacheLoader(l);
         * secondLevel.start();
         * firstLevel.start();
         * 
        * * @author Bela Ban * @author Daniel Gredler * @version $Id: LocalDelegatingCacheLoader.java 5874 2008-05-19 13:03:46Z manik.surtani@jboss.com $ */ public class LocalDelegatingCacheLoader extends AbstractCacheLoader { IndividualCacheLoaderConfig config; CacheSPI delegate = null; public void setConfig(IndividualCacheLoaderConfig config) { this.config = config; if (config instanceof LocalDelegatingCacheLoaderConfig) { delegate = (CacheSPI) ((LocalDelegatingCacheLoaderConfig) config).getDelegate(); } } public IndividualCacheLoaderConfig getConfig() { return config; } public Set getChildrenNames(Fqn fqn) throws Exception { Node node = delegate.getRoot().getChild(fqn); if (node == null) return null; Set cn = node.getChildrenNames(); // the cache loader contract is a bit different from the cache when it comes to dealing with childrenNames if (cn.isEmpty()) return null; return cn; } public Map get(Fqn name) throws Exception { NodeSPI n = (NodeSPI) delegate.getRoot().getChild(name); if (n == null) return null; // after this stage we know that the node exists. So never return a null - at worst, an empty map. Map m = n.getData(); if (m == null) m = Collections.emptyMap(); return m; } public boolean exists(Fqn name) throws Exception { return delegate.getNode(name) != null; } public Object put(Fqn name, Object key, Object value) throws Exception { return delegate.put(name, key, value); } public void put(Fqn name, Map attributes) throws Exception { delegate.put(name, attributes); } public Object remove(Fqn fqn, Object key) throws Exception { return delegate.remove(fqn, key); } public void remove(Fqn fqn) throws Exception { delegate.removeNode(fqn); } public void removeData(Fqn fqn) throws Exception { Node node = delegate.getRoot().getChild(fqn); if (node != null) node.clearData(); } protected void setDelegateCache(CacheSPI delegate) { this.delegate = delegate; } @Override public void loadEntireState(ObjectOutputStream os) throws Exception { try { // // We use the lock acquisition timeout rather than the // // state transfer timeout, otherwise we'd never try // // to break locks before the requesting node gives up // return cache._getState(Fqn.fromString(SEPARATOR), // cache.getLockAcquisitionTimeout(), // true, // false); // Until flush is in place, use the old mechanism // where we wait the full state retrieval timeout delegate.getStateTransferManager().getState(os, Fqn.ROOT, delegate.getConfiguration().getStateRetrievalTimeout(), true, false); } catch (Exception e) { throw e; } catch (Throwable t) { throw new RuntimeException("Caught exception getting state from delegate", t); } } @Override public void loadState(Fqn subtree, ObjectOutputStream os) throws Exception { throw new UnsupportedOperationException("setting and loading state for specific Fqns not supported"); } @Override public void storeEntireState(ObjectInputStream is) throws Exception { delegate.getStateTransferManager().setState(is, Fqn.ROOT); } @Override public void storeState(Fqn subtree, ObjectInputStream is) throws Exception { throw new UnsupportedOperationException("setting and loading state for specific Fqns not supported"); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/loader/TcpDelegatingCacheLoader.java0000644000175000017500000003040611032145203031271 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.loader; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.CacheException; import org.jboss.cache.Fqn; import org.jboss.cache.Modification; import org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig; import org.jboss.cache.loader.tcp.TcpCacheOperations; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.Socket; import java.util.List; import java.util.Map; import java.util.Set; /** * DelegatingCacheLoader implementation which delegates to a remote (not in the same VM) * CacheImpl using TCP/IP for communication. Example configuration for connecting to a TcpCacheServer * running at myHost:12345:
         * org.jboss.cache.loader.TcpDelegatingCacheLoader
         * 
         * host=localhost
         * port=2099
         * 
         * 
        * * @author Bela Ban * @version $Id: TcpDelegatingCacheLoader.java 6120 2008-06-30 11:58:59Z manik.surtani@jboss.com $ */ public class TcpDelegatingCacheLoader extends AbstractCacheLoader { private Socket sock; private TcpDelegatingCacheLoaderConfig config; ObjectInputStream in; ObjectOutputStream out; private static final Log log = LogFactory.getLog(TcpDelegatingCacheLoader.class); private static Method GET_CHILDREN_METHOD, GET_METHOD, PUT_KEY_METHOD, PUT_DATA_METHOD, REMOVE_KEY_METHOD, REMOVE_METHOD, PUT_MODS_METHOD, EXISTS_METHOD, REMOVE_DATA_METHOD; static { try { GET_CHILDREN_METHOD = TcpDelegatingCacheLoader.class.getDeclaredMethod("_getChildrenNames", Fqn.class); GET_METHOD = TcpDelegatingCacheLoader.class.getDeclaredMethod("_get", Fqn.class); EXISTS_METHOD = TcpDelegatingCacheLoader.class.getDeclaredMethod("_exists", Fqn.class); PUT_KEY_METHOD = TcpDelegatingCacheLoader.class.getDeclaredMethod("_put", Fqn.class, Object.class, Object.class); PUT_DATA_METHOD = TcpDelegatingCacheLoader.class.getDeclaredMethod("_put", Fqn.class, Map.class); REMOVE_KEY_METHOD = TcpDelegatingCacheLoader.class.getDeclaredMethod("_remove", Fqn.class, Object.class); REMOVE_DATA_METHOD = TcpDelegatingCacheLoader.class.getDeclaredMethod("_removeData", Fqn.class); REMOVE_METHOD = TcpDelegatingCacheLoader.class.getDeclaredMethod("_remove", Fqn.class); PUT_MODS_METHOD = TcpDelegatingCacheLoader.class.getDeclaredMethod("_put", List.class); } catch (Exception e) { log.fatal("Unable to initialise reflection methods", e); } } /** * Allows configuration via XML config file. */ public void setConfig(IndividualCacheLoaderConfig base) { if (base instanceof TcpDelegatingCacheLoaderConfig) { this.config = (TcpDelegatingCacheLoaderConfig) base; } else { config = new TcpDelegatingCacheLoaderConfig(base); } } public IndividualCacheLoaderConfig getConfig() { return config; } /** * Invokes the specified Method with the specified parameters, catching SocketExceptions and attempting to reconnect * to the TcpCacheServer if necessary. * * @param m method to invoke * @param params parameters * @return method return value */ protected Object invokeWithRetries(Method m, Object... params) { long endTime = System.currentTimeMillis() + config.getTimeout(); do { try { return m.invoke(this, params); } catch (IllegalAccessException e) { log.error("Should never get here!", e); } catch (InvocationTargetException e) { if (e.getCause() instanceof IOException) { try { // sleep 250 ms if (log.isDebugEnabled()) log.debug("Caught IOException. Retrying.", e); Thread.sleep(config.getReconnectWaitTime()); restart(); } catch (IOException e1) { // IOException starting; sleep a bit and retry } catch (InterruptedException e1) { // do nothing } } else { throw new CacheException("Problems invoking method call!", e); } } } while (System.currentTimeMillis() < endTime); throw new CacheException("Unable to communicate with TCPCacheServer(" + config.getHost() + ":" + config.getPort() + ") after " + config.getTimeout() + " millis, with reconnects every " + config.getReconnectWaitTime() + " millis."); } // ------------------ CacheLoader interface methods, which delegate to retry-aware methods public Set getChildrenNames(Fqn fqn) throws Exception { return (Set) invokeWithRetries(GET_CHILDREN_METHOD, fqn); } public Map get(Fqn name) throws Exception { return (Map) invokeWithRetries(GET_METHOD, name); } public boolean exists(Fqn name) throws Exception { return (Boolean) invokeWithRetries(EXISTS_METHOD, name); } public Object put(Fqn name, Object key, Object value) throws Exception { return invokeWithRetries(PUT_KEY_METHOD, name, key, value); } public void put(Fqn name, Map attributes) throws Exception { invokeWithRetries(PUT_DATA_METHOD, name, attributes); } @Override public void put(List modifications) throws Exception { invokeWithRetries(PUT_MODS_METHOD, modifications); } public Object remove(Fqn fqn, Object key) throws Exception { return invokeWithRetries(REMOVE_KEY_METHOD, fqn, key); } public void remove(Fqn fqn) throws Exception { invokeWithRetries(REMOVE_METHOD, fqn); } public void removeData(Fqn fqn) throws Exception { invokeWithRetries(REMOVE_DATA_METHOD, fqn); } // ------------------ Retry-aware CacheLoader interface method counterparts protected Set _getChildrenNames(Fqn fqn) throws Exception { Set cn; synchronized (out) { out.reset(); out.writeByte(TcpCacheOperations.GET_CHILDREN_NAMES); out.writeObject(fqn); out.flush(); Object retval = in.readObject(); if (retval instanceof Exception) { throw (Exception) retval; } cn = (Set) retval; } // the cache loader contract is a bit different from the cache when it comes to dealing with childrenNames if (cn.isEmpty()) return null; return cn; } protected Map _get(Fqn name) throws Exception { synchronized (out) { out.reset(); out.writeByte(TcpCacheOperations.GET); out.writeObject(name); out.flush(); Object retval = in.readObject(); if (retval instanceof Exception) { throw (Exception) retval; } return (Map) retval; } } protected boolean _exists(Fqn name) throws Exception { synchronized (out) { out.reset(); out.writeByte(TcpCacheOperations.EXISTS); out.writeObject(name); out.flush(); Object retval = in.readObject(); if (retval instanceof Exception) { throw (Exception) retval; } return (Boolean) retval; } } protected Object _put(Fqn name, Object key, Object value) throws Exception { synchronized (out) { out.reset(); out.writeByte(TcpCacheOperations.PUT_KEY_VAL); out.writeObject(name); out.writeObject(key); out.writeObject(value); out.flush(); Object retval = in.readObject(); if (retval instanceof Exception) { throw (Exception) retval; } return retval; } } protected void _put(Fqn name, Map attributes) throws Exception { synchronized (out) { out.reset(); out.writeByte(TcpCacheOperations.PUT); out.writeObject(name); out.writeObject(attributes); out.flush(); Object retval = in.readObject(); if (retval instanceof Exception) { throw (Exception) retval; } } } protected void _put(List modifications) throws Exception { synchronized (out) { out.reset(); out.writeByte(TcpCacheOperations.PUT_LIST); int length = modifications.size(); out.writeInt(length); for (Modification m : modifications) { m.writeExternal(out); } out.flush(); Object retval = in.readObject(); if (retval instanceof Exception) { throw (Exception) retval; } } } protected Object _remove(Fqn fqn, Object key) throws Exception { synchronized (out) { out.reset(); out.writeByte(TcpCacheOperations.REMOVE_KEY); out.writeObject(fqn); out.writeObject(key); out.flush(); Object retval = in.readObject(); if (retval instanceof Exception) { throw (Exception) retval; } return retval; } } protected void _remove(Fqn fqn) throws Exception { synchronized (out) { out.reset(); out.writeByte(TcpCacheOperations.REMOVE); out.writeObject(fqn); out.flush(); Object retval = in.readObject(); if (retval instanceof Exception) { throw (Exception) retval; } } } protected void _removeData(Fqn fqn) throws Exception { synchronized (out) { out.reset(); out.writeByte(TcpCacheOperations.REMOVE_DATA); out.writeObject(fqn); out.flush(); Object retval = in.readObject(); if (retval instanceof Exception) { throw (Exception) retval; } } } // ----------------- Lifecycle and no-op methods @Override public void start() throws IOException { sock = new Socket(config.getHost(), config.getPort()); out = new ObjectOutputStream(new BufferedOutputStream(sock.getOutputStream())); out.flush(); in = new ObjectInputStream(new BufferedInputStream(sock.getInputStream())); } @Override public void stop() { try { if (in != null) in.close(); } catch (IOException e) { if (log.isDebugEnabled()) log.debug("Unable to close TCP stream.", e); } try { if (out != null) out.close(); } catch (IOException e) { if (log.isDebugEnabled()) log.debug("Unable to close TCP stream.", e); } try { if (sock != null) sock.close(); } catch (IOException e) { if (log.isDebugEnabled()) log.debug("Unable to close socket.", e); } } protected void restart() throws IOException { stop(); start(); } @Override public void loadEntireState(ObjectOutputStream os) throws Exception { throw new UnsupportedOperationException("operation is not currently supported - need to define semantics first"); } @Override public void loadState(Fqn subtree, ObjectOutputStream os) throws Exception { throw new UnsupportedOperationException("operation is not currently supported - need to define semantics first"); } @Override public void storeEntireState(ObjectInputStream is) throws Exception { throw new UnsupportedOperationException("operation is not currently supported - need to define semantics first"); } @Override public void storeState(Fqn subtree, ObjectInputStream is) throws Exception { throw new UnsupportedOperationException("operation is not currently supported - need to define semantics first"); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/loader/AdjListJDBCCacheLoader.java0000644000175000017500000005203511130032064030535 0ustar twernertwernerpackage org.jboss.cache.loader; import net.jcip.annotations.ThreadSafe; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.Fqn; import org.jboss.cache.Modification; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.lock.StripedLock; import org.jboss.cache.util.Util; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.sql.Types; import java.util.AbstractMap; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Properties; import java.util.Set; /** * Adjacency List Model is the model of persisting trees in which each children holds a reference to its parent. * An alternative model is the Nested Set Model (a.k.a. Modified Preorder Model) - this approach adds some additional * indexing information to each persisted node. This indexing info is further used for optimizing operations like * subtree loading, deleting etc. The indexes are update for each insertion. *

        * Adjacency List Model proved more performance-effective for the following reason: the entire path is persisted rather * than only a reference to parent. Looking up nodes heavily relies on that, and the performance is similar as in the * case of Modified Preorder Model. Even more there is no costly update indexes operation. * * @author Mircea.Markus@iquestint.com * @author Galder Zamarreno * @version 1.0 */ @ThreadSafe public abstract class AdjListJDBCCacheLoader extends AbstractCacheLoader { protected ConnectionFactory cf; protected String driverName; private AdjListJDBCCacheLoaderConfig config; protected StripedLock lock = new StripedLock(); protected static Log log = LogFactory.getLog(AdjListJDBCCacheLoader.class); public void setConfig(CacheLoaderConfig.IndividualCacheLoaderConfig base) { config = processConfig(base); if (config.getDatasourceName() == null) { try { /* Instantiate an standalone connection factory as per configuration, either explicitly defined or the default one */ log.debug("Initialising with a connection factory since data source is not provided."); if (log.isDebugEnabled()) log.debug("Using connection factory " + config.getConnectionFactoryClass()); cf = (ConnectionFactory) Util.loadClass(config.getConnectionFactoryClass()).newInstance(); } catch (Exception e) { log.error("Connection factory class could not be loaded", e); throw new IllegalStateException("Connection factory class could not be loaded", e); } } else { /* We create the ManagedConnectionFactory instance but the JNDI lookup is no done until the start method is called, since that's when its registered in its lifecycle */ cf = new ManagedConnectionFactory(); } /* Regardless of the type of connection factory, we set the configuration */ cf.setConfig(config); } /** * Returns a map representing a node. * * @param name node's fqn * @return node * @throws Exception */ public Map get(Fqn name) throws Exception { lock.acquireLock(name, false); try { final Map node = loadNode(name); return node == NULL_NODE_IN_ROW ? new HashMap(0) : node; } finally { lock.releaseLock(name); } } /** * Fetches child node names (not pathes). * * @param fqn parent fqn * @return a set of child node names or null if there are not children found for the fqn * @throws Exception */ public Set getChildrenNames(Fqn fqn) throws Exception { Set children = null; Connection con = null; PreparedStatement ps = null; ResultSet rs = null; try { if (log.isDebugEnabled()) { log.debug("executing sql: " + config.getSelectChildNamesSql() + " (" + fqn + ")"); } con = cf.getConnection(); ps = con.prepareStatement(config.getSelectChildNamesSql()); ps.setString(1, fqn.toString()); lock.acquireLock(fqn, false); rs = ps.executeQuery(); if (rs.next()) { children = new HashSet(); do { String child = rs.getString(1); int slashInd = child.lastIndexOf('/'); String name = child.substring(slashInd + 1); //Fqn childFqn = Fqn.fromString(child); //String name = (String) childFqn.get(childFqn.size() - 1); children.add(name); } while (rs.next()); } } catch (SQLException e) { reportAndRethrowError("Failed to get children names for fqn " + fqn, e); } finally { safeClose(rs); safeClose(ps); cf.close(con); lock.releaseLock(fqn); } return children == null ? null : Collections.unmodifiableSet(children); } /** * Nullifies the node. * * @param name node's fqn * @throws Exception */ public void removeData(Fqn name) throws Exception { updateNode(name, null); } /** * First phase in transaction commit process. The changes are committed if only one phase if requested. * All the modifications are committed using the same connection. * * @param tx something representing transaction * @param modifications a list of modifications * @param one_phase indicates whether it's one or two phase commit transaction * @throws Exception */ @Override public void prepare(Object tx, List modifications, boolean one_phase) throws Exception { // start a tx cf.prepare(tx); put(modifications); // commit if it's one phase only if (one_phase) commit(tx); } /** * Commits a transaction. * * @param tx the tx to commit * @throws Exception */ @Override public void commit(Object tx) throws Exception { cf.commit(tx); } /** * Rolls back a transaction. * * @param tx the tx to rollback */ @Override public void rollback(Object tx) { cf.rollback(tx); } // Service implementation @Override public void start() throws Exception { cf.start(); Connection con = null; Statement st = null; try { con = cf.getConnection(); driverName = getDriverName(con); if (config.getCreateTable() && !tableExists(config.getTable(), con)) { if (log.isDebugEnabled()) { log.debug("executing ddl: " + config.getCreateTableDDL()); } st = con.createStatement(); st.executeUpdate(config.getCreateTableDDL()); } } finally { safeClose(st); cf.close(con); } createDummyTableIfNeeded(); } private void createDummyTableIfNeeded() throws Exception { Connection conn = null; PreparedStatement ps = null; try { conn = cf.getConnection(); ps = conn.prepareStatement(config.getDummyTableRemovalDDL()); ps.execute(); } catch (Exception e) { // ignore - it just means we didn't need to drop any database tables. } finally { safeClose(ps); cf.close(conn); } try { conn = cf.getConnection(); ps = conn.prepareStatement(config.getDummyTableCreationDDL()); ps.execute(); safeClose(ps); ps = conn.prepareStatement(config.getDummyTablePopulationSql()); ps.execute(); } finally { safeClose(ps); cf.close(conn); } } @Override public void stop() { try { if (config.getDropTable()) { Connection con = null; Statement st = null; try { if (log.isDebugEnabled()) { log.debug("executing ddl: " + config.getDropTableDDL()); } con = cf.getConnection(); st = con.createStatement(); st.executeUpdate(config.getDropTableDDL()); safeClose(st); } catch (SQLException e) { log.error("Failed to drop table: " + e.getMessage(), e); } finally { safeClose(st); cf.close(con); } } } finally { cf.stop(); } } /** * Checks that there is a row for the fqn in the database. * * @param name node's fqn * @return true if there is a row in the database for the given fqn even if the node column is null. * @throws Exception */ public boolean exists(Fqn name) throws Exception { lock.acquireLock(name, false); Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; try { conn = cf.getConnection(); ps = conn.prepareStatement(config.getExistsSql()); ps.setString(1, name.toString()); rs = ps.executeQuery(); return rs.next(); } finally { lock.releaseLock(name); safeClose(rs); safeClose(ps); cf.close(conn); } } /** * Removes attribute's value for a key. If after removal the node contains no attributes, the node is nullified. * * @param name node's name * @param key attribute's key * @return removed value or null if there was no value for the passed in key * @throws Exception */ public Object remove(Fqn name, Object key) throws Exception { lock.acquireLock(name, true); try { Object removedValue = null; Map node = loadNode(name); if (node != null && node != NULL_NODE_IN_ROW) { removedValue = node.remove(key); if (node.isEmpty()) { updateNode(name, null); } else { updateNode(name, node); } } return removedValue; } finally { lock.releaseLock(name); } } /** * Loads a node from the database. * * @param name the fqn * @return non-null Map representing the node, * null if there is no row with the fqn in the table, * NULL_NODE_IN_ROW if there is a row in the table with the fqn but the node column contains null. */ @SuppressWarnings("unchecked") protected Map loadNode(Fqn name) { boolean rowExists = false; Connection con = null; PreparedStatement ps = null; ResultSet rs = null; try { if (log.isDebugEnabled()) { log.debug("executing sql: " + config.getSelectNodeSql() + " (" + name + ")"); } con = cf.getConnection(); ps = con.prepareStatement(config.getSelectNodeSql()); ps.setString(1, name.toString()); rs = ps.executeQuery(); if (rs.next()) { rowExists = true; InputStream is = rs.getBinaryStream(1); if (is != null && !rs.wasNull()) { try { // deserialize result return (Map) unmarshall(is); } catch (Exception e) { throw new Exception("Unable to load to deserialize result: ", e); } finally { safeClose(is); } } } } catch (Exception e) { reportAndRethrowError("Failed to load node for fqn " + name, e); } finally { safeClose(rs); safeClose(ps); cf.close(con); } return rowExists ? NULL_NODE_IN_ROW : null; } /** * Inserts a node into the database * * @param name the fqn * @param node the node * @param rowMayExist if true, then this method will not be strict in testing for 1 row being inserted, since 0 may be inserted if the row already exists. */ protected void insertNode(Fqn name, Map node, boolean rowMayExist) { Connection con = null; PreparedStatement ps = null; try { if (log.isDebugEnabled()) { log.debug("executing sql: " + config.getInsertNodeSql() + " (" + name + ")"); } con = cf.getConnection(); ps = con.prepareStatement(config.getInsertNodeSql()); String fqnString = name.toString(); // the Fqn needs to be in the 1st and 4th places. ps.setString(1, fqnString); ps.setString(4, fqnString); if (node != null) { // ByteArrayOutputStream baos = new ByteArrayOutputStream(); // ObjectOutputStream oos = new ObjectOutputStream(baos); // oos.writeObject(node); byte[] byteStream = marshall(node); ByteArrayInputStream bais = new ByteArrayInputStream(byteStream); ps.setBinaryStream(2, bais, byteStream.length); } else { // a hack to handles the incomp. of SQL server jdbc driver prior to SQL SERVER 2005 if (driverName != null && (driverName.contains("SQLSERVER") || driverName.contains("POSTGRESQL") || driverName.contains("JCONNECT"))) { ps.setNull(2, Types.LONGVARBINARY); } else { ps.setNull(2, Types.BLOB); } //ps.setNull(2, Types.LONGVARBINARY); } if (name.size() == 0) { ps.setNull(3, Types.VARCHAR); } else { ps.setString(3, name.getAncestor(name.size() - 1).toString()); } int rows = ps.executeUpdate(); if (!rowMayExist && rows != 1) { throw new IllegalStateException("Expected one insert row but got " + rows); } } catch (RuntimeException e) { throw e; } catch (Exception e) { log.error("Failed to insert node :" + e.getMessage()); throw new IllegalStateException("Failed to insert node: " + e.getMessage(), e); } finally { safeClose(ps); cf.close(con); } } /** * Updates a node in the database. * * @param name the fqn * @param node new node value */ protected void updateNode(Fqn name, Map node) { Connection con = null; PreparedStatement ps = null; try { if (log.isDebugEnabled()) { log.debug("executing sql: " + config.getUpdateNodeSql()); } con = cf.getConnection(); ps = con.prepareStatement(config.getUpdateNodeSql()); if (node == null) { //ps.setNull(1, Types.BLOB); // ps.setNull(1, Types.LONGVARBINARY); // don't set it to null - simply use an empty hash map. node = new HashMap(0); } // ByteArrayOutputStream baos = new ByteArrayOutputStream(); // ObjectOutputStream oos = new ObjectOutputStream(baos); // oos.writeObject(node); byte[] byteStream = marshall(node); ByteArrayInputStream bais = new ByteArrayInputStream(byteStream); ps.setBinaryStream(1, bais, byteStream.length); ps.setString(2, name.toString()); /*int rows = */ ps.executeUpdate(); // if (rows != 1) // { // throw new IllegalStateException("Expected one updated row but got " + rows); // } } catch (Exception e) { reportAndRethrowError("Failed to update node for fqn " + name, e); } finally { safeClose(ps); cf.close(con); } } protected String getDriverName(Connection con) { if (con == null) return null; try { DatabaseMetaData dmd = con.getMetaData(); return toUpperCase(dmd.getDriverName()); } catch (SQLException e) { // This should not happen. A J2EE compatiable JDBC driver is // required to fully support metadata. throw new IllegalStateException("Error while getting the driver name", e); } } static String getRequiredProperty(Properties props, String name) { String value = props.getProperty(name); if (value == null) { throw new IllegalStateException("Missing required property: " + name); } return value; } protected boolean tableExists(String tableName, Connection con) { ResultSet rs = null; try { // (a j2ee spec compatible jdbc driver has to fully // implement the DatabaseMetaData) DatabaseMetaData dmd = con.getMetaData(); String catalog = con.getCatalog(); String schema = null; String quote = dmd.getIdentifierQuoteString(); if (tableName.startsWith(quote)) { if (!tableName.endsWith(quote)) { throw new IllegalStateException("Mismatched quote in table name: " + tableName); } int quoteLength = quote.length(); tableName = tableName.substring(quoteLength, tableName.length() - quoteLength); if (dmd.storesLowerCaseQuotedIdentifiers()) { tableName = toLowerCase(tableName); } else if (dmd.storesUpperCaseQuotedIdentifiers()) { tableName = toUpperCase(tableName); } } else { if (dmd.storesLowerCaseIdentifiers()) { tableName = toLowerCase(tableName); } else if (dmd.storesUpperCaseIdentifiers()) { tableName = toUpperCase(tableName); } } int dotIndex; if ((dotIndex = tableName.indexOf('.')) != -1) { // Yank out schema name ... schema = tableName.substring(0, dotIndex); tableName = tableName.substring(dotIndex + 1); } rs = dmd.getTables(catalog, schema, tableName, null); return rs.next(); } catch (SQLException e) { // This should not happen. A J2EE compatiable JDBC driver is // required fully support metadata. throw new IllegalStateException("Error while checking if table aleady exists " + tableName, e); } finally { safeClose(rs); } } protected abstract AdjListJDBCCacheLoaderConfig processConfig(CacheLoaderConfig.IndividualCacheLoaderConfig base); protected void reportAndRethrowError(String message, Exception cause) throws IllegalStateException { log.error(message, cause); throw new IllegalStateException(message, cause); } protected void safeClose(InputStream is) { if (is != null) { try { is.close(); } catch (IOException e) { log.warn("Failed to close input stream: " + e.getMessage()); } } } protected void safeClose(Statement st) { if (st != null) { try { st.close(); } catch (SQLException e) { log.warn("Failed to close statement: " + e.getMessage()); } } } protected void safeClose(ResultSet rs) { if (rs != null) { try { rs.close(); } catch (SQLException e) { log.warn("Failed to close result set: " + e.getMessage()); } } } protected Object unmarshall(InputStream from) throws Exception { return getMarshaller().objectFromStream(from); } protected byte[] marshall(Object obj) throws Exception { return getMarshaller().objectToByteBuffer(obj); } private static String toUpperCase(String s) { return s.toUpperCase(Locale.ENGLISH); } private static String toLowerCase(String s) { return s.toLowerCase((Locale.ENGLISH)); } // Inner protected static final Map NULL_NODE_IN_ROW = new AbstractMap() { @Override public Set> entrySet() { throw new UnsupportedOperationException(); } }; } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/loader/CacheLoader.java0000755000175000017500000003572510641242042026656 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.loader; import net.jcip.annotations.ThreadSafe; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.Modification; import org.jboss.cache.RegionManager; import org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.List; import java.util.Map; import java.util.Set; /** * A {@link org.jboss.cache.loader.CacheLoader} implementation persists and load keys to and from * secondary storage, such as a database or filesystem. Typically, * implementations store a series of keys and values (an entire {@link Map}) * under a single {@link Fqn}. Loading and saving properties of an entire * {@link Map} should be atomic. *

        * Lifecycle: First an instance of the loader is created, then the * configuration ({@link #setConfig(org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig)}) and cache ({@link * #setCache(CacheSPI)}) are set. After this, {@link #create()} is called. * Then {@link #start()} is called. When re-deployed, {@link #stop()} will be * called, followed by another {@link #start()}. Finally, when shut down, * {@link #destroy()} is called, after which the loader is unusable. *

        * An {@link org.jboss.cache.loader.AbstractCacheLoader} is provided as a convenient starting place * when implementing your own {@link org.jboss.cache.loader.CacheLoader}. *

        * It is important to note that all implementations are thread safe, as concurrent reads and writes, potentially even to * the same {@link Fqn}, are possible. *

        * * @author Manik Surtani (manik@jboss.org) * @see CacheSPI * @see org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig * @see org.jboss.cache.loader.AbstractCacheLoader * @since 2.0.0 */ @ThreadSafe public interface CacheLoader { /** * Sets the configuration. This is called before {@link #create()} and {@link #start()}. * * @param config May be an instance of the {@link org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig} base * class, in which case the cache loader should use the * {@link org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig#getProperties()} * method to find configuration information. Alternatively, * may be a type-specific subclass of {@link org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig}, * if there is one. */ void setConfig(IndividualCacheLoaderConfig config); /** * Gets the configuration. * * @return the configuration, represented by a {@link org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig} object. */ IndividualCacheLoaderConfig getConfig(); /** * Sets the {@link CacheSPI} that is maintaining this CacheLoader. * This method allows this CacheLoader to set a reference to the {@link CacheSPI}. * This method is called be called after the CacheLoader instance has been constructed. * * @param c The cache on which this loader works */ void setCache(CacheSPI c); /** * Returns a set of children node names. * All names are relative to this parent {@link Fqn}. * Returns null if the named node is not found or there are no children. * The returned set must not be modifiable. Implementors can use * {@link java.util.Collections#unmodifiableSet(java.util.Set)} to make the set unmodifiable. *

        * Implementors may impose restrictions on the contents of an Fqn (such as Strings-only) and as such, indirectly * impose the same restriction on the contents of a Set returned by getChildrenNames(). *

        * * @param fqn The {@link Fqn} of the parent * @return Set a set of children. Returns null if no children nodes are * present, or the parent is not present */ Set getChildrenNames(Fqn fqn) throws Exception; /** * Returns all keys and values from the persistent store, given a {@link org.jboss.cache.Fqn} * * @param name the {@link Fqn} to search for. * @return Map keys and values for the given node. Returns * null if the node is not found. If the node is found but has no * attributes, this method returns an empty Map. */ Map get(Fqn name) throws Exception; /** * Returns true if the CacheLoader has a node with a {@link Fqn}. * * @return true if node exists, false otherwise */ boolean exists(Fqn name) throws Exception; /** * Puts a key and value into the attribute map of a given node. If the * node does not exist, all parent nodes from the root down are created * automatically. Returns the old value. */ Object put(Fqn name, Object key, Object value) throws Exception; /** * Puts all entries of the map into the existing map of the given node, * overwriting existing keys, but not clearing the existing map before * insertion. * This is the same behavior as {@link Map#putAll}. * If the node does not exist, all parent nodes from the root down are created automatically * * @param name The fully qualified name of the node * @param attributes A Map of attributes. Can be null */ void put(Fqn name, Map attributes) throws Exception; /** * Applies all modifications to the backend store. * Changes may be applied in a single operation. * * @param modifications A List of modifications */ void put(List modifications) throws Exception; /** * Removes the given key and value from the attributes of the given node. * Does nothing if the node doesn't exist * Returns the removed value. */ Object remove(Fqn fqn, Object key) throws Exception; /** * Removes the given node and all its subnodes, does nothing if the node does not exist. * * @param fqn the {@link Fqn} of the node */ void remove(Fqn fqn) throws Exception; /** * Removes all attributes from a given node, but doesn't delete the node * itself or any subnodes. * * @param fqn the {@link Fqn} of the node */ void removeData(Fqn fqn) throws Exception; /** * Prepares a list of modifications. For example, for a DB-based CacheLoader: *

          *
        1. Create a local (JDBC) transaction *
        2. Associate the local transaction with tx (tx is the key) *
        3. Execute the corresponding SQL statements against the DB (statements derived from modifications) *
        * For non-transactional CacheLoader (e.g. file-based), the implementation could attempt to implement it's own transactional * logic, attempting to write data to a temp location (or memory) and writing it to the proper location upon commit. * * @param tx The transaction, indended to be used by implementations as an identifier of the transaction (and not necessarily a JTA {@link javax.transaction.Transaction} object) * @param modifications A {@link List} containing {@link org.jboss.cache.Modification}s, for the given transaction * @param one_phase Persist immediately and (for example) commit the local JDBC transaction as well. When true, * we won't get a {@link #commit(Object)} or {@link #rollback(Object)} method call later * @throws Exception */ void prepare(Object tx, List modifications, boolean one_phase) throws Exception; /** * Commits the transaction. A DB-based CacheLoader would look up the local * JDBC transaction asociated with tx and commit that * transaction. Non-transactional CacheLoaders could simply write the data * that was previously saved transiently under the given tx * key, to (for example) a file system. *

        * Note this only holds if the previous prepare() did not define

        one_phase=true
        * * @param tx transaction to commit */ void commit(Object tx) throws Exception; /** * Rolls the transaction back. A DB-based CacheLoader would look up the * local JDBC transaction asociated with tx and roll back that * transaction. * * @param tx transaction to roll back */ void rollback(Object tx); /** * Fetches the entire state for this cache from secondary storage (disk, database) * and writes it to a provided ObjectOutputStream. State written to the provided * ObjectOutputStream parameter is used for initialization of a new CacheImpl instance. * When the state gets transferred to the new cache instance its cacheloader calls * {@link #storeEntireState(ObjectInputStream)} *

        * Implementations of this method should not catch any exception or close the * given ObjectOutputStream parameter. In order to ensure cacheloader interoperability * contents of the cache are written to the ObjectOutputStream as a sequence of * NodeData objects. *

        * Default implementation is provided by {@link AbstractCacheLoader} and ensures cacheloader * interoperability. Implementors are encouraged to consider extending AbstractCacheLoader * prior to implementing completely custom cacheloader. * * @param os ObjectOutputStream to write state * @see AbstractCacheLoader#loadEntireState(ObjectOutputStream) * @see org.jboss.cache.marshall.NodeData */ void loadEntireState(ObjectOutputStream os) throws Exception; /** * Stores the entire state for this cache by reading it from a provided ObjectInputStream. * The state was provided to this cache by calling {@link #loadEntireState(ObjectOutputStream)}} * on some other cache instance. State currently in storage gets overwritten. *

        * Implementations of this method should not catch any exception or close the * given ObjectInputStream parameter. In order to ensure cacheloader interoperability * contents of the cache are read from the ObjectInputStream as a sequence of * NodeData objects. *

        * Default implementation is provided by {@link AbstractCacheLoader} and ensures cacheloader * interoperability. Implementors are encouraged to consider extending AbstractCacheLoader * prior to implementing completely custom cacheloader. * * @param is ObjectInputStream to read state * @see AbstractCacheLoader#storeEntireState(ObjectInputStream) * @see org.jboss.cache.marshall.NodeData */ void storeEntireState(ObjectInputStream is) throws Exception; /** * Fetches a portion of the state for this cache from secondary storage (disk, database) * and writes it to a provided ObjectOutputStream. State written to the provided * ObjectOutputStream parameter is used for activation of a portion of a new CacheImpl instance. * When the state gets transferred to the new cache instance its cacheloader calls * {@link #storeState(Fqn,ObjectInputStream)}. *

        * Implementations of this method should not catch any exception or close the * given ObjectOutputStream parameter. In order to ensure cacheloader interoperability * contents of the cache are written to the ObjectOutputStream as a sequence of * NodeData objects. *

        * Default implementation is provided by {@link AbstractCacheLoader} and ensures cacheloader * interoperability. Implementors are encouraged to consider extending AbstractCacheLoader * prior to implementing completely custom cacheloader. * * @param subtree Fqn naming the root (i.e. highest level parent) node of * the subtree for which state is requested. * @param os ObjectOutputStream to write state * @see AbstractCacheLoader#loadState(Fqn,ObjectOutputStream) * @see org.jboss.cache.Region#activate() * @see org.jboss.cache.marshall.NodeData */ void loadState(Fqn subtree, ObjectOutputStream os) throws Exception; /** * Stores the given portion of the cache tree's state in secondary storage. * Overwrite whatever is currently in secondary storage. If the transferred * state has Fqns equal to or children of parameter subtree, * then no special behavior is required. Otherwise, ensure that * the state is integrated under the given subtree. Typically * in the latter case subtree would be the Fqn of the buddy * backup region for * a buddy group; e.g. *

        * If the the transferred state had Fqns starting with "/a" and * subtree was "/_BUDDY_BACKUP_/192.168.1.2:5555" then the * state should be stored in the local persistent store under * "/_BUDDY_BACKUP_/192.168.1.2:5555/a" *

        * Implementations of this method should not catch any exception or close the * given ObjectInputStream parameter. In order to ensure cacheloader interoperability * contents of the cache are read from the ObjectInputStream as a sequence of * NodeData objects. *

        * Default implementation is provided by {@link AbstractCacheLoader} and ensures cacheloader * interoperability. Implementors are encouraged to consider extending AbstractCacheLoader * prior to implementing completely custom cacheloader. * * @param is ObjectInputStream to read state * @param subtree Fqn naming the root (i.e. highest level parent) node of * the subtree included in state. If the Fqns * of the data included in state are not * already children of subtree, then their * Fqns should be altered to make them children of * subtree before they are persisted. * @see AbstractCacheLoader#storeState(Fqn,ObjectInputStream) * @see org.jboss.cache.marshall.NodeData */ void storeState(Fqn subtree, ObjectInputStream is) throws Exception; /** * Sets the {@link org.jboss.cache.RegionManager} this object should use to manage * marshalling/unmarshalling of different regions using different * classloaders. *

        * NOTE: This method is only intended to be used * by the CacheSPI instance this cache loader is * associated with. *

        * * @param manager the region manager to use, or null. */ void setRegionManager(RegionManager manager); /** * Lifecycle method, called when the cache loader is created. * * @throws java.lang.Exception */ void create() throws java.lang.Exception; /** * Lifecycle method, called when the cache loader is started. * * @throws java.lang.Exception */ void start() throws java.lang.Exception; /** * Lifecycle method, called when the cache loader is stopped. */ void stop(); /** * Lifecycle method, called when the cache loader is destroyed. */ void destroy(); } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/loader/CacheLoaderAop.java0000755000175000017500000000200210222746130027276 0ustar twernertwernerpackage org.jboss.cache.loader; import org.jboss.cache.Fqn; /** * Responsible for storing and retrieving objects to/from secondary storage. * * @author Bela Ban Oct 31, 2003 * @version $Id: CacheLoaderAop.java 2 2005-03-31 10:09:28Z belaban $ */ public interface CacheLoaderAop extends CacheLoader { /** * Loads an object from a persistent store. * * @param name The key under which the object is stored * @return The object * @throws Exception Thrown if the object cannot be loaded */ Object loadObject(Fqn name) throws Exception; /** * Stores an object under a given key in the persistent store. If the object is already present, it will * be overwritten * * @param name * @param pojo * @throws Exception */ void storeObject(Fqn name, Object pojo) throws Exception; /** * Removes the object with the given key from the persistent store. * * @param name * @throws Exception */ void removeObject(Fqn name) throws Exception; } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/loader/s3/0000755000175000017500000000000011376174027024204 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/loader/s3/S3Exception.java0000644000175000017500000000126210770417262027212 0ustar twernertwernerpackage org.jboss.cache.loader.s3; import org.jboss.cache.CacheException; /** * Basic exception class. */ public class S3Exception extends CacheException { private static final long serialVersionUID = -5961236335942313217L; /** * Constructs a new S3Exception. */ public S3Exception() { } /** * Constructs a new S3Exception. */ public S3Exception(String arg0) { super(arg0); } /** * Constructs a new S3Exception. */ public S3Exception(Throwable arg0) { super(arg0); } /** * Constructs a new S3Exception. */ public S3Exception(String arg0, Throwable arg1) { super(arg0, arg1); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/loader/s3/S3LoaderConfig.java0000644000175000017500000001525511012441070027577 0ustar twernertwernerpackage org.jboss.cache.loader.s3; import java.lang.reflect.Field; import java.util.Properties; import net.noderunner.amazon.s3.Bucket; import net.noderunner.amazon.s3.CallingFormat; import net.noderunner.amazon.s3.Connection; import org.jboss.cache.CacheException; import org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig; /** * Amazon S3 loader configuration. * * @author Elias Ross */ public class S3LoaderConfig extends IndividualCacheLoaderConfig { private static final long serialVersionUID = 4626734068542420865L; private String accessKeyId; private String secretAccessKey; private boolean secure; private String server = Connection.DEFAULT_HOST; private int port; private transient Bucket bucket = new Bucket("jboss-cache"); private transient CallingFormat callingFormat = CallingFormat.SUBDOMAIN; private String location = Connection.LOCATION_DEFAULT; private boolean optimize = false; private boolean parentCache = true; public S3LoaderConfig() { setClassName(S3CacheLoader.class.getName()); } /** * For use by {@link S3CacheLoader}. * * @param base generic config object created by XML parsing. */ S3LoaderConfig(IndividualCacheLoaderConfig base) { setClassName(S3CacheLoader.class.getName()); populateFromBaseConfig(base); } /** * Returns a new connection. */ Connection getConnection() { return new Connection(getAccessKeyId(), getSecretAccessKey(), isSecure(), getServer(), getPort(), getCallingFormat()); } @Override public void setProperties(Properties props) { super.setProperties(props); if (props == null) return; setAccessKeyId(props.getProperty("cache.s3.accessKeyId")); setSecretAccessKey(props.getProperty("cache.s3.secretAccessKey")); setSecure(props.getProperty("cache.s3.secure")); setServer(props.getProperty("cache.s3.server", Connection.DEFAULT_HOST)); setPort(props.getProperty("cache.s3.port")); setBucket(props.getProperty("cache.s3.bucket")); setCallingFormat(props.getProperty("cache.s3.callingFormat")); setOptimize(props.getProperty("cache.s3.optimize")); setParentCache(props.getProperty("cache.s3.parentCache")); setLocation(props.getProperty("cache.s3.location")); } private void setLocation(String s) { if (s != null) this.location = s; } private void setParentCache(String s) { if (s != null) setParentCache(Boolean.valueOf(s)); } private void setOptimize(String s) { if (s != null) setOptimize(Boolean.valueOf(s)); } private void setCallingFormat(String s) { if (s != null) { try { Field field = CallingFormat.class.getDeclaredField(s); setCallingFormat((CallingFormat)field.get(null)); } catch (Exception e) { throw new CacheException(e); } } } private void setBucket(String s) { if (s != null) setBucket(new Bucket(s)); } private void setSecure(String s) { if (s != null) setSecure(Boolean.valueOf(s)); } private void setPort(String s) { if (s != null) setPort(Integer.parseInt(s)); else setPort(secure ? Connection.SECURE_PORT : Connection.INSECURE_PORT); } @Override public boolean equals(Object obj) { if (obj instanceof S3LoaderConfig && equalsExcludingProperties(obj)) { // TODO return this == obj; } return false; } @Override public int hashCode() { return 31 * hashCodeExcludingProperties(); // TODO } @Override public S3LoaderConfig clone() throws CloneNotSupportedException { return (S3LoaderConfig) super.clone(); } /** * Returns accessKeyId. */ public String getAccessKeyId() { return accessKeyId; } /** * Sets accessKeyId. */ public void setAccessKeyId(String accessKeyId) { this.accessKeyId = accessKeyId.trim(); } /** * Returns secretAccessKey. */ public String getSecretAccessKey() { return secretAccessKey.trim(); } /** * Sets secretAccessKey. */ public void setSecretAccessKey(String secretAccessKey) { this.secretAccessKey = secretAccessKey; } /** * Returns secure. */ public boolean isSecure() { return secure; } /** * Sets secure. */ public void setSecure(boolean secure) { this.secure = secure; } /** * Returns server. */ public String getServer() { return server; } /** * Sets server. */ public void setServer(String server) { this.server = server; } /** * Returns port. */ public int getPort() { return port; } /** * Sets port. */ public void setPort(int port) { this.port = port; } /** * Returns bucket. */ public Bucket getBucket() { return bucket; } /** * Sets bucket. */ public void setBucket(Bucket bucket) { this.bucket = bucket; } /** * Returns callingFormat. */ public CallingFormat getCallingFormat() { return callingFormat; } /** * Sets callingFormat. */ public void setCallingFormat(CallingFormat callingFormat) { this.callingFormat = callingFormat; } /** * Returns a debug string. */ @Override public String toString() { return super.toString() + " keyid=" + accessKeyId + " secret=" + "***" + " secure=" + secure + " server=" + server + " port=" + port + " bucket=" + bucket + " cf=" + callingFormat + " location=" + location; } /** * Returns true if certain CRUD operations are optimized to not do extra queries. * If true, then the behavior will be: *
          *
        • put(Fqn, Map) will always overwrite *
        • put(Fqn, Object, Object) will always overwrite, return null *
        • remove(Object) will return null *
        */ public boolean getOptimize() { return optimize; } /** * Sets optimize. */ public void setOptimize(boolean optimize) { this.optimize = optimize; } /** * Returns true if the existence of parent nodes should be cached. */ public boolean getParentCache() { return parentCache; } /** * Sets parentCache. */ public void setParentCache(boolean parentCache) { this.parentCache = parentCache; } /** * Returns location. */ public String getLocation() { return location; } }jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/loader/s3/S3CacheLoader.java0000644000175000017500000002733211006101071027371 0ustar twernertwernerpackage org.jboss.cache.loader.s3; import net.jcip.annotations.ThreadSafe; import net.noderunner.amazon.s3.Bucket; import net.noderunner.amazon.s3.Connection; import net.noderunner.amazon.s3.Entry; import net.noderunner.amazon.s3.GetStreamResponse; import net.noderunner.amazon.s3.ListResponse; import net.noderunner.amazon.s3.Response; import net.noderunner.amazon.s3.S3Object; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.Fqn; import org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig; import org.jboss.cache.loader.AbstractCacheLoader; import java.io.BufferedInputStream; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; /** * Uses the Amazon S3 service for storage. * See http://aws.amazon.com/ for information. * Does not support transaction isolation. *

        * Data is stored in a single bucket location. * The FQN comprises the key of the storage, the data the data itself. *

        * Internal structure: *

         * A/
         * B/_rootchild
         * C/_rootchild/_child1
         * C/_rootchild/_child2
         * C/_rootchild/_child3
         * B/_root2
         * 
        * The FQN component type is either prefixed with a _ for String, or a primitive type prefix. *

        * All put and many remove operations require fetching and merging data before storing data, * which increases latency. This fetching can be turned off. See {@link S3LoaderConfig#getOptimize()}. *

        * Parent nodes are added to the store as needed. * For example, when doing a put("/a/b/c"), the nodes "/a/b" and "/a" are created * if they do not exist. To prevent unnecessary checks of the store, * a local cache is kept of these "parent nodes". With multiple sites removing * parent nodes, this can potentially need to inconsistencies. To disable caching, * set {@link S3LoaderConfig#setParentCache} to false. *

        * * @author Elias Ross * @version $Id: JdbmCacheLoader.java 4298 2007-08-15 18:30:00Z genman $ */ @ThreadSafe @SuppressWarnings("unchecked") public class S3CacheLoader extends AbstractCacheLoader { private static final Log log = LogFactory.getLog(S3CacheLoader.class); private static final boolean trace = log.isTraceEnabled(); /** * Max number of dummy parent nodes to cache. */ private static final int PARENT_CACHE_SIZE = 100; /** * Configuration. */ private S3LoaderConfig config; /** * Unit separator. */ private final char SEP = Fqn.SEPARATOR.charAt(0); /** * Zero depth prefix. */ private final char DEPTH_0 = 'A'; /** * Stateless connection; thread safe. */ private Connection connection; /** * Map classes to characters. */ private static Map, Character> prefix = new HashMap, Character>(); static { prefix.put(Byte.class, 'B'); prefix.put(Character.class, 'C'); prefix.put(Double.class, 'D'); prefix.put(Float.class, 'F'); prefix.put(Integer.class, 'I'); prefix.put(Long.class, 'J'); prefix.put(Short.class, 'S'); prefix.put(Boolean.class, 'Z'); } /** * Empty parent nodes whose existence is cached. * Empty parents are required to ensure {@link #getChildrenNames(Fqn)} * and recursive {@link #remove(Fqn)} work correctly. */ private Set parents = Collections.synchronizedSet(new HashSet()); /** * Empty HashMap, serialized, lazy created. */ private S3Object dummyObj; /** * This cache loader is stateless, but as part of initialization access the service. * Creates a new bucket, if necessary. */ @Override public void start() throws Exception { log.debug("Starting"); try { this.connection = config.getConnection(); Response create = connection.create(getBucket(), config.getLocation()); if (!create.isOk()) throw new S3Exception("Unable to create bucket: " + create); log.info("S3 accessed successfully. Bucket created: " + create); } catch (Exception e) { destroy(); throw e; } } /** * Closes the connection; shuts down the HTTP connection pool. */ @Override public void stop() { log.debug("stop"); connection.shutdown(); } /** * Sets the configuration string for this cache loader. */ public void setConfig(IndividualCacheLoaderConfig base) { if (base instanceof S3LoaderConfig) { this.config = (S3LoaderConfig) base; } else { config = new S3LoaderConfig(base); } if (trace) log.trace("config=" + config); } public IndividualCacheLoaderConfig getConfig() { return config; } private String key(Fqn fqn) { return key(fqn.size(), fqn).toString(); } private String children(Fqn fqn) { return key(fqn.size() + 1, fqn).append(SEP).toString(); } private StringBuilder key(int depth, Fqn fqn) { StringBuilder sb = new StringBuilder(); List l = fqn.peekElements(); sb.append((char) (DEPTH_0 + depth)); for (Object o : l) { sb.append(SEP); if (o == null) sb.append("_null"); else if (o instanceof String) sb.append("_").append(o); else { // TODO Character c = prefix.get(o.getClass()); if (c == null) throw new IllegalArgumentException("not supported " + o.getClass()); sb.append(c.charValue()).append(o); } } return sb; } /** * Returns an unmodifiable set of relative children names, or * returns null if the parent node is not found or if no children are found. */ public Set getChildrenNames(Fqn name) throws Exception { String children = children(name); ListResponse response = connection.list(getBucket(), children); if (trace) { log.trace("getChildrenNames " + name + " response=" + response); } if (response.isNotFound()) return null; if (!response.isOk()) throw new Exception("List failed " + response); Set set = new HashSet(); for (Entry e : response.getEntries()) { // TODO decode prefix set.add(e.getKey().substring(children.length() + 1)); } if (set.isEmpty()) { return null; } return Collections.unmodifiableSet(set); } /** * Returns a map containing all key-value pairs for the given FQN, or null * if the node is not present. */ public Map get(Fqn name) throws Exception { GetStreamResponse response = connection.getStream(getBucket(), key(name)); try { if (trace) { log.trace("get " + name + " response=" + response); } if (response.isNotFound()) return null; if (!response.isOk()) throw new S3Exception("get failed " + response); BufferedInputStream is = new BufferedInputStream(response.getInputStream()); Map map = (Map) getMarshaller().objectFromStream(is); response.release(); return map; } finally { response.release(); } } private Bucket getBucket() { return config.getBucket(); } /** * Returns whether the given node exists. */ public boolean exists(Fqn name) throws Exception { Response response = connection.head(getBucket(), key(name)); if (trace) { log.trace("exists " + name + " response=" + response); } return response.isOk(); } private S3Object wrap(Map map) throws Exception { byte[] b = getMarshaller().objectToByteBuffer(map); return new S3Object(b); } /** * Stores a single FQN-key-value record. * This is slow, so avoid this method. */ public Object put(Fqn name, Object key, Object value) throws Exception { Map map = mayGet(name); Object oldValue; if (map != null) { oldValue = map.put(key, value); } else { map = new HashMap(Collections.singletonMap(key, value)); oldValue = null; } put0(name, map); return oldValue; } /** * Puts by replacing all the contents of the node. */ private void put0(Fqn name, Map map) throws Exception { put0(name, wrap(map)); } private void put0(Fqn name, S3Object obj) throws Exception { Response response = connection.put(getBucket(), key(name), obj); if (trace) { log.trace("put " + name + " obj=" + obj + " response=" + response); } ensureParent(name); if (!response.isOk()) throw new S3Exception("Put failed " + response); } private S3Object getDummy() throws Exception { if (dummyObj != null) return dummyObj; return dummyObj = wrap(new HashMap(0)); } /** * Ensures a parent node exists. * Calls recursively to initialize parents as necessary. */ private void ensureParent(Fqn name) throws Exception { if (name.size() <= 1) return; Fqn parent = name.getParent(); boolean cache = config.getParentCache(); if (cache && parents.contains(parent)) return; // potential race condition between exists and put if (!exists(parent)) put0(parent, getDummy()); if (cache) { parents.add(parent); if (parents.size() > PARENT_CACHE_SIZE) { parents.clear(); } } ensureParent(parent); } /** * Returns null if optimized; else fetches. */ private Map mayGet(Fqn name) throws Exception { if (config.getOptimize()) return null; else return get(name); } /** * Removes a key from an FQN. * Not very fast. */ public Object remove(Fqn name, Object key) throws Exception { Map map = get(name); Object oldValue; if (map != null) { oldValue = map.remove(key); } else { oldValue = null; } put0(name, map); return oldValue; } /** * Stores a map of key-values for a given FQN, but does not delete existing * key-value pairs (that is, it does not erase). */ public void put(Fqn name, Map values) throws Exception { Map map = mayGet(name); if (values == null) values = Collections.emptyMap(); if (map != null) map.putAll(values); else map = new HashMap(values); put0(name, map); } /** * Deletes the node for a given FQN and all its descendant nodes. */ public void remove(Fqn name) throws Exception { /* if (name.isRoot()) { log.trace("optimized delete"); connection.delete(getBucket()).assertOk(); connection.create(getBucket()).assertOk(); log.trace("done"); return; } */ Set children = getChildrenNames(name); if (children != null) { log.trace("remove children: " + children); for (String child : children) { remove(Fqn.fromRelativeElements(name, child)); } } Response response = connection.delete(getBucket(), key(name)); if (trace) { log.trace("delete " + name + " response=" + response); } if (!response.isOk() && !response.isNotFound()) throw new S3Exception("delete failed " + response); parents.remove(name); } /** * Clears the map for the given node, but does not remove the node. */ public void removeData(Fqn name) throws Exception { put0(name, getDummy()); } }jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/loader/AbstractDelegatingCacheLoader.java0000644000175000017500000001002511004125045032302 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.loader; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.Modification; import org.jboss.cache.RegionManager; import org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.List; import java.util.Map; import java.util.Set; /** * AbstractDelegatingCacheLoader provides standard functionality for a cache loader that simply delegates each * operation defined in the cache loader interface to the underlying cache loader, basically acting as a proxy to the * real cache loader. *

        * Any cache loader implementation that extends this class would be required to override any of the methods in * order to provide a different or added behaviour. * * @author Galder Zamarreno */ public abstract class AbstractDelegatingCacheLoader extends AbstractCacheLoader { private CacheLoader cacheLoader; public AbstractDelegatingCacheLoader(CacheLoader cl) { cacheLoader = cl; } public CacheLoader getCacheLoader() { return cacheLoader; } public void setCacheLoader(CacheLoader cacheLoader) { this.cacheLoader = cacheLoader; } public void setConfig(IndividualCacheLoaderConfig config) { cacheLoader.setConfig(config); } public IndividualCacheLoaderConfig getConfig() { return cacheLoader.getConfig(); } @Override public void setCache(CacheSPI c) { super.setCache(c); cacheLoader.setCache(c); } public Set getChildrenNames(Fqn fqn) throws Exception { return cacheLoader.getChildrenNames(fqn); } public Map get(Fqn name) throws Exception { return cacheLoader.get(name); } public boolean exists(Fqn name) throws Exception { return cacheLoader.exists(name); } public Object put(Fqn name, Object key, Object value) throws Exception { return cacheLoader.put(name, key, value); } public void put(Fqn name, Map attributes) throws Exception { cacheLoader.put(name, attributes); } @Override public void put(List modifications) throws Exception { cacheLoader.put(modifications); } public Object remove(Fqn fqn, Object key) throws Exception { return cacheLoader.remove(fqn, key); } public void remove(Fqn fqn) throws Exception { cacheLoader.remove(fqn); } public void removeData(Fqn fqn) throws Exception { cacheLoader.removeData(fqn); } @Override public void prepare(Object tx, List modifications, boolean one_phase) throws Exception { cacheLoader.prepare(tx, modifications, one_phase); } @Override public void commit(Object tx) throws Exception { cacheLoader.commit(tx); } @Override public void rollback(Object tx) { cacheLoader.rollback(tx); } @Override public void loadEntireState(ObjectOutputStream os) throws Exception { cacheLoader.loadEntireState(os); } @Override public void storeEntireState(ObjectInputStream is) throws Exception { cacheLoader.storeEntireState(is); } @Override public void loadState(Fqn subtree, ObjectOutputStream os) throws Exception { cacheLoader.loadState(subtree, os); } @Override public void storeState(Fqn subtree, ObjectInputStream is) throws Exception { cacheLoader.storeState(subtree, is); } @Override public void setRegionManager(RegionManager manager) { cacheLoader.setRegionManager(manager); } @Override public void create() throws Exception { cacheLoader.create(); } @Override public void start() throws Exception { cacheLoader.start(); } @Override public void stop() { cacheLoader.stop(); } @Override public void destroy() { cacheLoader.destroy(); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/loader/C3p0ConnectionFactory.java0000644000175000017500000000733311006101071030560 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.loader; import com.mchange.v2.c3p0.DataSources; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import javax.sql.DataSource; import java.sql.Connection; import java.sql.SQLException; import java.util.Enumeration; import java.util.Properties; /** * Standalone connection factory based on c3p0 connection pooling library * * @author Galder Zamarreno */ public class C3p0ConnectionFactory extends NonManagedConnectionFactory { private static final Log log = LogFactory.getLog(C3p0ConnectionFactory.class); private static final boolean trace = log.isTraceEnabled(); private DataSource ds; @Override public void setConfig(AdjListJDBCCacheLoaderConfig config) { super.setConfig(config); Properties properties = config.getProperties(); Enumeration e = properties.propertyNames(); while (e.hasMoreElements()) { String property = (String) e.nextElement(); if (property.startsWith("c3p0.")) { /* System properties should come before settings from XML configuration. For simplicity (c3p0 manual says overrides should not carry c3p0. start whereas system properties yes) and to avoid parsing, it's easier to set the values from XML configuration that should be c3p0. as system properties. So, this check allows us to determine if the System property was not set, in which case, we take the value from the XML configuration and set it to be a System property. If the value from the XML config was already set as System property, we do nothing, original System property should stand. */ String xmlPropertyValue = properties.getProperty(property); String sysPropertyValue = System.getProperty(property); if (System.getProperty(property) == null) { System.setProperty(property, xmlPropertyValue); if (log.isDebugEnabled()) { log.debug("c3p0 property defined in XML: " + property + "=" + xmlPropertyValue); } } else { if (log.isDebugEnabled()) { log.debug(property + "=" + sysPropertyValue + " defined as system property. It will override the value defined in XML which was: " + xmlPropertyValue); } } } } } @Override public void start() throws Exception { /* We need to call super so that the driver is loaded. This is required by the C3P0 manual. */ super.start(); DataSource unpooled = DataSources.unpooledDataSource(getUrl(), getUsr(), getPwd()); ds = DataSources.pooledDataSource(unpooled); if (log.isDebugEnabled()) { log.debug("Pooled datasource(url=" + getUrl() + ",usr=" + getUsr() + ",pwd=" + getPwd() + ") started."); } } @Override public Connection checkoutConnection() throws SQLException { Connection connection = ds.getConnection(); if (trace) { log.trace("Connection checked out: " + connection); } return connection; } @Override public void stop() { try { DataSources.destroy(ds); if (log.isDebugEnabled()) { log.debug("Pooled datasource destroyed."); } } catch (SQLException sqle) { log.warn("Could not destroy C3P0 connection pool: " + ds, sqle); } } protected DataSource getDataSource() { return ds; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/loader/TcpDelegatingCacheLoaderConfig.java0000644000175000017500000000717211006024704032426 0ustar twernertwernerpackage org.jboss.cache.loader; import org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig; import org.jboss.cache.util.Util; import java.util.Properties; public class TcpDelegatingCacheLoaderConfig extends IndividualCacheLoaderConfig { /** * The serialVersionUID */ private static final long serialVersionUID = -3138555335000168505L; private String host = "localhost"; private int port = 7500; private int timeout = 5000; private int reconnectWaitTime = 500; public TcpDelegatingCacheLoaderConfig() { setClassName(TcpDelegatingCacheLoader.class.getName()); } /** * For use by {@link TcpDelegatingCacheLoader}. * * @param base generic config object created by XML parsing. */ TcpDelegatingCacheLoaderConfig(IndividualCacheLoaderConfig base) { setClassName(TcpDelegatingCacheLoader.class.getName()); populateFromBaseConfig(base); } /** * For use by {@link TcpDelegatingCacheLoader}. * * @param host hostname of the delegate * @param port port the delegate is listening on * @param timeout after which to throw an IOException */ TcpDelegatingCacheLoaderConfig(String host, int port, int timeout) { setClassName(TcpDelegatingCacheLoader.class.getName()); this.host = host; this.port = port; this.timeout = timeout; } public String getHost() { return host; } public void setHost(String host) { testImmutability("host"); this.host = host; } public int getPort() { return port; } public void setPort(int port) { testImmutability("port"); this.port = port; } public int getTimeout() { return timeout; } public void setTimeout(int timeout) { testImmutability("timeout"); this.timeout = timeout; } public int getReconnectWaitTime() { return reconnectWaitTime; } public void setReconnectWaitTime(int reconnectWaitTime) { testImmutability("reconnectWaitTime"); this.reconnectWaitTime = reconnectWaitTime; } @Override public void setProperties(Properties props) { super.setProperties(props); String s = props.getProperty("host"); if (s != null && s.length() > 0) { this.host = s; } s = props.getProperty("port"); if (s != null && s.length() > 0) { this.port = Integer.parseInt(s); } s = props.getProperty("timeout"); if (s != null && s.length() > 0) { this.timeout = Integer.parseInt(s); } s = props.getProperty("reconnectWaitTime"); if (s != null && s.length() > 0) { this.reconnectWaitTime = Integer.parseInt(s); } } @Override public boolean equals(Object obj) { if (obj instanceof TcpDelegatingCacheLoaderConfig && equalsExcludingProperties(obj)) { TcpDelegatingCacheLoaderConfig other = (TcpDelegatingCacheLoaderConfig) obj; return Util.safeEquals(host, other.host) && (port == other.port) && (timeout == other.timeout) && (reconnectWaitTime == other.reconnectWaitTime); } return false; } @Override public int hashCode() { int result = hashCodeExcludingProperties(); result = 31 * result + (host == null ? 0 : host.hashCode()); result = 31 * result + port; result = 31 * result + timeout; result = 31 * result + reconnectWaitTime; return result; } @Override public TcpDelegatingCacheLoaderConfig clone() throws CloneNotSupportedException { return (TcpDelegatingCacheLoaderConfig) super.clone(); } }jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/loader/tcp/0000755000175000017500000000000011376174030024437 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/loader/tcp/TcpCacheServerMBean.java0000644000175000017500000000243710624760167031063 0ustar twernertwernerpackage org.jboss.cache.loader.tcp; import org.jboss.cache.Cache; import org.jboss.cache.CacheSPI; import org.jboss.cache.jmx.CacheJmxWrapperMBean; import java.net.UnknownHostException; /** * StandardMBean interface for {@link TcpCacheServer}. * * @author Bela Ban * @author Brian Stansberry * @version $Id: TcpCacheServerMBean.java 3889 2007-05-23 06:34:31Z bstansberry $ */ public interface TcpCacheServerMBean { void create() throws Exception; void start() throws Exception; void stop(); void destroy(); String getBindAddress(); void setBindAddress(String bind_addr) throws UnknownHostException; int getPort(); void setPort(int port); String getConfig(); void setConfig(String config); Cache getCache(); // BES 2007/5/22 Don't expose this setter via MBean interface, as // it's a different type from getter, which is invalid. This // setter doesn't need to be exposed vai JMX; the CacheJmxWrapper // is sufficient, and the setter is still there in the impl. //void setCache(CacheSPI cache); /** * Allows {@link #setCache(CacheSPI) injection of the CacheSPI} via * a {@link CacheJmxWrapperMBean}. * * @param wrapper */ void setCacheJmxWrapper(CacheJmxWrapperMBean wrapper); String getConnections(); } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/loader/tcp/TcpCacheServer.java0000644000175000017500000003660711032145203030144 0ustar twernertwernerpackage org.jboss.cache.loader.tcp; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.Cache; import org.jboss.cache.CacheException; import org.jboss.cache.CacheSPI; import org.jboss.cache.DefaultCacheFactory; import org.jboss.cache.Fqn; import org.jboss.cache.Modification; import org.jboss.cache.Node; import org.jboss.cache.NodeSPI; import org.jboss.cache.jmx.CacheJmxWrapperMBean; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketException; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; /** * TCP-IP based CacheServer, setCache TcpDelegatingCacheLoader with host and port of this server * * @author Bela Ban * @author Brian Stansberry * @version $Id: TcpCacheServer.java 6120 2008-06-30 11:58:59Z manik.surtani@jboss.com $ */ public class TcpCacheServer implements TcpCacheServerMBean { private ServerSocket srv_sock; private InetAddress bind_addr = null; private int port = 7500; private CacheSPI cache; private CacheJmxWrapperMBean wrapper; private String config; private boolean running = true; private final List conns = Collections.synchronizedList(new LinkedList()); /** * whether or not to start the server thread as a daemon. Should be false if started from the command line, true if started as an MBean. */ boolean daemon = true; static final Log log = LogFactory.getLog(TcpCacheServer.class); public String getBindAddress() { return bind_addr != null ? bind_addr.toString() : "n/a"; } public void setBindAddress(String bind_addr) throws UnknownHostException { if (bind_addr != null) { this.bind_addr = InetAddress.getByName(bind_addr); } } public int getPort() { return port; } public void setPort(int port) { this.port = port; } public String getConfig() { return config; } public void setConfig(String config) { this.config = config; } public Cache getCache() { return cache; } public void setCache(CacheSPI cache) { this.cache = cache; } public void setCacheJmxWrapper(CacheJmxWrapperMBean wrapper) { this.wrapper = wrapper; } public void start() throws Exception { if (cache == null) { // cache not directly set; get from wrapper or create from config if (wrapper != null) { cache = (CacheSPI) wrapper.getCache(); if (cache == null) { throw new CacheException("cache cannot be obtained from CacheJmxWrapperMBean;" + " be sure start() is invoked on wrapper before it is invoked on the" + " TcpCacheServer"); } } else if (config != null) { cache = (CacheSPI) new DefaultCacheFactory().createCache(this.config); } } if (cache == null) { throw new CacheException("cache reference is not set"); } srv_sock = new ServerSocket(port, 10, bind_addr); log.info("TcpCacheServer listening on : " + srv_sock.getInetAddress() + ":" + srv_sock.getLocalPort()); running = true; Thread serverThread = new Thread("TcpCacheServer") { @Override public void run() { try { while (running) { Socket client_sock = srv_sock.accept(); Connection conn = new Connection(client_sock, cache); conns.add(conn); conn.start(); } } catch (SocketException se) { if (!running) { // this is because of the stop() lifecycle method being called. // ignore. log.info("Shutting down TcpCacheServer"); } else { log.error("Caught exception! Shutting down server thread.", se); } } catch (IOException e) { log.error("Caught exception! Shutting down server thread.", e); } } }; serverThread.setDaemon(daemon); serverThread.start(); } public void stop() { running = false; synchronized (conns) { // Connection.close() removes conn from the list, // so copy off the list first to avoid ConcurrentModificationException List copy = new ArrayList(conns); for (Connection conn : copy) { conn.close(); } conns.clear(); } if (srv_sock != null) { try { srv_sock.close(); srv_sock = null; } catch (IOException e) { if (log.isDebugEnabled()) log.debug("Unable to close server socket", e); } } } public String getConnections() { synchronized (conns) { StringBuilder sb = new StringBuilder(); sb.append(conns.size()).append(" connections:\n"); for (Connection c : conns) { sb.append(c).append("\n"); } return sb.toString(); } } public void create() { } public void destroy() { } private class Connection implements Runnable { private Socket sock = null; private ObjectInputStream input = null; private ObjectOutputStream output = null; private CacheSPI c; private Thread t = null; public Connection(Socket sock, CacheSPI cache) throws IOException { this.sock = sock; output = new ObjectOutputStream(new BufferedOutputStream(sock.getOutputStream())); output.flush(); input = new ObjectInputStream(new BufferedInputStream(sock.getInputStream())); c = cache; } public void start() { t = new Thread(this, "TcpCacheServer.Connection"); t.setDaemon(true); t.start(); } public void close() { t = null; try { if (output != null) output.close(); } catch (Throwable th) { if (log.isDebugEnabled()) log.debug("Unable to close TCP stream", th); } try { if (input != null) input.close(); } catch (Throwable th) { if (log.isDebugEnabled()) log.debug("Unable to close TCP stream", th); } try { if (sock != null) sock.close(); } catch (Throwable th) { if (log.isDebugEnabled()) log.debug("Unable to close socket", th); } // remove self from connections list conns.remove(this); } public void run() { int op; Fqn fqn; Object key, val, retval; NodeSPI n; boolean flag; while (t != null && t.equals(Thread.currentThread())) { try { op = input.readByte(); } catch (IOException e) { log.debug("Client closed socket"); close(); break; } try { output.reset(); switch (op) { case TcpCacheOperations.GET_CHILDREN_NAMES: fqn = (Fqn) input.readObject(); Node node = c.getRoot().getChild(fqn); Set children = node == null ? Collections.emptySet() : node.getChildrenNames(); output.writeObject(children); break; case TcpCacheOperations.GET_KEY: fqn = (Fqn) input.readObject(); key = input.readObject(); retval = c.get(fqn, key); output.writeObject(retval); break; case TcpCacheOperations.GET: fqn = (Fqn) input.readObject(); n = (NodeSPI) c.getRoot().getChild(fqn); if (n == null) { // node doesn't exist - return null output.writeObject(null); break; } Map map = n.getData(); if (map == null) map = new HashMap(); output.writeObject(map); break; case TcpCacheOperations.EXISTS: fqn = (Fqn) input.readObject(); flag = c.getRoot().hasChild(fqn); output.writeObject(flag); break; case TcpCacheOperations.PUT_KEY_VAL: fqn = (Fqn) input.readObject(); key = input.readObject(); val = input.readObject(); retval = c.put(fqn, key, val); output.writeObject(retval); break; case TcpCacheOperations.PUT: fqn = (Fqn) input.readObject(); map = (Map) input.readObject(); c.put(fqn, map); output.writeObject(Boolean.TRUE); break; case TcpCacheOperations.PUT_LIST: int length = input.readInt(); retval = Boolean.TRUE; if (length > 0) { Modification mod; List mods = new ArrayList(length); for (int i = 0; i < length; i++) { mod = new Modification(); mod.readExternal(input); mods.add(mod); } try { handleModifications(mods); } catch (Exception ex) { retval = ex; } } output.writeObject(retval); break; case TcpCacheOperations.REMOVE_KEY: fqn = (Fqn) input.readObject(); key = input.readObject(); retval = c.remove(fqn, key); output.writeObject(retval); break; case TcpCacheOperations.REMOVE: fqn = (Fqn) input.readObject(); c.removeNode(fqn); output.writeObject(Boolean.TRUE); break; case TcpCacheOperations.REMOVE_DATA: fqn = (Fqn) input.readObject(); node = c.getRoot().getChild(fqn); if (node != null) { node.clearData(); output.writeObject(true); } else { output.writeObject(false); } break; case TcpCacheOperations.LOAD_ENTIRE_STATE: ObjectOutputStream os = (ObjectOutputStream) input.readObject(); if (c.getCacheLoaderManager() != null) { c.getCacheLoaderManager().getCacheLoader().loadEntireState(os); } output.writeObject(Boolean.TRUE); break; case TcpCacheOperations.STORE_ENTIRE_STATE: ObjectInputStream is = (ObjectInputStream) input.readObject(); if (c.getCacheLoaderManager() != null) { c.getCacheLoaderManager().getCacheLoader().storeEntireState(is); } output.writeObject(Boolean.TRUE); break; default: log.error("Operation " + op + " unknown"); break; } output.flush(); } catch (Exception e) { log.debug(e, e); try { output.writeObject(e); output.flush(); } catch (IOException e1) { log.error(e1, e1); } } } } @Override public String toString() { StringBuilder sb = new StringBuilder(); if (sock != null) { sb.append(sock.getRemoteSocketAddress()); } return sb.toString(); } protected void handleModifications(List modifications) throws CacheException { for (Modification m : modifications) { switch (m.getType()) { case PUT_DATA: c.put(m.getFqn(), m.getData()); break; case PUT_DATA_ERASE: c.put(m.getFqn(), m.getData()); break; case PUT_KEY_VALUE: c.put(m.getFqn(), m.getKey(), m.getValue()); break; case REMOVE_DATA: Node n = c.getRoot().getChild(m.getFqn()); if (n != null) n.clearData(); break; case REMOVE_KEY_VALUE: c.remove(m.getFqn(), m.getKey()); break; case REMOVE_NODE: c.removeNode(m.getFqn()); break; case MOVE: c.move(m.getFqn(), m.getFqn2()); break; default: log.error("modification type " + m.getType() + " not known"); break; } } } } public static void main(String[] args) throws Exception { String bind_addr = null; int port = 7500; TcpCacheServer server; String config = null; for (int i = 0; i < args.length; i++) { if (args[i].equals("-bind_addr")) { bind_addr = args[++i]; continue; } if (args[i].equals("-port")) { port = Integer.parseInt(args[++i]); continue; } if (args[i].equals("-config")) { config = args[++i]; continue; } help(); return; } server = new TcpCacheServer(); server.daemon = false; server.setBindAddress(bind_addr); server.setPort(port); server.setConfig(config); server.create(); server.start(); } private static void help() { System.out.println("TcpCacheServer [-bind_addr
        ] [-port ] [-config ] [-help]"); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/loader/tcp/TcpCacheOperations.java0000644000175000017500000000077510647175305031037 0ustar twernertwernerpackage org.jboss.cache.loader.tcp; /** * Statics that used to exist in the DelegatingCacheLoader class. * * @author Manik Surtani * @since 2.0.0 */ public interface TcpCacheOperations { int GET_CHILDREN_NAMES = 1; int GET_KEY = 2; int GET = 3; int EXISTS = 4; int PUT_KEY_VAL = 5; int PUT = 6; int REMOVE_KEY = 7; int REMOVE = 8; int REMOVE_DATA = 9; int LOAD_ENTIRE_STATE = 10; int STORE_ENTIRE_STATE = 11; int PUT_LIST = 12; } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/loader/ManagedConnectionFactory.java0000644000175000017500000001101211006101071031374 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.loader; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.sql.DataSource; import java.sql.Connection; import java.sql.SQLException; /** * ManagedConnectionFactory for Application Server managed environments * * @author Hany Mesha * @author Galder Zamarreno */ public class ManagedConnectionFactory implements ConnectionFactory { private static final Log log = LogFactory.getLog(ManagedConnectionFactory.class); private static final boolean trace = log.isTraceEnabled(); private DataSource dataSource; private String datasourceName; public void setConfig(AdjListJDBCCacheLoaderConfig config) { datasourceName = config.getDatasourceName(); } public void start() throws Exception { // A datasource will be registered in JNDI in the start portion of // its lifecycle, so now that we are in start() we can look it up InitialContext ctx = null; try { ctx = new InitialContext(); dataSource = (DataSource) ctx.lookup(datasourceName); if (trace) { log.trace("Datasource lookup for " + datasourceName + " succeded: " + dataSource); } } catch (NamingException e) { reportAndRethrowError("Failed to lookup datasource " + datasourceName, e); } finally { if (ctx != null) { try { ctx.close(); } catch (NamingException e) { log.warn("Failed to close naming context.", e); } } } } public Connection getConnection() throws SQLException { Connection connection = dataSource.getConnection(); if (trace) { log.trace("Connection checked out: " + connection); } return connection; } public void prepare(Object txId) { /* This implementation should be left empty. In a managed environment, we retrieve the datasource from the application server, and it's down to the configuration of the datasource whether it will participate in the current transaction, i.e. local-tx-datasource, no-tx-datasource,...etc. The application server will make sure that if configured, the datasource will participate in the transaction and will commit/rollback changes if required. Therefore, we don't have to do anything for these transactional methods but leave them empty. */ } public void commit(Object txId) { /* This implementation should be left empty. In a managed environment, we retrieve the datasource from the application server, and it's down to the configuration of the datasource whether it will participate in the current transaction, i.e. local-tx-datasource, no-tx-datasource,...etc. The application server will make sure that if configured, the datasource will participate in the transaction and will commit/rollback changes if required. Therefore, we don't have to do anything for these transactional methods but leave them empty. */ } public void rollback(Object txId) { /* This implementation should be left empty. In a managed environment, we retrieve the datasource from the application server, and it's down to the configuration of the datasource whether it will participate in the current transaction, i.e. local-tx-datasource, no-tx-datasource,...etc. The application server will make sure that if configured, the datasource will participate in the transaction and will commit/rollback changes if required. Therefore, we don't have to do anything for these transactional methods but leave them empty. */ } public void close(Connection con) { safeClose(con); } public void stop() { } private void safeClose(Connection con) { if (con != null) { try { con.close(); } catch (SQLException e) { log.warn("Failed to close connection", e); } } } private void reportAndRethrowError(String message, Exception cause) throws IllegalStateException { log.error(message, cause); throw new IllegalStateException(message, cause); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/loader/jdbm/0000755000175000017500000000000011376174030024565 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/loader/jdbm/JdbmCacheLoader.java0000644000175000017500000003726611032145203030362 0ustar twernertwernerpackage org.jboss.cache.loader.jdbm; import jdbm.RecordManager; import jdbm.RecordManagerFactory; import jdbm.btree.BTree; import jdbm.helper.Tuple; import jdbm.helper.TupleBrowser; import net.jcip.annotations.ThreadSafe; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.FqnComparator; import org.jboss.cache.Modification; import org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig; import org.jboss.cache.loader.AbstractCacheLoader; import java.io.File; import java.io.IOException; import java.io.Serializable; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; /** * A persistent CacheLoader based on the JDBM project. * See http://jdbm.sourceforge.net/ . * Does not support transaction isolation. *

        *

        The configuration string format is:

        *
        environmentDirectoryName[#databaseName]
        *

        where databaseName, if omitted, defaults to the ClusterName property * of the CacheImpl.

        *

        * Data is sorted out like: *

         * / = N
         * /node1 = N
         * /node1/K/k1 = v1
         * /node1/K/k2 = v2
         * /node2 = N
         * /node2/node3 = N
         * /node2/node3/K/k1 = v1
         * /node2/node3/K/k2 = v2
         * /node2/node4 = N
         * 
        * N represents a node, K represents a key block. k and v represent key/value * pairs. *

        * The browse operations lock the entire tree; eventually the JDBM team plans to fix this. * * @author Elias Ross * @version $Id: JdbmCacheLoader.java 6120 2008-06-30 11:58:59Z manik.surtani@jboss.com $ */ @ThreadSafe public class JdbmCacheLoader extends AbstractCacheLoader { private static final Log log = LogFactory.getLog(JdbmCacheLoader.class); private static final boolean trace = log.isTraceEnabled(); private static final String KEYS = "K"; private static final String NODE = "N"; private static final String NAME = "JdbmCacheLoader"; private JdbmCacheLoaderConfig config; private RecordManager recman; private BTree tree; /* * Service implementation -- lifecycle methods. * Note that setConfig() and setCache() are called before create(). */ @Override public void create() throws Exception { checkNotOpen(); } /** * Opens the environment and the database specified by the configuration * string. The environment and databases are created if necessary. */ @Override public void start() throws Exception { log.trace("Starting JdbmCacheLoader instance."); checkNotOpen(); checkNonNull(cache, "CacheSPI object is required"); String locationStr = config.getLocation(); if (locationStr == null) { locationStr = System.getProperty("java.io.tmpdir"); config.setLocation(locationStr); } // test location File location = new File(locationStr); if (!location.exists()) { boolean created = location.mkdirs(); if (!created) throw new IOException("Unable to create cache loader location " + location); } if (!location.isDirectory()) { throw new IOException("Cache loader location [" + location + "] is not a directory!"); } /* Parse config string. */ File homeDir; int offset = locationStr.indexOf('#'); String cacheDbName; if (offset >= 0 && offset < locationStr.length() - 1) { homeDir = new File(locationStr.substring(0, offset)); cacheDbName = locationStr.substring(offset + 1); } else { homeDir = new File(locationStr); cacheDbName = cache.getClusterName(); } try { openDatabase(new File(homeDir, cacheDbName)); } catch (Exception e) { destroy(); throw e; } } /** * Opens all databases and initializes database related information. */ private void openDatabase(File f) throws Exception { Properties props = new Properties(); // Incorporate properties from setConfig() ? // props.put(RecordManagerOptions.SERIALIZER, RecordManagerOptions.SERIALIZER_EXTENSIBLE); // props.put(RecordManagerOptions.PROFILE_SERIALIZATION, "false"); recman = RecordManagerFactory.createRecordManager(f.toString(), props); long recid = recman.getNamedObject(NAME); log.debug(NAME + " located as " + recid); if (recid == 0) { tree = BTree.createInstance(recman, new JdbmFqnComparator()); recman.setNamedObject(NAME, tree.getRecid()); } else { tree = BTree.load(recman, recid); } log.info("JDBM database " + f + " opened with " + tree.size() + " entries"); } /** * Closes all databases, ignoring exceptions, and nulls references to all * database related information. */ private void closeDatabases() { if (recman != null) { try { recman.close(); } catch (Exception shouldNotOccur) { log.warn("Caught unexpected exception", shouldNotOccur); } } recman = null; tree = null; } /** * Closes the databases and environment, and nulls references to them. */ @Override public void stop() { log.debug("stop"); closeDatabases(); } /* * CacheLoader implementation. */ /** * Sets the configuration string for this cache loader. */ public void setConfig(IndividualCacheLoaderConfig base) { checkNotOpen(); if (base instanceof JdbmCacheLoaderConfig) { this.config = (JdbmCacheLoaderConfig) base; } else { config = new JdbmCacheLoaderConfig(base); } if (trace) log.trace("Configuring cache loader with location = " + config.getLocation()); } public IndividualCacheLoaderConfig getConfig() { return config; } /** * Sets the CacheImpl owner of this cache loader. */ @Override public void setCache(CacheSPI c) { super.setCache(c); checkNotOpen(); } /** * Returns a special FQN for keys of a node. */ private Fqn keys(Fqn name) { return Fqn.fromRelativeElements(name, KEYS); } /** * Returns a special FQN for key of a node. */ private Fqn key(Fqn name, Object key) { return Fqn.fromRelativeElements(name, KEYS, nullMask(key)); } /** * Returns an unmodifiable set of relative children names, or * returns null if the parent node is not found or if no children are found. * This is a fairly expensive operation, and is assumed to be performed by * browser applications. Calling this method as part of a run-time * transaction is not recommended. */ public Set getChildrenNames(Fqn name) throws Exception { if (trace) { log.trace("getChildrenNames " + name); } synchronized (tree) { return getChildrenNames0(name); } } private Set getChildrenNames0(Fqn name) throws IOException { TupleBrowser browser = tree.browse(name); Tuple t = new Tuple(); if (browser.getNext(t)) { if (!t.getValue().equals(NODE)) { log.trace(" not a node"); return null; } } else { log.trace(" no nodes"); return null; } Set set = new HashSet(); // Want only /a/b/c/X nodes int depth = name.size() + 1; while (browser.getNext(t)) { Fqn fqn = (Fqn) t.getKey(); int size = fqn.size(); if (size < depth) { break; } if (size == depth && t.getValue().equals(NODE)) { set.add((String) fqn.getLastElement()); } } if (set.isEmpty()) { return null; } return Collections.unmodifiableSet(set); } /** * Returns a map containing all key-value pairs for the given FQN, or null * if the node is not present. * This operation is always non-transactional, even in a transactional * environment. */ public Map get(Fqn name) throws Exception { checkOpen(); checkNonNull(name, "name"); if (tree.find(name) == null) { if (trace) { log.trace("get, no node: " + name); } return null; } Fqn keys = keys(name); Tuple t = new Tuple(); Map map = new HashMap(); synchronized (tree) { TupleBrowser browser = tree.browse(keys); while (browser.getNext(t)) { Fqn fqn = (Fqn) t.getKey(); if (!fqn.isChildOf(keys)) { break; } Object k = fqn.getLastElement(); Object v = t.getValue(); map.put(nullUnmask(k), nullUnmask(v)); } } if (trace) { log.trace("get " + name + " map=" + map); } return map; } /** * Returns whether the given node exists. */ public boolean exists(Fqn name) throws IOException { return tree.find(name) != null; } private void commit() throws Exception { recman.commit(); } /** * Stores a single FQN-key-value record. * Intended to be used in a non-transactional environment, but will use * auto-commit in a transactional environment. */ public Object put(Fqn name, Object key, Object value) throws Exception { try { return put0(name, key, value); } finally { commit(); } } private Object put0(Fqn name, Object key, Object value) throws Exception { checkNonNull(name, "name"); makeNode(name); Fqn rec = key(name, key); Object oldValue = insert(rec, value); if (trace) { log.trace("put " + rec + " value=" + value + " old=" + oldValue); } return oldValue; } /** * Stores a map of key-values for a given FQN, but does not delete existing * key-value pairs (that is, it does not erase). * Intended to be used in a non-transactional environment, but will use * auto-commit in a transactional environment. */ public void put(Fqn name, Map values) throws Exception { put0(name, values); commit(); } private void put0(Fqn name, Map values) throws Exception { if (trace) { log.trace("put " + name + " values=" + values); } makeNode(name); if (values == null) { return; } for (Map.Entry me : values.entrySet()) { Fqn rec = key(name, me.getKey()); insert(rec, nullMask(me.getValue())); } } /** * Marks a FQN as a node. */ private void makeNode(Fqn fqn) throws IOException { if (exists(fqn)) { return; } int size = fqn.size(); // TODO should not modify so darn often for (int i = size; i >= 0; i--) { Fqn child = fqn.getAncestor(i); Object existing = tree.insert(child, NODE, false); if (existing != null) { break; } } } private Object insert(Fqn fqn, Object value) throws IOException { return nullUnmask(tree.insert(fqn, nullMask(value), true)); } /** * Erase a FQN and children. * Does not commit. */ private void erase0(Fqn name) throws IOException { erase0(name, true); } private void erase0(Fqn name, boolean self) throws IOException { if (trace) { log.trace("erase " + name + " self=" + self); } synchronized (tree) { TupleBrowser browser = tree.browse(name); Tuple t = new Tuple(); if (browser.getNext(t) && self) { tree.remove(t.getKey()); } while (browser.getNext(t)) { Fqn fqn = (Fqn) t.getKey(); if (!fqn.isChildOf(name)) { break; } tree.remove(fqn); } } } /** * Erase a FQN's key. * Does not commit. */ private Object eraseKey0(Fqn name, Object key) throws IOException { if (trace) { log.trace("eraseKey " + name + " key " + key); } Fqn fqnKey = key(name, key); try { return tree.remove(fqnKey); } catch (IllegalArgumentException e) { // Seems to be harmless // log.warn("IllegalArgumentException for " + fqnKey); // dump(); return null; } } /** * Applies the given modifications. * Intended to be used in a non-transactional environment, but will use * auto-commit in a transactional environment. */ @Override public void put(List modifications) throws Exception { checkOpen(); checkNonNull(modifications, "modifications"); super.put(modifications); commit(); } /** * Deletes the node for a given FQN and all its descendent nodes. * Intended to be used in a non-transactional environment, but will use * auto-commit in a transactional environment. */ public void remove(Fqn name) throws Exception { erase0(name); commit(); } /** * Deletes a single FQN-key-value record. * Intended to be used in a non-transactional environment, but will use * auto-commit in a transactional environment. */ public Object remove(Fqn name, Object key) throws Exception { try { return eraseKey0(name, key); } finally { commit(); } } /** * Clears the map for the given node, but does not remove the node. */ public void removeData(Fqn name) throws Exception { erase0(name, false); } /** * Throws an exception if the environment is not open. */ private void checkOpen() { if (tree == null) { throw new IllegalStateException( "Operation not allowed before calling create()"); } } /** * Throws an exception if the environment is not open. */ private void checkNotOpen() { if (tree != null) { throw new IllegalStateException( "Operation not allowed after calling create()"); } } /** * Throws an exception if the parameter is null. */ private void checkNonNull(Object param, String paramName) { if (param == null) { throw new NullPointerException( "Parameter must not be null: " + paramName); } } private Object nullMask(Object o) { return (o == null) ? Null.NULL : o; } private Object nullUnmask(Object o) { return (o == Null.NULL) ? null : o; } /** * Dumps the tree to debug. */ public void dump() throws IOException { dump(Fqn.ROOT); } /** * Dumps the tree past the key to debug. */ public void dump(Object key) throws IOException { TupleBrowser browser = tree.browse(key); Tuple t = new Tuple(); log.debug("contents: " + key); while (browser.getNext(t)) { log.debug(t.getKey() + "\t" + t.getValue()); } log.debug(""); } @Override public String toString() { BTree bt = tree; int size = (bt == null) ? -1 : bt.size(); return "JdbmCacheLoader locationStr=" + config.getLocation() + " size=" + size; } } class JdbmFqnComparator extends FqnComparator implements Serializable { private static final long serialVersionUID = 1000; } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/loader/jdbm/JdbmCacheLoaderConfig.java0000644000175000017500000000317411006024704031502 0ustar twernertwernerpackage org.jboss.cache.loader.jdbm; import org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig; import org.jboss.cache.util.Util; import java.util.Properties; public class JdbmCacheLoaderConfig extends IndividualCacheLoaderConfig { private static final long serialVersionUID = 4626734068542420865L; private String location; public JdbmCacheLoaderConfig() { setClassName(JdbmCacheLoader.class.getName()); } /** * For use by {@link JdbmCacheLoader}. * * @param base generic config object created by XML parsing. */ JdbmCacheLoaderConfig(IndividualCacheLoaderConfig base) { setClassName(JdbmCacheLoader.class.getName()); populateFromBaseConfig(base); } public String getLocation() { return location; } public void setLocation(String location) { testImmutability("location"); this.location = location; } @Override public void setProperties(Properties props) { super.setProperties(props); setLocation(props != null ? props.getProperty("location") : null); } @Override public boolean equals(Object obj) { if (obj instanceof JdbmCacheLoaderConfig && equalsExcludingProperties(obj)) { return Util.safeEquals(location, ((JdbmCacheLoaderConfig) obj).location); } return false; } @Override public int hashCode() { return 31 * hashCodeExcludingProperties() + (location == null ? 0 : location.hashCode()); } @Override public JdbmCacheLoaderConfig clone() throws CloneNotSupportedException { return (JdbmCacheLoaderConfig) super.clone(); } }jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/loader/jdbm/Null.java0000644000175000017500000000062411004125045026332 0ustar twernertwernerpackage org.jboss.cache.loader.jdbm; /** * Special FQN entry to indicate null. */ class Null implements java.io.Serializable { private static final long serialVersionUID = -1337133713374690630L; static final Object NULL = new Null(); private Null() { } @Override public String toString() { return "null"; } Object readResolve() { return NULL; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/loader/SingletonStoreCacheLoader.java0000644000175000017500000004321611032145203031541 0ustar twernertwernerpackage org.jboss.cache.loader; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.Fqn; import org.jboss.cache.Modification; import org.jboss.cache.NodeSPI; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig.SingletonStoreConfig; import org.jboss.cache.notifications.annotation.CacheListener; import org.jboss.cache.notifications.annotation.CacheStarted; import org.jboss.cache.notifications.annotation.CacheStopped; import org.jboss.cache.notifications.annotation.ViewChanged; import org.jboss.cache.notifications.event.Event; import org.jboss.cache.notifications.event.ViewChangedEvent; import org.jgroups.Address; import org.jgroups.View; import java.io.ObjectInputStream; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Vector; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; /** * SingletonStoreCacheLoader is a delegating cache loader used for situations when only one node should interact with * the underlying store. The coordinator of the cluster will be responsible for the underlying CacheLoader. * SingletonStoreCacheLoader is a simply facade to a real CacheLoader implementation. It always delegates reads to the * real CacheLoader. *

        * Writes are forwarded only if this SingletonStoreCacheLoader is currently the cordinator. This avoid having all * CacheLoaders in a cluster writing the same data to the same underlying store. Although not incorrect (e.g. a DB * will just discard additional INSERTs for the same key, and throw an exception), this will avoid a lot of * redundant work.
        *

        * Whenever the current coordinator dies (or leaves), the second in line will take over. That SingletonStoreCacheLoader * will then pass writes through to its underlying CacheLoader. Optionally, when a new coordinator takes over the * Singleton, it can push the in-memory state to the cache cacheLoader, within a time constraint. * * @author Bela Ban * @author Galder Zamarreno */ public class SingletonStoreCacheLoader extends AbstractDelegatingCacheLoader { /** * Log instance. */ private static final Log log = LogFactory.getLog(SingletonStoreCacheLoader.class); private static final boolean trace = log.isTraceEnabled(); /** * Name of thread that should pushing in-memory state to cache loader. */ private static final String THREAD_NAME = "InMemoryToCacheLoaderPusher"; /** * Configuration for the SingletonStoreCacheLoader. */ private SingletonStoreDefaultConfig config; /** * Executor service used to submit tasks to push in-memory state. */ private final ExecutorService executor; /** * Future result of the in-memory push state task. This allows SingletonStoreCacheLoader to check whether there's any * push taks on going. */ private Future pushStateFuture; /* FutureTask guarantess a safe publication of the result */ /** * Address instance that allows SingletonStoreCacheLoader to find out whether it became the coordinator of the * cluster, or whether it stopped being it. This dictates whether the SingletonStoreCacheLoader is active or not. */ private Address localAddress; /** * Whether the the current node is the coordinator and therefore SingletonStoreCacheLoader is active. Being active * means delegating calls to the underlying cache loader. */ private boolean active; /** * Empty constructor so that it can instantiated using reflection. */ public SingletonStoreCacheLoader() { super(null); executor = Executors.newSingleThreadExecutor(new ThreadFactory() { public Thread newThread(Runnable r) { return new Thread(r, THREAD_NAME); } }); } /** * Sets the config for SingletonStoreCacheLoader and for the delegating cache loader. */ @Override public void setConfig(CacheLoaderConfig.IndividualCacheLoaderConfig config) { super.setConfig(config); SingletonStoreConfig ssc = config.getSingletonStoreConfig(); if (ssc instanceof SingletonStoreDefaultConfig) { this.config = (SingletonStoreDefaultConfig) ssc; } else if (ssc != null) { this.config = new SingletonStoreDefaultConfig(ssc); } else { this.config = new SingletonStoreDefaultConfig(); } } @Override public void create() throws Exception { super.create(); cache.addCacheListener(new SingletonStoreListener()); } /** * Protected constructor which should only be used from unit tests. Production code should set * pushStateWhenCoordinator using setConfig() method instead. * * @param SingletonStoreDefaultConfig configuration instance for SingletonStoreCacheLoader */ protected SingletonStoreCacheLoader(SingletonStoreDefaultConfig config) { this(); this.config = config; } /** * Returns SingletonStoreCacheLoader's configuration instance. This method has been defined for convenience reasons * when unit testing SingletonStoreCacheLoader's configuration. * * @return instance of SingletonStoreDefaultConfig */ protected SingletonStoreDefaultConfig getSingletonStoreDefaultConfig() { return config; } /** * Returns the Future instance of a running in-memory to cache loader push task. This method has been defined for * convenience reasons when unit testing. * * @return an instance of Future */ protected Future getPushStateFuture() { return pushStateFuture; } /** * Method called when the node either becomes the coordinator or stops being the coordinator. If it becomes the * coordinator, it can optionally start the in-memory state transfer to the underlying cache store. * * @param newActiveState true if the node just became the coordinator, false if the nodes stopped being the coordinator. */ protected void activeStatusChanged(boolean newActiveState) throws PushStateException { active = newActiveState; log.debug("changed mode: " + this); if (active && config.isPushStateWhenCoordinator()) { doPushState(); } } /** * Factory method for the creation of a Callable task in charge of pushing in-memory state to cache loader. * * @return new instance of Callable whose call() method either throws an exception or returns null if the task * was successfull. */ protected Callable createPushStateTask() { return new Callable() { public Object call() throws Exception { final boolean debugEnabled = log.isDebugEnabled(); if (debugEnabled) log.debug("start pushing in-memory state to cache cacheLoader"); pushState(cache.getRoot()); if (debugEnabled) log.debug("in-memory state passed to cache cacheLoader successfully"); return null; } }; } /** * Pushes the state of a specific node by reading the node's data from the cache and putting in the cache store * via the cache loader. This method is call recursively so that it iterates through the whole cache. * * @param node instance of NodeSPI to push to the cache loader * @throws Exception if there's any issues reading the data from the cache or pushing the node's data to the cache * loader. */ protected void pushState(NodeSPI node) throws Exception { /* Put the node's data first */ Set keys = node.getKeysDirect(); Fqn fqn = node.getFqn(); for (Object aKey : keys) { Object value = cache.get(fqn, aKey); put(fqn, aKey, value); } /* Navigates to the children */ Collection children = node.getChildrenDirect(); for (NodeSPI aChildren : children) { //Map.Entry entry = (Map.Entry) aChildren; pushState(aChildren); } } /** * Method that waits for the in-memory to cache loader state to finish. This method's called in case a push state * is already in progress and we need to wait for it to finish. * * @param future instance of Future representing the on going push task * @param timeout time to wait for the push task to finish * @param unit instance of TimeUnit representing the unit of timeout */ protected void awaitForPushToFinish(Future future, int timeout, TimeUnit unit) { final boolean debugEnabled = log.isDebugEnabled(); try { if (debugEnabled) log.debug("wait for state push to cache loader to finish"); future.get(timeout, unit); } catch (TimeoutException e) { if (debugEnabled) log.debug("timed out waiting for state push to cache loader to finish"); } catch (ExecutionException e) { if (debugEnabled) log.debug("exception reported waiting for state push to cache loader to finish"); } catch (InterruptedException ie) { /* Re-assert the thread's interrupted status */ Thread.currentThread().interrupt(); if (trace) log.trace("wait for state push to cache loader to finish was interrupted"); } } /** * Called when the SingletonStoreCacheLoader discovers that the node has become the coordinator and push in memory * state has been enabled. It might not actually push the state if there's an ongoing push task running, in which * case will wait for the push task to finish. * * @throws PushStateException when the push state task reports an issue. */ private void doPushState() throws PushStateException { if (pushStateFuture == null || pushStateFuture.isDone()) { Callable task = createPushStateTask(); pushStateFuture = executor.submit(task); try { waitForTaskToFinish(pushStateFuture, config.getPushStateWhenCoordinatorTimeout(), TimeUnit.MILLISECONDS); } catch (Exception e) { throw new PushStateException("unable to complete in memory state push to cache loader", e); } } else { /* at the most, we wait for push state timeout value. if it push task finishes earlier, this call * will stop when the push task finishes, otherwise a timeout exception will be reported */ awaitForPushToFinish(pushStateFuture, config.getPushStateWhenCoordinatorTimeout(), TimeUnit.MILLISECONDS); } } /** * Waits, within a time constraint, for a task to finish. * * @param future represents the task waiting to finish. * @param timeout maximum time to wait for the time to finish. * @param unit instance of TimeUnit representing the unit of timeout * @throws Exception if any issues are reported while waiting for the task to finish */ private void waitForTaskToFinish(Future future, int timeout, TimeUnit unit) throws Exception { try { future.get(timeout, unit); } catch (TimeoutException e) { throw new Exception("task timed out", e); } catch (InterruptedException e) { /* Re-assert the thread's interrupted status */ Thread.currentThread().interrupt(); if (trace) log.trace("task was interrupted"); } finally { /* no-op if task is completed */ future.cancel(true); /* interrupt if running */ } } /** * Indicates whether the current nodes is the coordinator of the cluster. * * @param newView View instance containing the new view of the cluster * @return whether the current node is the coordinator or not. */ private boolean isCoordinator(View newView) { if (newView != null && localAddress != null) { Vector mbrs = newView.getMembers(); if (mbrs != null && mbrs.size() > 0 && localAddress.equals(mbrs.firstElement())) { /* This node is the coordinator */ return true; } return false; } /* Invalid new view, so previous value returned */ return active; } /** * Calls the underlying cache loader's operation if the current node is the coordinator. */ @Override public Object put(Fqn name, Object key, Object value) throws Exception { if (active) { return super.put(name, key, value); } return null; } /** * Calls the underlying cache loader's operation if the current node is the coordinator. */ @Override public void put(Fqn name, Map attributes) throws Exception { if (active) { super.put(name, attributes); } } /** * Calls the underlying cache loader's operation if the current node is the coordinator. */ @Override public void put(List modifications) throws Exception { if (active) { super.put(modifications); } } /** * Calls the underlying cache loader's operation if the current node is the coordinator. */ @Override public Object remove(Fqn fqn, Object key) throws Exception { if (active) { return super.remove(fqn, key); } return null; } /** * Calls the underlying cache loader's operation if the current node is the coordinator. */ @Override public void remove(Fqn fqn) throws Exception { if (active) { super.remove(fqn); } } /** * Calls the underlying cache loader's operation if the current node is the coordinator. */ @Override public void removeData(Fqn fqn) throws Exception { if (active) { super.removeData(fqn); } } /** * Calls the underlying cache loader's operation if the current node is the coordinator. */ @Override public void prepare(Object tx, List modifications, boolean one_phase) throws Exception { if (active) { super.prepare(tx, modifications, one_phase); } } /** * Calls the underlying cache loader's operation if the current node is the coordinator. */ @Override public void commit(Object tx) throws Exception { if (active) { super.commit(tx); } } /** * Calls the underlying cache loader's operation if the current node is the coordinator. */ @Override public void rollback(Object tx) { if (active) { super.rollback(tx); } } /** * Calls the underlying cache loader's operation if the current node is the coordinator. */ @Override public void storeEntireState(ObjectInputStream is) throws Exception { if (active) { super.storeEntireState(is); } } /** * Calls the underlying cache loader's operation if the current node is the coordinator. */ @Override public void storeState(Fqn subtree, ObjectInputStream is) throws Exception { if (active) { super.storeState(subtree, is); } } /** * Calls the underlying cache loader's operation if the current node is the coordinator. */ @Override public String toString() { return "loc_addr=" + localAddress + ", active=" + active; } /** * Cache listener that reacts to cluster topology changes to find out whether a new coordinator is elected. * SingletonStoreCacheLoader reacts to these changes in order to decide which node should interact with the * underlying cache store. */ @CacheListener public class SingletonStoreListener { /** * Cache started, check whether the node is the coordinator and set the singleton store cache loader's active * status. */ @CacheStarted public void cacheStarted(Event e) { localAddress = cache.getLocalAddress(); active = cache.getRPCManager().isCoordinator(); if (log.isDebugEnabled()) log.debug("cache started: " + this); } @CacheStopped public void cacheStopped(Event e) { if (log.isDebugEnabled()) log.debug("cache stopped: " + this); } /** * The cluster formation changed, so determine whether the current node stopped being the coordinator or became * the coordinator. This method can lead to an optional in memory to cache loader state push, if the current node * became the coordinator. This method will report any issues that could potentially arise from this push. */ @ViewChanged public void viewChange(ViewChangedEvent event) { boolean tmp = isCoordinator(event.getNewView()); if (active != tmp) { try { activeStatusChanged(tmp); } catch (PushStateException e) { log.error("exception reported changing nodes active status", e); } } } } /** * Exception representing any issues that arise from pushing the in-memory state to the cache loader. */ public static class PushStateException extends Exception { private static final long serialVersionUID = 5542893943730200886L; public PushStateException(String message, Throwable cause) { super(message, cause); } public PushStateException(Throwable cause) { super(cause); } } }jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/loader/AsyncCacheLoaderConfig.java0000644000175000017500000000643211004125045030765 0ustar twernertwernerpackage org.jboss.cache.loader; import org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig; import java.util.Properties; public class AsyncCacheLoaderConfig extends IndividualCacheLoaderConfig { /** * The serialVersionUID */ private static final long serialVersionUID = 5038037589485991681L; private int batchSize = 100; private boolean returnOld = true; private int queueSize = 0; private boolean useAsyncPut = true; /** * Default constructor. */ public AsyncCacheLoaderConfig() { setClassName(AsyncCacheLoader.class.getName()); } /** * For use by {@link AsyncCacheLoader}. * * @param base generic config object created by XML parsing. */ AsyncCacheLoaderConfig(IndividualCacheLoaderConfig base) { setClassName(AsyncCacheLoader.class.getName()); populateFromBaseConfig(base); } public int getBatchSize() { return batchSize; } public void setBatchSize(int batchSize) { testImmutability("batchSize"); this.batchSize = batchSize; } public int getQueueSize() { return queueSize; } public void setQueueSize(int queueSize) { testImmutability("queueSize"); this.queueSize = queueSize; } public boolean getReturnOld() { return returnOld; } public void setReturnOld(boolean returnOld) { testImmutability("returnOld"); this.returnOld = returnOld; } public boolean getUseAsyncPut() { return useAsyncPut; } public void setUseAsyncPut(boolean useAsyncPut) { testImmutability("useAsyncPut"); this.useAsyncPut = useAsyncPut; } @Override public void setProperties(Properties props) { super.setProperties(props); String s; s = props.getProperty("cache.async.batchSize"); if (s != null) { batchSize = Integer.parseInt(s); } if (batchSize <= 0) { throw new IllegalArgumentException("Invalid size: " + batchSize); } s = props.getProperty("cache.async.returnOld"); if (s != null) { returnOld = Boolean.valueOf(s); } s = props.getProperty("cache.async.queueSize"); if (s != null) { queueSize = Integer.parseInt(s); } s = props.getProperty("cache.async.put"); if (s != null) { useAsyncPut = Boolean.valueOf(s); } } @Override public boolean equals(Object obj) { if (obj instanceof AsyncCacheLoaderConfig && equalsExcludingProperties(obj)) { AsyncCacheLoaderConfig other = (AsyncCacheLoaderConfig) obj; return (batchSize == other.batchSize) && (queueSize == other.queueSize) && (returnOld == other.returnOld) && (useAsyncPut == other.useAsyncPut); } return false; } @Override public int hashCode() { int result = hashCodeExcludingProperties(); result = 31 * result + batchSize; result = 31 * result + queueSize; result = 31 * result + (returnOld ? 0 : 1); result = 31 * result + (useAsyncPut ? 0 : 1); return result; } @Override public AsyncCacheLoaderConfig clone() throws CloneNotSupportedException { return (AsyncCacheLoaderConfig) super.clone(); } }jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/loader/AsyncCacheLoader.java0000644000175000017500000002516411006101071027635 0ustar twernertwerner/** * */ package org.jboss.cache.loader; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.CacheException; import org.jboss.cache.Fqn; import org.jboss.cache.Modification; import org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig; import org.jboss.cache.util.MapCopy; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; /** * The AsyncCacheLoader is a delegating cache loader that extends * AbstractDelegatingCacheLoader overriding methods to that should not * just delegate the operation to the underlying cache loader. *

        * Read operations are done synchronously, while write (CRUD - Create, Remove, * Update, Delete) operations are done asynchronously. There is no provision * for exception handling at the moment for problems encountered with the * underlying CacheLoader during a CRUD operation, and the exception is just * logged. *

        * When configuring the CacheLoader, use the following attribute: *

        * * <attribute name="CacheLoaderAsynchronous">true</attribute> * *

        * to define whether cache loader operations are to be asynchronous. If not * specified, a cache loader operation is assumed synchronous. *

        *

        * The following additional parameters are available: *

        *
        cache.async.batchSize
        *
        Number of modifications to commit in one transaction, default is * 100. The minimum batch size is 1.
        *
        cache.async.pollWait
        *
        How long to wait before processing an incomplete batch, in * milliseconds. Default is 100. Set this to 0 to not wait before processing * available records.
        *
        cache.async.returnOld
        *
        If true, this loader returns the old values from {@link * #put} and {@link #remove} methods. Otherwise, these methods always return * null. Default is true. false improves the performance of these * operations.
        *
        cache.async.queueSize
        *
        Maximum number of entries to enqueue for asynchronous processing. * Lowering this size may help prevent out-of-memory conditions. It also may * help to prevent less records lost in the case of JVM failure. Default is * 10,000 operations.
        *
        cache.async.put
        *
        If set to false, all {@link #put} operations will be processed * synchronously, and then only the {@link #remove} operations will be * processed asynchronously. This mode may be useful for processing * expiration of messages within a separate thread and keeping other * operations synchronous for reliability. *
        *
        * For increased performance for many smaller transactions, use higher values * for cache.async.batchSize and * cache.async.pollWait. For larger sized records, use a smaller * value for cache.async.queueSize. * * @author Manik Surtani (manik.surtani@jboss.com) */ public class AsyncCacheLoader extends AbstractDelegatingCacheLoader { private static final Log log = LogFactory.getLog(AsyncCacheLoader.class); private static final boolean trace = log.isTraceEnabled(); private static AtomicInteger threadId = new AtomicInteger(0); /** * Default limit on entries to process asynchronously. */ private static final int DEFAULT_QUEUE_SIZE = 10000; private AsyncCacheLoaderConfig config; private AsyncProcessor processor; private AtomicBoolean stopped = new AtomicBoolean(true); private BlockingQueue queue = new ArrayBlockingQueue(DEFAULT_QUEUE_SIZE); public AsyncCacheLoader() { super(null); } public AsyncCacheLoader(CacheLoader cacheLoader) { super(cacheLoader); } @Override public void setConfig(IndividualCacheLoaderConfig base) { if (base instanceof AsyncCacheLoaderConfig) { config = (AsyncCacheLoaderConfig) base; } else { config = new AsyncCacheLoaderConfig(base); } if (config.getQueueSize() > 0) { queue = new ArrayBlockingQueue(config.getQueueSize()); } super.setConfig(base); } @Override public Map get(Fqn name) throws Exception { try { return super.get(name); } catch (IOException e) { // FileCacheLoader sometimes does this apparently log.trace(e); return new HashMap(); // ? } } Object get(Fqn name, Object key) throws Exception { if (config.getReturnOld()) { try { Map map = super.get(name); if (map != null) { return map.get(key); } } catch (IOException e) { // FileCacheLoader sometimes does this apparently log.trace(e); } } return null; } @Override public Object put(Fqn name, Object key, Object value) throws Exception { if (config.getUseAsyncPut()) { Object oldValue = get(name, key); Modification mod = new Modification(Modification.ModificationType.PUT_KEY_VALUE, name, key, value); enqueue(mod); return oldValue; } else { return super.put(name, key, value); } } @Override public void put(Fqn name, Map attributes) throws Exception { if (config.getUseAsyncPut()) { // JBCACHE-769 -- make a defensive copy Map attrs = (attributes == null ? null : new MapCopy(attributes)); Modification mod = new Modification(Modification.ModificationType.PUT_DATA, name, attrs); enqueue(mod); } else { super.put(name, attributes); // Let delegate make its own defensive copy } } @Override public void put(List modifications) throws Exception { if (config.getUseAsyncPut()) { for (Modification modification : modifications) { enqueue(modification); } } else { super.put(modifications); } } @Override public Object remove(Fqn name, Object key) throws Exception { Object oldValue = get(name, key); Modification mod = new Modification(Modification.ModificationType.REMOVE_KEY_VALUE, name, key); enqueue(mod); return oldValue; } @Override public void remove(Fqn name) throws Exception { Modification mod = new Modification(Modification.ModificationType.REMOVE_NODE, name); enqueue(mod); } @Override public void removeData(Fqn name) throws Exception { Modification mod = new Modification(Modification.ModificationType.REMOVE_DATA, name); enqueue(mod); } @Override public void start() throws Exception { if (log.isInfoEnabled()) log.info("Async cache loader starting: " + this); stopped.set(false); super.start(); processor = new AsyncProcessor(); processor.start(); } @Override public void stop() { stopped.set(true); if (processor != null) { processor.stop(); } super.stop(); } private void enqueue(Modification mod) throws CacheException, InterruptedException { if (stopped.get()) { throw new CacheException("AsyncCacheLoader stopped; no longer accepting more entries."); } if (trace) { log.trace("Enqueuing modification " + mod); } queue.put(mod); } /** * Processes (by batch if possible) a queue of {@link Modification}s. * * @author manik surtani */ private class AsyncProcessor implements Runnable { private Thread t; // Modifications to invoke as a single put private final List mods = new ArrayList(config.getBatchSize()); public void start() { if (t == null || !t.isAlive()) { t = new Thread(this, "AsyncCacheLoader-" + threadId.getAndIncrement()); t.setDaemon(true); t.start(); } } public void stop() { if (t != null) { t.interrupt(); try { t.join(); } catch (InterruptedException e) { } } if (!queue.isEmpty()) { log.warn("Async queue not yet empty, possibly interrupted"); } } public void run() { while (!Thread.interrupted()) { try { run0(); } catch (InterruptedException e) { break; } } try { if (trace) log.trace("process remaining batch " + mods.size()); put(mods); if (trace) log.trace("process remaining queued " + queue.size()); while (!queue.isEmpty()) { run0(); } } catch (InterruptedException e) { log.trace("remaining interrupted"); } } private void run0() throws InterruptedException { log.trace("Checking for modifications"); int i = queue.drainTo(mods, config.getBatchSize()); if (i == 0) { Modification m = queue.take(); mods.add(m); } if (trace) { log.trace("Calling put(List) with " + mods.size() + " modifications"); } put(mods); mods.clear(); } private void put(List mods) { try { AsyncCacheLoader.super.put(mods); } catch (Exception e) { if (log.isWarnEnabled()) log.warn("Failed to process async modifications: " + e); log.debug("Exception: ", e); } } @Override public String toString() { return "TQ t=" + t; } } @Override public String toString() { return super.toString() + " delegate=[" + super.getCacheLoader() + "]" + " processor=" + processor + " stopped=" + stopped + " batchSize=" + config.getBatchSize() + " returnOld=" + config.getReturnOld() + " asyncPut=" + config.getUseAsyncPut() + " queue.remainingCapacity()=" + queue.remainingCapacity() + " queue.peek()=" + queue.peek(); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/loader/ReadOnlyDelegatingCacheLoader.java0000644000175000017500000000514011004125045032256 0ustar twernertwernerpackage org.jboss.cache.loader; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.Fqn; import org.jboss.cache.Modification; import java.io.ObjectInputStream; import java.util.List; import java.util.Map; /** * Provides ignoreModifications features to all cache loaders. * * @author Manik Surtani * @since 2.1.0 */ public class ReadOnlyDelegatingCacheLoader extends AbstractDelegatingCacheLoader { private static final Log log = LogFactory.getLog(ReadOnlyDelegatingCacheLoader.class); public ReadOnlyDelegatingCacheLoader(CacheLoader cl) { super(cl); } @Override public Object put(Fqn name, Object key, Object value) throws Exception { log.trace("Not delegating write operation to underlying cache loader"); Map map = get(name); return (map == null) ? null : map.get(key); } @Override public void put(Fqn name, Map attributes) throws Exception { log.trace("Not delegating write operation to underlying cache loader"); } @Override public void put(List modifications) throws Exception { log.trace("Not delegating write operation to underlying cache loader"); } @Override public Object remove(Fqn fqn, Object key) throws Exception { log.trace("Not delegating write operation to underlying cache loader"); Map map = get(fqn); return (map == null) ? null : map.get(key); } @Override public void remove(Fqn fqn) throws Exception { log.trace("Not delegating write operation to underlying cache loader"); } @Override public void removeData(Fqn fqn) throws Exception { log.trace("Not delegating write operation to underlying cache loader"); } @Override public void prepare(Object tx, List modifications, boolean one_phase) throws Exception { log.trace("Not delegating write operation to underlying cache loader"); } @Override public void commit(Object tx) throws Exception { log.trace("Not delegating write operation to underlying cache loader"); } @Override public void rollback(Object tx) { log.trace("Not delegating write operation to underlying cache loader"); } @Override public void storeEntireState(ObjectInputStream is) throws Exception { log.trace("Not delegating write operation to underlying cache loader"); } @Override public void storeState(Fqn subtree, ObjectInputStream is) throws Exception { log.trace("Not delegating write operation to underlying cache loader"); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/loader/ConnectionFactory.java0000644000175000017500000000141410573103535030143 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.loader; import java.sql.Connection; import java.sql.SQLException; /** * ConnectionFactory interface defining the operations to be defined by connection providers * * @author Hany Mesha * @author Galder Zamarreno */ public interface ConnectionFactory { void setConfig(AdjListJDBCCacheLoaderConfig config); void start() throws Exception; Connection getConnection() throws SQLException; void prepare(Object tx); void commit(Object tx); void rollback(Object tx); void close(Connection con); void stop(); } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/loader/AdjListJDBCCacheLoaderConfig.java0000644000175000017500000004345711067143565031715 0ustar twernertwernerpackage org.jboss.cache.loader; import org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig; import org.jboss.cache.util.Util; import java.util.Properties; /** * AdjListJDBCCacheLoaderConfig * * @author Manik Surtani (manik@jboss.org) * @author Galder Zamarreno */ @SuppressWarnings("deprecation") public class AdjListJDBCCacheLoaderConfig extends IndividualCacheLoaderConfig { /** * The serialVersionUID */ private static final long serialVersionUID = -8371846151643130281L; private static final boolean CREATE_TABLE_DEFAULT = true; private static final boolean DROP_TABLE_DEFAULT = false; private static final String PARENT_COLUMN_DEFAULT = "parent"; private static final String NODE_TYPE_DEFAULT = "blob"; private static final String NODE_COLUMN_DEFAULT = "node"; private static final String FQN_TYPE_DEFAULT = "varchar(255)"; private static final String FQN_COLUMN_DEFAULT = "fqn"; private static final String PRIMARY_KEY_DEFAULT = "jbosscache_pk"; private static final String TABLE_DEFAULT = "jbosscache"; private boolean createTable = CREATE_TABLE_DEFAULT; private String createTableDDL; private String datasourceName; private String deleteAllSql; private String deleteNodeSql; private boolean dropTable = DROP_TABLE_DEFAULT; private String dropTableDDL; private String driverClass; private String insertNodeSql; private String jdbcURL; private String jdbcUser; private String jdbcPassword; private String selectChildFqnsSql; private String selectChildNamesSql; private String selectNodeSql; private String updateNodeSql; private String updateTableSql; private String existsSql; private String connectionFactoryClass; private String primaryKey = PRIMARY_KEY_DEFAULT; private String fqnType = FQN_TYPE_DEFAULT; private String nodeType = NODE_TYPE_DEFAULT; private String parentColumn = PARENT_COLUMN_DEFAULT; protected String table = TABLE_DEFAULT; protected String nodeColumn = NODE_COLUMN_DEFAULT; protected String fqnColumn = FQN_COLUMN_DEFAULT; public AdjListJDBCCacheLoaderConfig() { } /** * For use by {@link JDBCCacheLoaderOld}. * * @param base generic config object created by XML parsing. */ AdjListJDBCCacheLoaderConfig(IndividualCacheLoaderConfig base) { populateFromBaseConfig(base); } public boolean getCreateTable() { return createTable; } public void setCreateTable(boolean createTable) { testImmutability("createTable"); this.createTable = createTable; } public String getCreateTableDDL() { if (createTableDDL == null) { setCreateTableDDL(constructCreateTableDDL()); } return createTableDDL; } public void setCreateTableDDL(String createTableDDL) { testImmutability("createTableDDL"); this.createTableDDL = createTableDDL; } public String getDatasourceName() { return datasourceName; } public void setDatasourceName(String datasourceName) { testImmutability("datasourceName"); this.datasourceName = datasourceName; } public String getDeleteAllSql() { if (deleteAllSql == null) { setDeleteAllSql(constructDeleteAllSql()); } return deleteAllSql; } public void setDeleteAllSql(String deleteAllSql) { testImmutability("deleteAllSql"); this.deleteAllSql = deleteAllSql; } public String getDeleteNodeSql() { if (deleteNodeSql == null) { setDeleteNodeSql(constructDeleteNodeSql()); } return deleteNodeSql; } public void setDeleteNodeSql(String deleteNodeSql) { testImmutability("deleteNodeSql"); this.deleteNodeSql = deleteNodeSql; } public String getDriverClass() { return driverClass; } public void setDriverClass(String driverClass) { testImmutability("driverClass"); this.driverClass = driverClass; } public boolean getDropTable() { return dropTable; } public void setDropTable(boolean dropTable) { testImmutability("dropTable"); this.dropTable = dropTable; } public String getInsertNodeSql() { if (insertNodeSql == null) { setInsertNodeSql(constructInsertNodeSql()); } return insertNodeSql; } public String getExistsSql() { if (existsSql == null) { setExistsSql(constructExistsSql()); } return existsSql; } public void setExistsSql(String existsSql) { testImmutability("existsSql"); this.existsSql = existsSql; } public void setInsertNodeSql(String insertNodeSql) { testImmutability("insertNodeSql"); this.insertNodeSql = insertNodeSql; } public String getSelectChildFqnsSql() { if (selectChildFqnsSql == null) { setSelectChildFqnsSql(constructSelectChildNamesSql()); } return selectChildFqnsSql; } public void setSelectChildFqnsSql(String selectChildFqnsSql) { testImmutability("selectChildFqnsSql"); this.selectChildFqnsSql = selectChildFqnsSql; } public String getSelectNodeSql() { if (selectNodeSql == null) { setSelectNodeSql(constructSelectNodeSql()); } return selectNodeSql; } public void setSelectNodeSql(String selectNodeSql) { testImmutability("selectNodeSql"); this.selectNodeSql = selectNodeSql; } public String getTable() { return table; } public void setTable(String table) { testImmutability("table"); this.table = table; } public String getUpdateTableSql() { return updateTableSql; } public void setUpdateTableSql(String updateTableSql) { testImmutability("updateTableSql"); this.updateTableSql = updateTableSql; } public String getDropTableDDL() { if (dropTableDDL == null) { setDropTableDDL(constructDropTableDDL()); } return dropTableDDL; } public void setDropTableDDL(String dropTableDDL) { testImmutability("dropTableDDL"); this.dropTableDDL = dropTableDDL; } public String getSelectChildNamesSql() { if (selectChildNamesSql == null) { setSelectChildNamesSql(constructSelectChildNamesSql()); } return selectChildNamesSql; } public void setSelectChildNamesSql(String selectChildNamesSql) { testImmutability("selectChildNamesSql"); this.selectChildNamesSql = selectChildNamesSql; } public String getUpdateNodeSql() { if (updateNodeSql == null) { setUpdateNodeSql(constructUpdateNodeSql()); } return updateNodeSql; } public void setUpdateNodeSql(String updateNodeSql) { testImmutability("updateNodeSql"); this.updateNodeSql = updateNodeSql; } public String getJdbcPassword() { return jdbcPassword; } public void setJdbcPassword(String jdbcPassword) { testImmutability("jdbcPassword"); this.jdbcPassword = jdbcPassword; } public String getJdbcURL() { return jdbcURL; } public void setJdbcURL(String jdbcURL) { testImmutability("jdbcURL"); this.jdbcURL = jdbcURL; } public String getJdbcUser() { return jdbcUser; } public void setJdbcUser(String jdbcUser) { testImmutability("jdbcUser"); this.jdbcUser = jdbcUser; } public String getConnectionFactoryClass() { return connectionFactoryClass; } public void setConnectionFactoryClass(String connectionFactoryClass) { testImmutability("connectionFactoryClass"); this.connectionFactoryClass = connectionFactoryClass; } public String getPrimaryKey() { return primaryKey; } public void setPrimaryKey(String primaryKey) { testImmutability("primaryKey"); this.primaryKey = primaryKey; } public String getFqnType() { return fqnType; } public void setFqnType(String fqnType) { testImmutability("fqnType"); this.fqnType = fqnType; } public String getNodeType() { return nodeType; } public void setNodeType(String nodeType) { testImmutability("nodeType"); this.nodeType = nodeType; } public String getParentColumn() { return parentColumn; } public void setParentColumn(String parentColumn) { testImmutability("parentColumn"); this.parentColumn = parentColumn; } public String getNodeColumn() { return nodeColumn; } public void setNodeColumn(String nodeColumn) { testImmutability("nodeColumn"); this.nodeColumn = nodeColumn; } public String getFqnColumn() { return fqnColumn; } public void setFqnColumn(String fqnColumn) { testImmutability("fqnColumn"); this.fqnColumn = fqnColumn; } @Override public void setProperties(Properties props) { super.setProperties(props); datasourceName = props.getProperty("cache.jdbc.datasource"); if (datasourceName == null) { this.driverClass = AdjListJDBCCacheLoader.getRequiredProperty(props, "cache.jdbc.driver"); this.jdbcURL = AdjListJDBCCacheLoader.getRequiredProperty(props, "cache.jdbc.url"); this.jdbcUser = AdjListJDBCCacheLoader.getRequiredProperty(props, "cache.jdbc.user"); this.jdbcPassword = AdjListJDBCCacheLoader.getRequiredProperty(props, "cache.jdbc.password"); if (log.isDebugEnabled()) { log.debug("Properties: " + "cache.jdbc.url=" + jdbcURL + ", cache.jdbc.driver=" + driverClass + ", cache.jdbc.user=" + jdbcUser + ", cache.jdbc.password=" + jdbcPassword + ", cache.jdbc.table=" + table); } } String prop = props.getProperty("cache.jdbc.table.create"); this.createTable = prop == null ? CREATE_TABLE_DEFAULT : Boolean.valueOf(prop); prop = props.getProperty("cache.jdbc.table.drop"); this.dropTable = prop == null ? DROP_TABLE_DEFAULT : Boolean.valueOf(prop); this.table = props.getProperty("cache.jdbc.table.name", TABLE_DEFAULT); primaryKey = props.getProperty("cache.jdbc.table.primarykey", PRIMARY_KEY_DEFAULT); fqnColumn = props.getProperty("cache.jdbc.fqn.column", FQN_COLUMN_DEFAULT); fqnType = props.getProperty("cache.jdbc.fqn.type", FQN_TYPE_DEFAULT); nodeColumn = props.getProperty("cache.jdbc.node.column", NODE_COLUMN_DEFAULT); nodeType = props.getProperty("cache.jdbc.node.type", NODE_TYPE_DEFAULT); parentColumn = props.getProperty("cache.jdbc.parent.column", PARENT_COLUMN_DEFAULT); selectChildNamesSql = constructSelectChildNamesSql(); deleteNodeSql = constructDeleteNodeSql(); deleteAllSql = constructDeleteAllSql(); /* select child fqns and select child names sql is the same */ selectChildFqnsSql = constructSelectChildNamesSql(); insertNodeSql = constructInsertNodeSql(); updateNodeSql = constructUpdateNodeSql(); selectNodeSql = constructSelectNodeSql(); createTableDDL = constructCreateTableDDL(); dropTableDDL = constructDropTableDDL(); connectionFactoryClass = props.getProperty("cache.jdbc.connection.factory", "org.jboss.cache.loader.NonManagedConnectionFactory"); } private String constructDropTableDDL() { return "drop table " + table; } private String constructCreateTableDDL() { return "create table " + table + "(" + fqnColumn + " " + fqnType + " not null, " + nodeColumn + " " + nodeType + ", " + parentColumn + " " + fqnType + ", constraint " + primaryKey + " primary key (" + fqnColumn + "))"; } @Override public boolean equals(Object obj) { if (obj instanceof AdjListJDBCCacheLoaderConfig && equalsExcludingProperties(obj)) { AdjListJDBCCacheLoaderConfig other = (AdjListJDBCCacheLoaderConfig) obj; return (this.createTable == other.createTable) && Util.safeEquals(createTableDDL, other.createTableDDL) && Util.safeEquals(datasourceName, other.datasourceName) && Util.safeEquals(deleteAllSql, other.deleteAllSql) && Util.safeEquals(deleteNodeSql, other.deleteNodeSql) && Util.safeEquals(driverClass, other.driverClass) && (dropTable == other.dropTable) && Util.safeEquals(dropTableDDL, other.dropTableDDL) && Util.safeEquals(insertNodeSql, other.insertNodeSql) && Util.safeEquals(jdbcPassword, other.jdbcPassword) && Util.safeEquals(jdbcURL, other.jdbcURL) && Util.safeEquals(jdbcUser, other.jdbcUser) && Util.safeEquals(selectChildFqnsSql, other.selectChildFqnsSql) && Util.safeEquals(selectChildNamesSql, other.selectChildNamesSql) && Util.safeEquals(selectNodeSql, other.selectNodeSql) && Util.safeEquals(table, other.table) && Util.safeEquals(updateNodeSql, other.updateNodeSql) && Util.safeEquals(updateTableSql, other.updateTableSql) && Util.safeEquals(connectionFactoryClass, other.connectionFactoryClass) && Util.safeEquals(primaryKey, other.primaryKey) && Util.safeEquals(nodeType, other.nodeType) && Util.safeEquals(fqnType, other.fqnType) && Util.safeEquals(parentColumn, other.parentColumn); } return false; } @Override public int hashCode() { int result = hashCodeExcludingProperties(); result = 31 * result + (createTable ? 0 : 1); result = 31 * result + (createTableDDL == null ? 0 : createTableDDL.hashCode()); result = 31 * result + (datasourceName == null ? 0 : datasourceName.hashCode()); result = 31 * result + (deleteAllSql == null ? 0 : deleteAllSql.hashCode()); result = 31 * result + (deleteNodeSql == null ? 0 : deleteNodeSql.hashCode()); result = 31 * result + (driverClass == null ? 0 : driverClass.hashCode()); result = 31 * result + (dropTable ? 0 : 1); result = 31 * result + (dropTableDDL == null ? 0 : dropTableDDL.hashCode()); result = 31 * result + (insertNodeSql == null ? 0 : insertNodeSql.hashCode()); result = 31 * result + (jdbcPassword == null ? 0 : jdbcPassword.hashCode()); result = 31 * result + (jdbcUser == null ? 0 : jdbcUser.hashCode()); result = 31 * result + (jdbcURL == null ? 0 : jdbcURL.hashCode()); result = 31 * result + (selectChildFqnsSql == null ? 0 : selectChildFqnsSql.hashCode()); result = 31 * result + (selectChildNamesSql == null ? 0 : selectChildNamesSql.hashCode()); result = 31 * result + (selectNodeSql == null ? 0 : selectNodeSql.hashCode()); result = 31 * result + (table == null ? 0 : table.hashCode()); result = 31 * result + (updateNodeSql == null ? 0 : updateNodeSql.hashCode()); result = 31 * result + (updateTableSql == null ? 0 : updateTableSql.hashCode()); result = 31 * result + (connectionFactoryClass == null ? 0 : connectionFactoryClass.hashCode()); result = 31 * result + (primaryKey == null ? 0 : primaryKey.hashCode()); result = 31 * result + (nodeType == null ? 0 : nodeType.hashCode()); result = 31 * result + (fqnType == null ? 0 : fqnType.hashCode()); result = 31 * result + (parentColumn == null ? 0 : parentColumn.hashCode()); return result; } @Override public AdjListJDBCCacheLoaderConfig clone() throws CloneNotSupportedException { return (AdjListJDBCCacheLoaderConfig) super.clone(); } private String constructSelectNodeSql() { return "select " + nodeColumn + " from " + table + " where " + fqnColumn + "=?"; } private String constructUpdateNodeSql() { return "update " + table + " set " + nodeColumn + "=? where " + fqnColumn + "=?"; } private String constructDeleteAllSql() { return "delete from " + table; } private String constructDeleteNodeSql() { return "delete from " + table + " where " + fqnColumn + "=?"; } private String constructSelectChildNamesSql() { return "select " + fqnColumn + " from " + table + " where " + parentColumn + "=?"; } private String constructExistsSql() { return "select '1' from " + table + " where " + fqnColumn + "=?"; } private String constructInsertNodeSql() { // This SQL string takes in 4 params - fqn, node (serialized data), parent, and fqn AGAIN. // the benefit of this is is that it will run without failing even if the row already exists, so you don't need // to check if the row exists before running this query. Returns '1' if the row was inserted, '0' otherwise, // but does NOT fail on primary key conflict. // the 'dummy' table, table_D, *must* exist though, and could contain just a single dummy constant row. return "INSERT INTO " + table + " (" + fqnColumn + ", " + nodeColumn + ", " + parentColumn + ") SELECT ?, ?, ? FROM " + table + "_D WHERE NOT EXISTS (SELECT " + fqnColumn + " FROM " + table + " WHERE " + fqnColumn + " = ?)"; } public String getDummyTableCreationDDL() { return "create table " + table + "_D (i CHAR)"; } public String getDummyTableRemovalDDL() { return "drop table " + table + "_D"; } public String getDummyTablePopulationSql() { return "insert into " + table + "_D values ('x')"; } }jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/loader/LocalDelegatingCacheLoaderConfig.java0000644000175000017500000000303011032145203032714 0ustar twernertwernerpackage org.jboss.cache.loader; import org.jboss.cache.Cache; import org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig; public class LocalDelegatingCacheLoaderConfig extends IndividualCacheLoaderConfig { private static final long serialVersionUID = 4626734068542420865L; private transient Cache delegate; public LocalDelegatingCacheLoaderConfig() { setClassName(LocalDelegatingCacheLoader.class.getName()); } /** * For use by {@link org.jboss.cache.loader.FileCacheLoader}. * * @param base generic config object created by XML parsing. */ LocalDelegatingCacheLoaderConfig(IndividualCacheLoaderConfig base) { setClassName(LocalDelegatingCacheLoader.class.getName()); populateFromBaseConfig(base); } @Override public boolean equals(Object obj) { return obj instanceof LocalDelegatingCacheLoaderConfig && equalsExcludingProperties(obj) && delegate == ((LocalDelegatingCacheLoaderConfig) obj).delegate; } @Override public int hashCode() { return 31 * hashCodeExcludingProperties() + (delegate == null ? 0 : delegate.hashCode()); } @Override public LocalDelegatingCacheLoaderConfig clone() throws CloneNotSupportedException { LocalDelegatingCacheLoaderConfig clone = (LocalDelegatingCacheLoaderConfig) super.clone(); clone.delegate = delegate; return clone; } public Cache getDelegate() { return delegate; } public void setDelegate(Cache delegate) { this.delegate = delegate; } }jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/loader/NonManagedConnectionFactory.java0000644000175000017500000001177711006101071032071 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.loader; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; /** * Standard connection factory for standalone JBossCache implementations without connection pooling capabilities. * * @author Hany Mesha * @author Galder Zamarreno */ class NonManagedConnectionFactory implements ConnectionFactory { private static final Log log = LogFactory.getLog(NonManagedConnectionFactory.class); private static final boolean trace = log.isTraceEnabled(); static final ThreadLocal connection = new ThreadLocal(); private String url; private String usr; private String pwd; private String driverClass; public void setConfig(AdjListJDBCCacheLoaderConfig config) { url = config.getJdbcURL(); usr = config.getJdbcUser(); pwd = config.getJdbcPassword(); driverClass = config.getDriverClass(); } public void start() throws Exception { loadDriver(driverClass); } public void prepare(Object tx) { Connection con = getConnection(); try { if (con.getAutoCommit()) { con.setAutoCommit(false); } } catch (Exception e) { reportAndRethrowError("Failed to set auto-commit", e); } /* Connection set in ThreadLocal, no reason to return. It was previously returned for legacy purpouses and to trace log the connection opening in JDBCCacheLoader. */ connection.set(con); if (trace) { log.trace("opened tx connection: tx=" + tx + ", con=" + con); } } public Connection getConnection() { Connection con = connection.get(); if (con == null) { try { con = checkoutConnection(); // connection.set(con); } catch (SQLException e) { reportAndRethrowError("Failed to get connection for url=" + url + ", user=" + usr + ", password=" + pwd, e); } } if (trace) { log.trace("using connection: " + con); } return con; } public Connection checkoutConnection() throws SQLException { return DriverManager.getConnection(url, usr, pwd); } public void commit(Object tx) { Connection con = connection.get(); if (con == null) { throw new IllegalStateException("Failed to commit: thread is not associated with the connection!"); } try { con.commit(); if (trace) { log.trace("committed tx=" + tx + ", con=" + con); } } catch (SQLException e) { reportAndRethrowError("Failed to commit", e); } finally { closeTxConnection(con); } } public void rollback(Object tx) { Connection con = connection.get(); try { con.rollback(); if (trace) { log.trace("rolledback tx=" + tx + ", con=" + con); } } catch (SQLException e) { reportAndRethrowError("Failed to rollback", e); } finally { closeTxConnection(con); } } public void close(Connection con) { if (con != null && con != connection.get()) { try { con.close(); if (trace) { log.trace("closed non tx connection: " + con); } } catch (SQLException e) { log.warn("Failed to close connection " + con, e); } } } public void stop() { } public String getUrl() { return url; } public String getUsr() { return usr; } public String getPwd() { return pwd; } public String getDriverClass() { return driverClass; } protected void loadDriver(String drv) { try { if (trace) { log.trace("Attempting to load driver: " + drv); } Class.forName(drv).newInstance(); } catch (Exception e) { reportAndRethrowError("Failed to load driver " + drv, e); } } private void closeTxConnection(Connection con) { safeClose(con); connection.set(null); } private void safeClose(Connection con) { if (con != null) { try { con.close(); } catch (SQLException e) { log.warn("Failed to close connection", e); } } } private void reportAndRethrowError(String message, Exception cause) throws IllegalStateException { log.error(message, cause); throw new IllegalStateException(message, cause); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/loader/JDBCCacheLoaderConfig.java0000644000175000017500000001017011013137335030411 0ustar twernertwernerpackage org.jboss.cache.loader; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig; import java.util.Properties; /** * Builds the different SQLs needed by JDBCCacheLoader. * * @author Mircea.Markus@iquestint.com * @author Galder Zamarreno * @version 1.0 */ public class JDBCCacheLoaderConfig extends AdjListJDBCCacheLoaderConfig { private static final long serialVersionUID = -8371846151643130271L; private static final Log log = LogFactory.getLog(JDBCCacheLoaderConfig.class); private String deleteNodeSql; private String recursiveChildrenSql; private String nodeCountSql; private String sqlConcat; private String startingWith; private String appendSepparator; public JDBCCacheLoaderConfig(IndividualCacheLoaderConfig base) { super(base); setClassName(JDBCCacheLoader.class.getName()); } public JDBCCacheLoaderConfig() { setClassName(JDBCCacheLoader.class.getName()); } @Override public void setProperties(Properties props) { super.setProperties(props); sqlConcat = props.getProperty("cache.jdbc.sql-concat"); disectSqlConcat(); deleteNodeSql = constructDeleteNodeSql(); recursiveChildrenSql = constructRecursiveChildrenSql(); nodeCountSql = constructNodeCountSql(); } /** * Returns the sql string for removing a node and all its children. */ @Override public String getDeleteNodeSql() { if (startingWith == null || appendSepparator == null || deleteNodeSql == null) { disectSqlConcat(); setDeleteNodeSql(constructDeleteNodeSql()); } return deleteNodeSql; } /** * Returns an sql that will return a node and all its children. */ public String getRecursiveChildrenSql() { if (startingWith == null || appendSepparator == null || recursiveChildrenSql == null) { disectSqlConcat(); setRecursiveChildrenSql(constructRecursiveChildrenSql()); } return recursiveChildrenSql; } public void setRecursiveChildrenSql(String recursiveChildrenSql) { this.recursiveChildrenSql = recursiveChildrenSql; } /** * Returns an sql that will count all the persisted node. */ public String getNodeCountSql() { if (startingWith == null || appendSepparator == null || nodeCountSql == null) { disectSqlConcat(); setNodeCountSql(constructNodeCountSql()); } return nodeCountSql; } public void setNodeCountSql(String nodeCountSql) { this.nodeCountSql = nodeCountSql; } public String getSqlConcat() { return sqlConcat; } public void setSqlConcat(String sqlConcat) { testImmutability("sqlConcat"); this.sqlConcat = sqlConcat; } public String getStartingWith() { return startingWith; } public void setStartingWith(String startingWith) { this.startingWith = startingWith; } public String getAppendSepparator() { return appendSepparator; } public void setAppendSepparator(String appendSepparator) { this.appendSepparator = appendSepparator; } private void disectSqlConcat() { if (sqlConcat == null) { log.info("Missiing JDBCCacheLoader config 'cache.jdbc.sql-concat', using default value:'concat(1,2)'"); sqlConcat = "concat(1,2)"; } startingWith = sqlConcat.replace('1', '?').replace("2", "'%'"); //concat(?, '%') appendSepparator = sqlConcat.replace("1", fqnColumn).replace("2", "'/'"); //concat(fqnColumn, '/') } private String constructNodeCountSql() { return "select count(*) from " + table; } private String constructRecursiveChildrenSql() { return "select " + fqnColumn + "," + nodeColumn + " from " + table + " where " + appendSepparator + " like " + startingWith; } private String constructDeleteNodeSql() { return "delete from " + table + " where " + appendSepparator + " like " + startingWith; } }jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/loader/AbstractCacheLoader.java0000644000175000017500000002735711032145203030335 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.loader; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.CacheException; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.Modification; import org.jboss.cache.Region; import org.jboss.cache.RegionManager; import org.jboss.cache.buddyreplication.BuddyFqnTransformer; import org.jboss.cache.buddyreplication.BuddyManager; import org.jboss.cache.marshall.Marshaller; import org.jboss.cache.marshall.NodeData; import org.jboss.cache.marshall.NodeDataExceptionMarker; import org.jboss.cache.marshall.NodeDataMarker; import org.jboss.cache.util.MapCopy; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; /** * A convenience abstract implementation of a {@link CacheLoader}. Specific methods to note are methods like * {@link #storeState(org.jboss.cache.Fqn,java.io.ObjectInputStream)}, {@link #loadState(org.jboss.cache.Fqn,java.io.ObjectOutputStream)}, * {@link #storeEntireState(java.io.ObjectInputStream)} and {@link #loadEntireState(java.io.ObjectOutputStream)} which have convenience * implementations here. *

        * Also useful to note is the implementation of {@link #put(java.util.List)}, used during the prepare phase of a transaction. *

        * * @author Manik Surtani (manik@jboss.org) * @author Galder Zamarreno * @since 2.0.0 */ public abstract class AbstractCacheLoader implements CacheLoader { protected CacheSPI cache; protected RegionManager regionManager; private static final Log log = LogFactory.getLog(AbstractCacheLoader.class); private static final boolean trace = log.isTraceEnabled(); protected BuddyFqnTransformer buddyFqnTransformer = new BuddyFqnTransformer(); /** * HashMap>. List of open transactions. Note that this is purely transient, as * we don't use a log, recovery is not available */ protected Map> transactions = new ConcurrentHashMap>(); public void put(Fqn fqn, Map attributes, boolean erase) throws Exception { if (erase) { removeData(fqn); } // JBCACHE-769 -- make a defensive copy Map attrs = (attributes == null ? null : new MapCopy(attributes)); put(fqn, attrs); } public void storeEntireState(ObjectInputStream is) throws Exception { storeState(Fqn.ROOT, is); } @SuppressWarnings("unchecked") public void storeState(Fqn subtree, ObjectInputStream in) throws Exception { // remove entire existing state this.remove(subtree); boolean moveToBuddy = subtree.isChildOf(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN) && subtree.size() > 1; // store new state Object objectFromStream = cache.getMarshaller().objectFromObjectStream(in); if (objectFromStream instanceof NodeDataMarker) { // no persistent state sent across; return? return; } if (objectFromStream instanceof NodeDataExceptionMarker) { NodeDataExceptionMarker ndem = (NodeDataExceptionMarker) objectFromStream; throw new CacheException("State provider cacheloader at node " + ndem.getCacheNodeIdentity() + " threw exception during loadState (see Caused by)", ndem.getCause()); } List nodeData = (List) objectFromStream; for (Object aNodeData : nodeData) { NodeData nd = (NodeData) aNodeData; if (nd.isExceptionMarker()) { NodeDataExceptionMarker ndem = (NodeDataExceptionMarker) nd; throw new CacheException("State provider cacheloader at node " + ndem.getCacheNodeIdentity() + " threw exception during loadState (see Caused by)", ndem.getCause()); } } storeStateHelper(subtree, nodeData, moveToBuddy); } protected void storeStateHelper(Fqn subtree, List nodeData, boolean moveToBuddy) throws Exception { for (Object aNodeData : nodeData) { NodeData nd = (NodeData) aNodeData; if (nd.isMarker()) break; Fqn fqn; if (moveToBuddy) { fqn = buddyFqnTransformer.getBackupFqn(subtree, nd.getFqn()); } else { fqn = nd.getFqn(); } if (nd.getAttributes() != null) { this.put(fqn, nd.getAttributes(), true);// creates a node with 0 or more attributes } else { this.put(fqn, null);// creates a node with null attributes } } } public void loadEntireState(ObjectOutputStream os) throws Exception { loadState(Fqn.ROOT, os); } public void loadState(Fqn subtree, ObjectOutputStream os) throws Exception { // ClassLoader currentCL = Thread.currentThread().getContextClassLoader(); // try // { // Set the TCCL to any classloader registered for subtree // setUnmarshallingClassLoader(subtree); loadStateHelper(subtree, os); // } // finally // { // Thread.currentThread().setContextClassLoader(currentCL); // } } public void setCache(CacheSPI c) { this.cache = c; } public void setRegionManager(RegionManager regionManager) { this.regionManager = regionManager; } protected void regionAwareMarshall(Fqn fqn, Object toMarshall) throws Exception { Region r = regionManager == null ? null : regionManager.getValidMarshallingRegion(fqn); ClassLoader originalClassLoader = null; boolean needToResetLoader = false; Thread current = null; if (r != null) { // set the region's class loader as the thread's context classloader needToResetLoader = true; current = Thread.currentThread(); originalClassLoader = current.getContextClassLoader(); current.setContextClassLoader(r.getClassLoader()); } try { doMarshall(fqn, toMarshall); } finally { if (needToResetLoader) current.setContextClassLoader(originalClassLoader); } } protected Object regionAwareUnmarshall(Fqn fqn, Object toUnmarshall) throws Exception { Region r = regionManager == null ? null : regionManager.getValidMarshallingRegion(fqn); ClassLoader originalClassLoader = null; boolean needToResetLoader = false; Thread current = null; if (r != null) { if (trace) log.trace("Using region " + r.getFqn() + ", which has registered class loader " + r.getClassLoader() + " as a context class loader."); // set the region's class loader as the thread's context classloader needToResetLoader = true; current = Thread.currentThread(); originalClassLoader = current.getContextClassLoader(); current.setContextClassLoader(r.getClassLoader()); } try { return doUnmarshall(fqn, toUnmarshall); } finally { if (needToResetLoader) current.setContextClassLoader(originalClassLoader); } } protected void doMarshall(Fqn fqn, Object toMarshall) throws Exception { throw new RuntimeException("Should be overridden"); } protected Object doUnmarshall(Fqn fqn, Object toUnmarshall) throws Exception { throw new RuntimeException("Should be overridden"); } /** * Do a preorder traversal: visit the node first, then the node's children */ protected void loadStateHelper(Fqn fqn, ObjectOutputStream out) throws Exception { List list = new LinkedList(); getNodeDataList(fqn, list); if (trace) log.trace("Loading state of " + list.size() + " nodes into stream"); cache.getMarshaller().objectToObjectStream(list, out, fqn); } protected void getNodeDataList(Fqn fqn, List list) throws Exception { Map attrs; Set childrenNames; String childName; Fqn tmpFqn; NodeData nd; // first handle the current node attrs = get(fqn); if (attrs == null || attrs.size() == 0) { nd = new NodeData(fqn); } else { nd = new NodeData(fqn, attrs); } //out.writeObject(nd); list.add(nd); // then visit the children childrenNames = getChildrenNames(fqn); if (childrenNames == null) { return; } for (Object childrenName : childrenNames) { childName = (String) childrenName; tmpFqn = Fqn.fromRelativeElements(fqn, childName); if (!cache.getInternalFqns().contains(tmpFqn)) getNodeDataList(tmpFqn, list); } } public void put(List modifications) throws Exception { for (Modification m : modifications) { switch (m.getType()) { case PUT_DATA: put(m.getFqn(), m.getData()); break; case PUT_DATA_ERASE: removeData(m.getFqn()); put(m.getFqn(), m.getData()); break; case PUT_KEY_VALUE: put(m.getFqn(), m.getKey(), m.getValue()); break; case REMOVE_DATA: removeData(m.getFqn()); break; case REMOVE_KEY_VALUE: remove(m.getFqn(), m.getKey()); break; case REMOVE_NODE: remove(m.getFqn()); break; case MOVE: // involve moving all children too, _move(m.getFqn(), m.getFqn2()); break; default: throw new CacheException("Unknown modificatiobn " + m.getType()); } } } private void _move(Fqn fqn, Fqn parent) throws Exception { Object name = fqn.getLastElement(); Fqn newFqn = Fqn.fromRelativeElements(parent, name); // start deep. Set childrenNames = getChildrenNames(fqn); if (childrenNames != null) { for (Object c : childrenNames) { _move(Fqn.fromRelativeElements(fqn, c), newFqn); } } // get data for node. Map data = get(fqn); if (data != null)// if null, then the node never existed. Don't bother removing? { remove(fqn); put(newFqn, data); } } protected Marshaller getMarshaller() { return cache.getMarshaller(); } // empty implementations for loaders that do not wish to implement lifecycle. public void create() throws Exception { } public void start() throws Exception { } public void stop() { } public void destroy() { } // Adds simple transactional capabilities to cache loaders that are inherently non-transactional. If your cache loader implementation // is tansactional though, then override these. public void prepare(Object tx, List modifications, boolean one_phase) throws Exception { if (one_phase) { put(modifications); } else { transactions.put(tx, modifications); } } public void commit(Object tx) throws Exception { List modifications = transactions.remove(tx); if (modifications == null) { throw new Exception("transaction " + tx + " not found in transaction table"); } put(modifications); } public void rollback(Object tx) { transactions.remove(tx); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/loader/JDBCCacheLoader.java0000755000175000017500000002265311075122042027274 0ustar twernertwernerpackage org.jboss.cache.loader; import net.jcip.annotations.ThreadSafe; import org.apache.commons.logging.LogFactory; import org.jboss.cache.Fqn; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig; import org.jboss.cache.marshall.NodeData; import java.io.InputStream; import java.io.ObjectInputStream; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.HashMap; import java.util.List; import java.util.Map; /** * JDBC implementation of AdjListJDBCCacheLoader. * Represents a faster alternative than JDBCCacheLoaderOld and relies on the same database structrure. * It is backward compatible with data created by existing JDBCCacheLoaderOld implemetation. * All configuration elements described there {@link org.jboss.cache.loader.JDBCCacheLoaderOld} also apply for this * implementation. *

        *

        * Additional configuration info:
        * cache.jdbc.sql-concat : DBMS specific function for concat strings. Most likely this will be concat(1,2), but might * be different for proprietary systems. * * @author Mircea.Markus@iquestint.com * @author Galder Zamarreno * @version 1.0 */ @ThreadSafe public class JDBCCacheLoader extends AdjListJDBCCacheLoader { private JDBCCacheLoaderConfig config; /** * Builds a AdjListJDBCCacheLoaderConfig based on the supplied base config. */ @Override protected AdjListJDBCCacheLoaderConfig processConfig(CacheLoaderConfig.IndividualCacheLoaderConfig base) { if (base instanceof JDBCCacheLoaderConfig) { config = (JDBCCacheLoaderConfig) base; } else { config = new JDBCCacheLoaderConfig(base); } return config; } /** * As per interface's contract. * Performance Note: Optimised O(nodeDepth) db calls. */ public Object put(Fqn name, Object key, Object value) throws Exception { Map m = new HashMap(1); m.put(key, value); Map existing = _put(name, m); return existing == null ? null : existing.get(key); } /** * As per interface's contract. * Performance Note: Optimised O(nodeDepth) db calls. */ public void put(Fqn name, Map attributes) throws Exception { _put(name, attributes); } @Override protected void storeStateHelper(Fqn subtree, List nodeData, boolean moveToBuddy) throws Exception { for (Object aNodeData : nodeData) { NodeData nd = (NodeData) aNodeData; if (nd.isMarker()) break; Fqn fqn; if (moveToBuddy) { fqn = buddyFqnTransformer.getBackupFqn(subtree, nd.getFqn()); } else { fqn = nd.getFqn(); } if (nd.getAttributes() != null) { this.put(fqn, nd.getAttributes(), true);// creates a node with 0 or more attributes } else { this.put(fqn, null);// creates a node with null attributes } } } /** * As per interface's contrect. * Performance Note: O(1) db calls. */ public void remove(Fqn fqn) throws Exception { Connection conn = null; PreparedStatement ps = null; try { conn = cf.getConnection(); ps = conn.prepareStatement(config.getDeleteNodeSql()); //apend / at the end avoids this issue: 'a/b/cd' is not a child of 'a/b/c' ps.setString(1, fqn.isRoot() ? fqn.toString() : fqn + Fqn.SEPARATOR); lock.acquireLock(fqn, true); ps.executeUpdate(); if (log.isTraceEnabled()) { log.trace("Deleting all the children of " + fqn + ". Used sql is'" + config.getDeleteNodeSql() + '\''); } } catch (SQLException e) { log.error("Failed to remove the node : " + fqn, e); throw new IllegalStateException("Failure while removing sub-tree (" + fqn + ")" + e.getMessage()); } finally { safeClose(ps); cf.close(conn); lock.releaseLock(fqn); } } /** * Subscribes to contract. * Performance Note: O(2) db calls. */ @Override protected void getNodeDataList(Fqn fqn, List list) throws Exception { Map nodeAttributes = loadNode(fqn); if (nodeAttributes == null) { return; } Connection connection = null; PreparedStatement ps = null; ResultSet rs = null; try { connection = cf.getConnection(); ps = connection.prepareStatement(config.getRecursiveChildrenSql()); ps.setString(1, fqn.isRoot() ? fqn.toString() : fqn.toString() + Fqn.SEPARATOR); rs = ps.executeQuery(); while (rs.next()) { Map attributes = readAttributes(rs, 2); Fqn path = Fqn.fromString(rs.getString(1)); NodeData nodeData = (attributes == null || attributes.isEmpty()) ? new NodeData(path) : new NodeData(path, attributes); list.add(nodeData); } } catch (SQLException e) { log.error("Failed to load state for node(" + fqn + ") :" + e.getMessage(), e); throw new IllegalStateException("Failed to load state for node(" + fqn + ") :" + e.getMessage()); } finally { safeClose(rs); safeClose(ps); cf.close(connection); } } private Map readAttributes(ResultSet rs, int index) throws SQLException { Map result; InputStream is = rs.getBinaryStream(index); if (is != null && !rs.wasNull()) { try { Object marshalledNode = unmarshall(is); result = (Map) marshalledNode; } catch (Exception e) { log.error("Failure while reading attribute set from db", e); throw new SQLException("Failure while reading attribute set from db " + e); } } else { result = null; } return result; } private Map _put(Fqn name, Map attributes) throws Exception { lock.acquireLock(name, true); try { Map result = null; Map treeNode = loadNode(name); if (treeNode == null) { addNewSubtree(name, attributes); } else if (treeNode == NULL_NODE_IN_ROW) { updateNode(name, attributes); } else if (attributes != null && !attributes.isEmpty()) { //the node exists and the attribute map is NOT null Map newAttributes = new HashMap(treeNode); newAttributes.putAll(attributes);//creation sequnce important - we need to overwrite old values updateNode(name, newAttributes); result = treeNode; } return result; } finally { lock.releaseLock(name); } } private void addNewSubtree(Fqn name, Map attributes) throws Exception { Fqn currentNode = name; do { if (currentNode.equals(name)) { insertNode(currentNode, attributes, false); } else { insertNode(currentNode, null, true); } if (currentNode.isRoot()) break; currentNode = currentNode.getParent(); } while (!exists(currentNode)); } /** * Start is overwritten for the sake of backward compatibility only. * Here is the issue: old implementation does not create a Fqn.ROOT if not specifically told so. * As per put's contract, when calling put('/a/b/c', 'key', 'value') all parent nodes should be created up to root. * Root is not created, though. The compatibility problem comes in the case of loade ENTIRE state. * The implementation checks node's existence firstly, and based on that continues or not. As root is not * persisted nothing is loaded etc. */ @Override public void start() throws Exception { log = LogFactory.getLog(getClass()); super.start(); if (!exists(Fqn.ROOT) && getNodeCount() > 0) { put(Fqn.ROOT, null); } } /** * Returns a number representing the count of persisted children. */ public int getNodeCount() throws Exception { Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; try { if (log.isDebugEnabled()) { log.debug("executing sql: " + config.getNodeCountSql()); } conn = cf.getConnection(); ps = conn.prepareStatement(config.getNodeCountSql()); rs = ps.executeQuery(); rs.next();//count(*) will always return one row return rs.getInt(1); } catch (Exception e) { log.error("Failure while trying to get the count of persisted nodes: " + e.getMessage(), e); throw new IllegalStateException("Failure while trying to get the count of persisted nodes: " + e.getMessage()); } finally { safeClose(rs); safeClose(ps); cf.close(conn); } } @Override public void storeState(Fqn subtree, ObjectInputStream in) throws Exception { super.storeState(subtree, in); } public IndividualCacheLoaderConfig getConfig() { return config; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/loader/JDBCCacheLoaderOldConfig.java0000644000175000017500000000143011010550166031045 0ustar twernertwernerpackage org.jboss.cache.loader; import org.jboss.cache.config.CacheLoaderConfig; /** * Cache loader configuration for JDBCCacheLoaderOld implementation. * * @author Galder Zamarreno * @deprecated please use JDBCCacheLoaderConfig */ @Deprecated @SuppressWarnings("deprecation") public class JDBCCacheLoaderOldConfig extends AdjListJDBCCacheLoaderConfig { /** * The serialVersionUID */ private static final long serialVersionUID = -2911675876306188529L; public JDBCCacheLoaderOldConfig() { setClassName(JDBCCacheLoaderOld.class.getName()); } JDBCCacheLoaderOldConfig(CacheLoaderConfig.IndividualCacheLoaderConfig base) { super(base); setClassName(JDBCCacheLoaderOld.class.getName()); } }jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/loader/SingletonStoreDefaultConfig.java0000644000175000017500000001174610647237721032146 0ustar twernertwernerpackage org.jboss.cache.loader; import org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig.SingletonStoreConfig; import org.jboss.cache.config.Dynamic; import java.util.Properties; /** * Default singleton store cache loader configuration implementation, which is provided with the default singleton store * cache loader implementation. It provides with the capability of defining whether to push the in memory state to cache * loader when becoming the coordinator within a time constraint. * * @author Galder Zamarreno */ public class SingletonStoreDefaultConfig extends SingletonStoreConfig { private static final long serialVersionUID = -5828927920142613537L; /** * Boolean indicating whether push state when coordinator has been configured. */ @Dynamic private boolean pushStateWhenCoordinator; /** * Number of milliseconds configured defining the time constraint for the state push. */ @Dynamic private int pushStateWhenCoordinatorTimeout; /** * Default constructor that sets default values for singleton store cache loader configuration taking in account * that this configuration belongs to the default singleton store cache loader implementation. */ public SingletonStoreDefaultConfig() { /* pushStateWhenCoordinator enabled by default with 20 seconds as default timeout*/ pushStateWhenCoordinator = true; pushStateWhenCoordinatorTimeout = 20000; /* if we got to this point, we know that singleton store must have been enabled */ setSingletonStoreEnabled(true); /* and we also know that the configuration was created by SingletonStoreCacheLoader */ setSingletonStoreClass(SingletonStoreCacheLoader.class.getName()); } /** * Constructor that sets the assumed values for the default singleton store cache loader implementation and also * the properties, as per the properties section defined in the XML configuration. * * @param base contains properties set in XML configuration */ public SingletonStoreDefaultConfig(SingletonStoreConfig base) { this(); setSingletonStoreproperties(base.getSingletonStoreproperties()); } @Override public boolean isSingletonStoreEnabled() { return true; } @Override public void setSingletonStoreEnabled(boolean singletonStoreEnabled) { /* ignore it */ } @Override public String getSingletonStoreClass() { return SingletonStoreCacheLoader.class.getName(); } @Override public void setSingletonStoreClass(String singletonStoreClass) { /* ignore it */ } /** * Takes the properties defined and populates the individual instance fields of the default singleton store cache * loader configuration. * * @param props is an instance of Properties containing these values. */ @Override public void setSingletonStoreproperties(Properties props) { super.setSingletonStoreproperties(props); String pushStateWhenCoordinatorStr = props.getProperty("pushStateWhenCoordinator"); if (pushStateWhenCoordinatorStr != null) { /* if not null, we use the defined value, otherwise we leave it to the default value, true */ /* note: default value for a null property is false, hence the check */ setPushStateWhenCoordinator(Boolean.valueOf(pushStateWhenCoordinatorStr)); } String pushStateWhenCoordinatorTimeoutStr = props.getProperty("pushStateWhenCoordinatorTimeout"); if (pushStateWhenCoordinatorTimeoutStr != null) { setPushStateWhenCoordinatorTimeout(Integer.parseInt(pushStateWhenCoordinatorTimeoutStr)); } } public boolean isPushStateWhenCoordinator() { return pushStateWhenCoordinator; } public void setPushStateWhenCoordinator(boolean pushStateWhenCoordinator) { testImmutability("pushStateWhenCoordinator"); this.pushStateWhenCoordinator = pushStateWhenCoordinator; } public int getPushStateWhenCoordinatorTimeout() { return pushStateWhenCoordinatorTimeout; } public void setPushStateWhenCoordinatorTimeout(int pushStateWhenCoordinatorTimeout) { testImmutability("pushStateWhenCoordinatorTimeout"); this.pushStateWhenCoordinatorTimeout = pushStateWhenCoordinatorTimeout; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj instanceof SingletonStoreDefaultConfig) { SingletonStoreDefaultConfig other = (SingletonStoreDefaultConfig) obj; return (other.pushStateWhenCoordinator == this.pushStateWhenCoordinator) && (other.pushStateWhenCoordinatorTimeout == this.pushStateWhenCoordinatorTimeout); } return false; } @Override public int hashCode() { int result = 13; result = 23 * result + (pushStateWhenCoordinator ? 0 : 1); result = 23 * result + pushStateWhenCoordinatorTimeout; return result; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/loader/bdbje/0000755000175000017500000000000011376174032024721 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/loader/bdbje/BdbjeCacheLoader.java0000644000175000017500000007706711010550166030655 0ustar twernertwernerpackage org.jboss.cache.loader.bdbje; import com.sleepycat.bind.serial.SerialBinding; import com.sleepycat.bind.serial.StoredClassCatalog; import com.sleepycat.bind.tuple.TupleBinding; import com.sleepycat.bind.tuple.TupleInput; import com.sleepycat.bind.tuple.TupleOutput; import com.sleepycat.je.*; import net.jcip.annotations.ThreadSafe; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.Modification; import org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig; import org.jboss.cache.loader.AbstractCacheLoader; import org.jboss.cache.util.reflect.ReflectionUtil; import java.io.File; import java.io.IOException; import java.io.Serializable; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; /** * A persistent CacheLoader based on Berkeley DB Java Edition. *

        *

        The configuration string format is:

        *
        environmentDirectoryName[#databaseName]
        *

        where databaseName, if omitted, defaults to the ClusterName property * of the CacheImpl.

        *

        *

        A je.properties file may optionally be placed in the JE environment * directory and used to customize the default JE configuration.

        * * @author Mark Hayes May 16, 2004 * @author Bela Ban * @version $Id: BdbjeCacheLoader.java 5808 2008-05-08 10:05:42Z manik.surtani@jboss.com $ */ @ThreadSafe public class BdbjeCacheLoader extends AbstractCacheLoader { private static final int MAX_TXN_RETRIES = 10; private static final char LOWEST_UTF_CHAR = '\u0001'; private static final Log log = LogFactory.getLog(BdbjeCacheLoader.class); private BdbjeCacheLoaderConfig config; private Environment env; private String cacheDbName; private String catalogDbName; private Database cacheDb; private Database catalogDb; private StoredClassCatalog catalog; private SerialBinding serialBinding; private Map txnMap; private boolean transactional; /* * Service implementation -- lifecycle methods. * Note that setConfig() and setCache() are called before create(). */ /** * Does nothing since start() does all the work. */ @Override public void create() throws Exception { String license = "\n*************************************************************************************\n" + "Berkeley DB Java Edition version: " + JEVersion.CURRENT_VERSION.toString() + "\n" + "JBoss Cache can use Berkeley DB Java Edition from Oracle \n" + "(http://www.oracle.com/database/berkeley-db/je/index.html)\n" + "for persistent, reliable and transaction-protected data storage.\n" + "If you choose to use Berkeley DB Java Edition with JBoss Cache, you must comply with the terms\n" + "of Oracle's public license, included in the file LICENSE.txt.\n" + "If you prefer not to release the source code for your own application in order to comply\n" + "with the Oracle public license, you may purchase a different license for use of\n" + "Berkeley DB Java Edition with JBoss Cache.\n" + "See http://www.oracle.com/database/berkeley-db/je/index.html for pricing and license terms\n" + "*************************************************************************************"; System.out.println(license); log.trace("Creating BdbjeCacheLoader instance."); checkNotOpen(); } /** * Opens the JE environment and the database specified by the configuration * string. The environment and databases are created if necessary. */ @Override public void start() throws Exception { log.trace("Starting BdbjeCacheLoader instance."); checkNotOpen(); if (cache == null) { throw new IllegalStateException( "A non-null Cache property (CacheSPI object) is required"); } String configStr = config.getLocation(); if (config.getLocation() == null) { configStr = System.getProperty("java.io.tmpdir"); ReflectionUtil.setValue(config, "accessible", true); config.setLocation(configStr); } // test location File location = new File(configStr); if (!location.exists()) { boolean created = location.mkdirs(); if (!created) throw new IOException("Unable to create cache loader location " + location); } if (!location.isDirectory()) { throw new IOException("Cache loader location [" + location + "] is not a directory!"); } /* Parse config string. */ File homeDir; int offset = configStr.indexOf('#'); if (offset >= 0 && offset < configStr.length() - 1) { homeDir = new File(configStr.substring(0, offset)); cacheDbName = configStr.substring(offset + 1); } else { homeDir = new File(configStr); cacheDbName = cache.getClusterName(); } catalogDbName = cacheDbName + "_class_catalog"; /* * If the CacheImpl is transactional, we will create transactional * databases. However, we always create a transactional environment * since it may be shared by transactional and non-transactional caches. */ transactional = cache.getTransactionManager() != null; try { /* Open the environment, creating it if it doesn't exist. */ EnvironmentConfig envConfig = new EnvironmentConfig(); envConfig.setAllowCreate(true); envConfig.setTransactional(true); envConfig.setLockTimeout(1000 * cache.getConfiguration().getLockAcquisitionTimeout()); // these are in nanos if (log.isTraceEnabled()) log.trace("Creating JE environment with home dir " + homeDir); env = new Environment(homeDir, envConfig); if (log.isDebugEnabled()) log.debug("Created JE environment " + env + " for cache loader " + this); /* Open cache and catalog databases. */ openDatabases(); } catch (Exception e) { destroy(); throw e; } } /** * Opens all databases and initializes database related information. */ private void openDatabases() throws Exception { /* Use a generic database config, with no duplicates allowed. */ DatabaseConfig dbConfig = new DatabaseConfig(); dbConfig.setAllowCreate(true); dbConfig.setTransactional(transactional); /* Create/open the cache database and associated catalog database. */ cacheDb = env.openDatabase(null, cacheDbName, dbConfig); catalogDb = env.openDatabase(null, catalogDbName, dbConfig); /* Use the catalog for the serial binding. */ catalog = new StoredClassCatalog(catalogDb); serialBinding = new SerialBinding(catalog, null); /* Start with a fresh transaction map. */ txnMap = new ConcurrentHashMap(); } /** * Closes all databases, ignoring exceptions, and nulls references to all * database related information. */ private void closeDatabases() { if (cacheDb != null) { try { cacheDb.close(); } catch (Exception shouldNotOccur) { log.warn("Caught unexpected exception", shouldNotOccur); } } if (catalogDb != null) { try { catalogDb.close(); } catch (Exception shouldNotOccur) { log.warn("Caught unexpected exception", shouldNotOccur); } } cacheDb = null; catalogDb = null; catalog = null; serialBinding = null; txnMap = null; } /** * Closes the JE databases and environment, and nulls references to them. * The environment and databases are not removed from the file system. * Exceptions during close are ignored. */ @Override public void stop() { closeDatabases(); if (env != null) { try { env.close(); } catch (Exception shouldNotOccur) { log.warn("Unexpected exception", shouldNotOccur); } } env = null; } /* * CacheLoader implementation. */ /** * Sets the configuration string for this cache loader. */ public void setConfig(IndividualCacheLoaderConfig base) { checkNotOpen(); if (base instanceof BdbjeCacheLoaderConfig) { this.config = (BdbjeCacheLoaderConfig) base; } else { config = new BdbjeCacheLoaderConfig(base); } if (log.isTraceEnabled()) log.trace("Configuring cache loader with location = " + config.getLocation()); } public IndividualCacheLoaderConfig getConfig() { return config; } /** * Sets the CacheImpl owner of this cache loader. */ @Override public void setCache(CacheSPI c) { super.setCache(c); checkNotOpen(); } /** * Returns an unmodifiable set of relative children names (strings), or * returns null if the parent node is not found or if no children are found. * This is a fairly expensive operation, and is assumed to be performed by * browser applications. Calling this method as part of a run-time * transaction is not recommended. */ public Set getChildrenNames(Fqn name) throws Exception { checkOpen(); checkNonNull(name, "name"); DatabaseEntry prefixEntry = makeKeyEntry(name); DatabaseEntry dataEntry = new DatabaseEntry(); dataEntry.setPartial(0, 0, true); String namePart = ""; int namePartIndex = name.size(); Set set = null; Cursor cursor = cacheDb.openCursor(null, null); try { while (true) { DatabaseEntry keyEntry = makeKeyEntry(prefixEntry, namePart); OperationStatus status = cursor.getSearchKeyRange(keyEntry, dataEntry, null); if (status != OperationStatus.SUCCESS || !startsWith(keyEntry, prefixEntry)) { break; } if (set == null) { set = new HashSet(); } Fqn childName = makeKeyObject(keyEntry); namePart = childName.get(namePartIndex).toString(); set.add(namePart); namePart += LOWEST_UTF_CHAR; } } finally { cursor.close(); } if (set != null) { return Collections.unmodifiableSet(set); } else { return null; } } /** * Returns a map containing all key-value pairs for the given FQN, or null * if the node is not present. * This operation is always non-transactional, even in a transactional * environment. */ public Map get(Fqn name) throws Exception { checkOpen(); checkNonNull(name, "name"); DatabaseEntry keyEntry = makeKeyEntry(name); DatabaseEntry foundData = new DatabaseEntry(); OperationStatus status = cacheDb.get(null, keyEntry, foundData, null); if (status == OperationStatus.SUCCESS) { // changed createIfNull param to true // See http://jira.jboss.com/jira/browse/JBCACHE-118 return makeDataObject(foundData); } else { return null; } } // See http://jira.jboss.com/jira/browse/JBCACHE-118 for why this is commented out. /** * Returns the data object stored under the given FQN and key, or null if * the FQN and key are not present. * This operation is always non-transactional, even in a transactional * environment. */ // public Object get(Fqn name, Object key) // throws Exception { // // Map map = get(name); // if (map != null) { // return map.get(key); // } else { // return null; // } // } /** * Returns whether the given node exists. */ public boolean exists(Fqn name) throws Exception { checkOpen(); checkNonNull(name, "name"); DatabaseEntry keyEntry = makeKeyEntry(name); DatabaseEntry foundData = new DatabaseEntry(); foundData.setPartial(0, 0, true); OperationStatus status = cacheDb.get(null, keyEntry, foundData, null); return (status == OperationStatus.SUCCESS); } /** * Stores a single FQN-key-value record. * Intended to be used in a non-transactional environment, but will use * auto-commit in a transactional environment. */ public Object put(Fqn name, Object key, Object value) throws Exception { checkOpen(); checkNonNull(name, "name"); Object oldVal; if (transactional) { Modification mod = new Modification(Modification.ModificationType.PUT_KEY_VALUE, name, key, value); commitModification(mod); oldVal = mod.getOldValue(); } else { oldVal = doPut(null, name, key, value); } return oldVal; } /** * Internal version of store(String,Object,Object) that allows passing a * transaction. */ private Object doPut(Transaction txn, Fqn name, Object key, Object value) throws Exception { Object oldVal = null; /* To update-or-insert, try putNoOverwrite first, then a RMW cycle. */ Map map = new HashMap(); map.put(key, value); DatabaseEntry dataEntry = makeDataEntry(map); DatabaseEntry keyEntry = makeKeyEntry(name); Cursor cursor = cacheDb.openCursor(txn, null); try { OperationStatus status = cursor.putNoOverwrite(keyEntry, dataEntry); if (status == OperationStatus.SUCCESS) { createParentNodes(cursor, name); } else { DatabaseEntry foundData = new DatabaseEntry(); status = cursor.getSearchKey(keyEntry, foundData, LockMode.RMW); if (status == OperationStatus.SUCCESS) { map = makeDataObject(foundData); oldVal = map.put(key, value); cursor.putCurrent(makeDataEntry(map)); } } } finally { cursor.close(); } return oldVal; } /** * Stores a map of key-values for a given FQN, but does not delete existing * key-value pairs (that is, it does not erase). * Intended to be used in a non-transactional environment, but will use * auto-commit in a transactional environment. */ public void put(Fqn name, Map values) throws Exception { checkOpen(); checkNonNull(name, "name"); if (transactional) { commitModification( new Modification(Modification.ModificationType.PUT_DATA, name, values)); } else { doPut(null, name, values); } } /** * Internal version of put(Fqn,Map) that allows passing a * transaction. */ private void doPut(Transaction txn, Fqn name, Map values) throws Exception { // JBCACHE-769 -- make a defensive copy values = (values == null ? null : new HashMap(values)); /* To update-or-insert, try putNoOverwrite first, then a RMW cycle. */ DatabaseEntry dataEntry = makeDataEntry(values); DatabaseEntry keyEntry = makeKeyEntry(name); Cursor cursor = cacheDb.openCursor(txn, null); try { OperationStatus status = cursor.putNoOverwrite(keyEntry, dataEntry); if (status == OperationStatus.SUCCESS) { createParentNodes(cursor, name); } else { DatabaseEntry foundData = new DatabaseEntry(); status = cursor.getSearchKey(keyEntry, foundData, LockMode.RMW); if (status == OperationStatus.SUCCESS) { Map map = makeDataObject(foundData); if (values != null) { map.putAll(values); } cursor.putCurrent(makeDataEntry(map)); } } } finally { cursor.close(); } } /** * Internal version of put(Fqn,Map) that allows passing a * transaction and erases existing data. */ private void doPutErase(Transaction txn, Fqn name, Map values) throws Exception { // JBCACHE-769 -- make a defensive copy values = (values == null ? null : new HashMap(values)); DatabaseEntry dataEntry = makeDataEntry(values); DatabaseEntry keyEntry = makeKeyEntry(name); Cursor cursor = cacheDb.openCursor(txn, null); try { cursor.put(keyEntry, dataEntry); createParentNodes(cursor, name); } finally { cursor.close(); } } /** * Applies the given modifications. * Intended to be used in a non-transactional environment, but will use * auto-commit in a transactional environment. */ @Override public void put(List modifications) throws Exception { checkOpen(); checkNonNull(modifications, "modifications"); if (transactional) { commitModifications(modifications); } else { doPut(null, modifications); } } /** * Internal version of put(List) that allows passing a transaction. */ private void doPut(Transaction txn, List modifications) throws Exception { /* This could be optimized by grouping modifications by Fqn, and * performing a single database operation for each Fqn (record). */ for (Modification mod : modifications) { Fqn name = mod.getFqn(); Object oldVal; switch (mod.getType()) { case PUT_KEY_VALUE: oldVal = doPut(txn, name, mod.getKey(), mod.getValue()); mod.setOldValue(oldVal); break; case PUT_DATA: doPut(txn, name, mod.getData()); break; case PUT_DATA_ERASE: doPutErase(txn, name, mod.getData()); break; case REMOVE_KEY_VALUE: oldVal = doRemove(txn, name, mod.getKey()); mod.setOldValue(oldVal); break; case REMOVE_NODE: doRemove(txn, name); break; case REMOVE_DATA: doRemoveData(txn, name); break; default: throw new IllegalArgumentException( "Unknown Modification type: " + mod.getType()); } } } /** * Creates parent nodes of the given Fqn, moving upward until an existing * node is found. */ private void createParentNodes(Cursor cursor, Fqn name) throws Exception { DatabaseEntry dataEntry = makeDataEntry(null); for (int nParts = name.size() - 1; nParts >= 1; nParts -= 1) { DatabaseEntry keyEntry = makeKeyEntry(name, nParts); OperationStatus status = cursor.putNoOverwrite(keyEntry, dataEntry); if (status != OperationStatus.SUCCESS) { break; } } } /** * Deletes the node for a given FQN and all its descendent nodes. * Intended to be used in a non-transactional environment, but will use * auto-commit in a transactional environment. */ public void remove(Fqn name) throws Exception { checkOpen(); checkNonNull(name, "name"); if (transactional) { commitModification( new Modification(Modification.ModificationType.REMOVE_NODE, name)); } else { doRemove(null, name); } } /** * Internal version of remove(Fqn) that allows passing a transaction. */ private void doRemove(Transaction txn, Fqn name) throws Exception { // if the Fqn is Fqn.ROOT, make sure we get all the children and delete them instead. if (name.isRoot()) { Set children = getChildrenNames(name); if (children != null) { for (String child : children) { doRemove(txn, Fqn.fromElements(child)); } } } else { DatabaseEntry keyEntry = makeKeyEntry(name); DatabaseEntry foundKey = new DatabaseEntry(); DatabaseEntry foundData = new DatabaseEntry(); foundData.setPartial(0, 0, true); Cursor cursor = cacheDb.openCursor(txn, null); try { OperationStatus status = cursor.getSearchKey(keyEntry, foundData, LockMode.RMW); while (status == OperationStatus.SUCCESS) { cursor.delete(); status = cursor.getNext(foundKey, foundData, LockMode.RMW); if (status == OperationStatus.SUCCESS && !startsWith(foundKey, keyEntry)) { status = OperationStatus.NOTFOUND; } } } finally { cursor.close(); } } } /** * Deletes a single FQN-key-value record. * Intended to be used in a non-transactional environment, but will use * auto-commit in a transactional environment. */ public Object remove(Fqn name, Object key) throws Exception { checkOpen(); checkNonNull(name, "name"); Object oldVal; if (transactional) { Modification mod = new Modification(Modification.ModificationType.REMOVE_KEY_VALUE, name, key); commitModification(mod); oldVal = mod.getOldValue(); } else { oldVal = doRemove(null, name, key); } return oldVal; } /** * Internal version of remove(String,Object) that allows passing a * transaction. */ private Object doRemove(Transaction txn, Fqn name, Object key) throws Exception { Object oldVal = null; DatabaseEntry keyEntry = makeKeyEntry(name); DatabaseEntry foundData = new DatabaseEntry(); Cursor cursor = cacheDb.openCursor(txn, null); try { OperationStatus status = cursor.getSearchKey(keyEntry, foundData, LockMode.RMW); if (status == OperationStatus.SUCCESS) { Map map = makeDataObject(foundData); oldVal = map.remove(key); cursor.putCurrent(makeDataEntry(map)); } } finally { cursor.close(); } return oldVal; } /** * Clears the map for the given node, but does not remove the node. */ public void removeData(Fqn name) throws Exception { checkOpen(); checkNonNull(name, "name"); if (transactional) { commitModification( new Modification(Modification.ModificationType.REMOVE_DATA, name)); } else { doRemoveData(null, name); } } /** * Internal version of removeData(Fqn) that allows passing a transaction. */ private void doRemoveData(Transaction txn, Fqn name) throws Exception { DatabaseEntry dataEntry = new DatabaseEntry(); dataEntry.setPartial(0, 0, true); DatabaseEntry keyEntry = makeKeyEntry(name); Cursor cursor = cacheDb.openCursor(txn, null); try { OperationStatus status = cursor.getSearchKey(keyEntry, dataEntry, LockMode.RMW); if (status == OperationStatus.SUCCESS) { cursor.putCurrent(makeDataEntry(null)); } } finally { cursor.close(); } } /** * Begins a transaction and applies the given modifications. *

        *

        If onePhase is true, commits the transaction; otherwise, associates * the txn value with the transaction and expects commit() or rollback() to * be called later with the same tx value. Performs retries if necessary to * resolve deadlocks.

        */ @Override public void prepare(Object tx, List modifications, boolean onePhase) throws Exception { checkOpen(); checkNonNull(modifications, "modifications"); if (!onePhase) { checkNonNull(tx, "tx"); } if (!transactional) { throw new UnsupportedOperationException( "prepare() not allowed with a non-transactional cache loader"); } Transaction txn = performTransaction(modifications); if (onePhase) { txn.commit(); } else { txnMap.put(tx, txn); } } /** * Performs and commits a single modification. The loader must be * transactional. Commits the transaction if successful, or aborts the * transaction and throws an exception if not successful. */ private void commitModification(Modification mod) throws Exception { commitModifications(Collections.singletonList(mod)); } /** * Performs and commits a list of modifications. The loader must be * transactional. Commits the transaction if successful, or aborts the * transaction and throws an exception if not successful. */ private void commitModifications(List mods) throws Exception { if (!transactional) throw new IllegalStateException(); Transaction txn = performTransaction(mods); txn.commit(); } /** * Performs the given operation, starting a transaction and performing * retries. Returns the transaction if successful; aborts the transaction * and throws an exception if not successful. */ private Transaction performTransaction(List modifications) throws Exception { /* * Note that we can't use TransactionRunner here since if onePhase=false * in the call to prepare(), we do not want to commit. TransactionRunner * always commits or aborts. */ int retries = MAX_TXN_RETRIES; while (true) { Transaction txn = env.beginTransaction(null, null); try { doPut(txn, modifications); return txn; } catch (Exception e) { txn.abort(); if (e instanceof DeadlockException && retries > 0) { retries -= 1; } else { throw e; } } } } /** * Commits the given transaction, or throws IllegalArgumentException if the * given key is not associated with an uncommited transaction. */ @Override public void commit(Object tx) throws Exception { checkOpen(); checkNonNull(tx, "tx"); Transaction txn = txnMap.remove(tx); if (txn != null) { txn.commit(); } else if (transactional) { throw new IllegalArgumentException("Unknown txn key: " + tx); } } /** * Commits the given transaction, or throws IllegalArgumentException if the * given key is not associated with an uncommited transaction. */ @Override public void rollback(Object tx) { checkOpen(); checkNonNull(tx, "tx"); Transaction txn = txnMap.remove(tx); if (txn != null) { try { txn.abort(); } catch (Exception ignored) { } } else if (transactional) { throw new IllegalArgumentException("Unknown txn key: " + tx); } } /** * Returns whether the given entry starts with the given prefix bytes. * Used to determine whether a database key starts with a given FQN. */ private boolean startsWith(DatabaseEntry entry, DatabaseEntry prefix) { int size = prefix.getSize(); if (size > entry.getSize()) { return false; } byte[] d1 = entry.getData(); byte[] d2 = prefix.getData(); int o1 = entry.getOffset(); int o2 = prefix.getOffset(); for (int i = 0; i < size; i += 1) { if (d1[o1 + i] != d2[o2 + i]) { return false; } } return true; } /** * Converts a database entry to an Fqn. */ private Fqn makeKeyObject(DatabaseEntry entry) { Fqn name = Fqn.ROOT; TupleInput tupleInput = TupleBinding.entryToInput(entry); while (tupleInput.available() > 0) { String part = tupleInput.readString(); name = Fqn.fromRelativeElements(name, part); } return name; } /** * Converts an Fqn to a database entry. */ private DatabaseEntry makeKeyEntry(Fqn name) { return makeKeyEntry(name, name.size()); } /** * Converts an Fqn to a database entry, outputing the given number of name * parts. */ private DatabaseEntry makeKeyEntry(Fqn name, int nParts) { /* Write the sequence of name parts. */ TupleOutput tupleOutput = new TupleOutput(); for (int i = 0; i < nParts; i += 1) { tupleOutput.writeString(name.get(i).toString()); } /* Return the tuple as an entry. */ DatabaseEntry entry = new DatabaseEntry(); TupleBinding.outputToEntry(tupleOutput, entry); return entry; } /** * Creates a key database entry from a parent database entry (prefix) and * a child name part. */ private DatabaseEntry makeKeyEntry(DatabaseEntry prefix, String namePart) { /* Write the bytes of the prefix followed by the child name. */ TupleOutput tupleOutput = new TupleOutput(); tupleOutput.writeFast(prefix.getData(), prefix.getOffset(), prefix.getSize()); tupleOutput.writeString(namePart); /* Return the tuple as an entry. */ DatabaseEntry entry = new DatabaseEntry(); TupleBinding.outputToEntry(tupleOutput, entry); return entry; } /** * Converts a database entry to a Map. */ @SuppressWarnings("unchecked") private Map makeDataObject(DatabaseEntry entry) { Map map = (Map) serialBinding.entryToObject(entry); if (map == null) { map = new HashMap(); } return map; } /** * Converts a Map to a database entry. */ private DatabaseEntry makeDataEntry(Map map) { if (map != null) { if (map.size() == 0) { map = null; } else if (!(map instanceof Serializable)) { map = new HashMap(map); } } DatabaseEntry entry = new DatabaseEntry(); serialBinding.objectToEntry(map, entry); return entry; } /** * Throws an exception if the environment is not open. */ private void checkOpen() { if (env == null) { throw new IllegalStateException( "Operation not allowed before calling create()"); } } /** * Throws an exception if the environment is not open. */ private void checkNotOpen() { if (env != null) { throw new IllegalStateException( "Operation not allowed after calling create()"); } } /** * Throws an exception if the parameter is null. */ private void checkNonNull(Object param, String paramName) { if (param == null) { throw new NullPointerException( "Parameter must not be null: " + paramName); } } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/loader/bdbje/BdbjeCacheLoaderConfig.java0000644000175000017500000000321011006024704031755 0ustar twernertwernerpackage org.jboss.cache.loader.bdbje; import org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig; import org.jboss.cache.util.Util; import java.util.Properties; public class BdbjeCacheLoaderConfig extends IndividualCacheLoaderConfig { private static final long serialVersionUID = 4626734068542420865L; private String location; public BdbjeCacheLoaderConfig() { setClassName(BdbjeCacheLoader.class.getName()); } /** * For use by {@link BdbjeCacheLoader}. * * @param base generic config object created by XML parsing. */ BdbjeCacheLoaderConfig(IndividualCacheLoaderConfig base) { setClassName(BdbjeCacheLoader.class.getName()); populateFromBaseConfig(base); } public String getLocation() { return location; } public void setLocation(String location) { testImmutability("location"); this.location = location; } @Override public void setProperties(Properties props) { super.setProperties(props); setLocation(props != null ? props.getProperty("location") : null); } @Override public boolean equals(Object obj) { if (obj instanceof BdbjeCacheLoaderConfig && equalsExcludingProperties(obj)) { return Util.safeEquals(location, ((BdbjeCacheLoaderConfig) obj).location); } return false; } @Override public int hashCode() { return 31 * hashCodeExcludingProperties() + (location == null ? 0 : location.hashCode()); } @Override public BdbjeCacheLoaderConfig clone() throws CloneNotSupportedException { return (BdbjeCacheLoaderConfig) super.clone(); } }jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/loader/FileCacheLoaderConfig.java0000644000175000017500000000430411006024704030565 0ustar twernertwernerpackage org.jboss.cache.loader; import org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig; import org.jboss.cache.config.Dynamic; import org.jboss.cache.util.Util; import java.util.Properties; public class FileCacheLoaderConfig extends IndividualCacheLoaderConfig { private static final long serialVersionUID = 4626734068542420865L; private String location; @Dynamic private boolean checkCharacterPortability = true; public FileCacheLoaderConfig() { setClassName(FileCacheLoader.class.getName()); } /** * For use by {@link FileCacheLoader}. * * @param base generic config object created by XML parsing. */ FileCacheLoaderConfig(IndividualCacheLoaderConfig base) { setClassName(FileCacheLoader.class.getName()); populateFromBaseConfig(base); } public String getLocation() { return location; } public void setLocation(String location) { testImmutability("location"); this.location = location; } public boolean isCheckCharacterPortability() { return checkCharacterPortability; } public void setCheckCharacterPortability(boolean checkCharacterPortability) { testImmutability("check.character.portability"); this.checkCharacterPortability = checkCharacterPortability; } @Override public void setProperties(Properties props) { super.setProperties(props); if (props != null) { setLocation(props.getProperty("location")); String prop = props.getProperty("check.character.portability"); setCheckCharacterPortability((prop == null || Boolean.valueOf(prop))); } } @Override public boolean equals(Object obj) { if (obj instanceof FileCacheLoaderConfig && equalsExcludingProperties(obj)) { return Util.safeEquals(location, ((FileCacheLoaderConfig) obj).location); } return false; } @Override public int hashCode() { return 31 * hashCodeExcludingProperties() + (location == null ? 0 : location.hashCode()); } @Override public FileCacheLoaderConfig clone() throws CloneNotSupportedException { return (FileCacheLoaderConfig) super.clone(); } }jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/loader/ChainingCacheLoader.java0000644000175000017500000003350211031017265030304 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.loader; import org.jboss.cache.Fqn; import org.jboss.cache.Modification; import org.jboss.cache.RegionManager; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig; import org.jboss.cache.factories.ComponentRegistry; import org.jboss.cache.factories.annotations.Inject; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; /** * This decorator is used whenever more than one cache loader is configured. READ operations are directed to * each of the cache loaders (in the order which they were configured) until a non-null (or non-empty in the case * of retrieving collection objects) result is achieved. *

        * WRITE operations are propagated to ALL registered cacheloaders that specified set ignoreModifications to false. * * @author Manik Surtani (manik@jboss.org) */ public class ChainingCacheLoader extends AbstractCacheLoader { private final List cacheLoaders = new ArrayList(2); private final List writeCacheLoaders = new ArrayList(2); private final List cacheLoaderConfigs = new ArrayList(2); private ComponentRegistry registry; /** * Sets the configuration. Will be called before {@link #create()} and {@link #start()} * * @param config ignored */ public void setConfig(IndividualCacheLoaderConfig config) { // don't do much here? } public IndividualCacheLoaderConfig getConfig() { return null; } @Inject public void injectDependencies(ComponentRegistry registry) { this.registry = registry; } /** * Returns a list of children names, all names are relative. Returns null if the parent node is not found. * The returned set must not be modified, e.g. use Collections.unmodifiableSet(s) to return the result * * @param fqn The FQN of the parent * @return Set. A list of children. Returns null if no children nodes are present, or the parent is * not present */ public Set getChildrenNames(Fqn fqn) throws Exception { Set answer = null; for (CacheLoader l : cacheLoaders) { answer = l.getChildrenNames(fqn); if (answer != null && answer.size() > 0) break; } return answer; } /** * Returns all keys and values from the persistent store, given a fully qualified name * * @param name * @return Map of keys and values for the given node. Returns null if the node was not found, or * if the node has no attributes * @throws Exception */ public Map get(Fqn name) throws Exception { Map answer = null; for (CacheLoader l : cacheLoaders) { answer = l.get(name); if (answer != null) break; } return answer; } /** * Checks whether the CacheLoader has a node with Fqn * * @param name * @return True if node exists, false otherwise */ public boolean exists(Fqn name) throws Exception { boolean answer = false; for (CacheLoader l : cacheLoaders) { answer = l.exists(name); if (answer) break; } return answer; } /** * Inserts key and value into the attributes hashmap of the given node. If the node does not exist, all * parent nodes from the root down are created automatically. Returns the old value */ public Object put(Fqn name, Object key, Object value) throws Exception { Object answer = null; Iterator i = writeCacheLoaders.iterator(); boolean isFirst = true; while (i.hasNext()) { CacheLoader l = i.next(); Object tAnswer = l.put(name, key, value); if (isFirst) { answer = tAnswer; isFirst = false; } } return answer; } /** * Inserts all elements of attributes into the attributes hashmap of the given node, overwriting existing * attributes, but not clearing the existing hashmap before insertion (making it a union of existing and * new attributes) * If the node does not exist, all parent nodes from the root down are created automatically * * @param name The fully qualified name of the node * @param attributes A Map of attributes. Can be null */ public void put(Fqn name, Map attributes) throws Exception { for (CacheLoader l : writeCacheLoaders) { l.put(name, attributes); } } /** * Inserts all modifications to the backend store. Overwrite whatever is already in * the datastore. * * @param modifications A List of modifications * @throws Exception */ @Override public void put(List modifications) throws Exception { for (CacheLoader l : writeCacheLoaders) { l.put(modifications); } } /** * Removes the given key and value from the attributes of the given node. No-op if node doesn't exist. * Returns the first response from the loader chain. */ public Object remove(Fqn name, Object key) throws Exception { Object answer = null; Iterator i = writeCacheLoaders.iterator(); boolean isFirst = true; while (i.hasNext()) { CacheLoader l = i.next(); Object tAnswer = l.remove(name, key); if (isFirst) { answer = tAnswer; isFirst = false; } } return answer; } /** * Removes the given node. If the node is the root of a subtree, this will recursively remove all subnodes, * depth-first */ public void remove(Fqn name) throws Exception { for (CacheLoader l : writeCacheLoaders) l.remove(name); } /** * Removes all attributes from a given node, but doesn't delete the node itself * * @param name * @throws Exception */ public void removeData(Fqn name) throws Exception { for (CacheLoader l : writeCacheLoaders) { l.removeData(name); } } /** * Prepare the modifications. For example, for a DB-based CacheLoader: *

          *
        1. Create a local (JDBC) transaction *
        2. Associate the local transaction with tx (tx is the key) *
        3. Execute the coresponding SQL statements against the DB (statements derived from modifications) *
        * For non-transactional CacheLoader (e.g. file-based), this could be a null operation * * @param tx The transaction, just used as a hashmap key * @param modifications List, a list of all modifications within the given transaction * @param one_phase Persist immediately and (for example) commit the local JDBC transaction as well. When true, * we won't get a {@link #commit(Object)} or {@link #rollback(Object)} method call later * @throws Exception */ @Override public void prepare(Object tx, List modifications, boolean one_phase) throws Exception { for (CacheLoader l : writeCacheLoaders) { l.prepare(tx, modifications, one_phase); } } /** * Commit the transaction. A DB-based CacheLoader would look up the local JDBC transaction asociated * with tx and commit that transaction
        * Non-transactional CacheLoaders could simply write the data that was previously saved transiently under the * given tx key, to (for example) a file system (note this only holds if the previous prepare() did * not define one_phase=true * * @param tx */ @Override public void commit(Object tx) throws Exception { for (CacheLoader l : writeCacheLoaders) { l.commit(tx); } } /** * Roll the transaction back. A DB-based CacheLoader would look up the local JDBC transaction asociated * with tx and roll back that transaction * * @param tx */ @Override public void rollback(Object tx) { for (CacheLoader l : writeCacheLoaders) { l.rollback(tx); } } /** * Creates individual cache loaders. * * @throws Exception */ @Override public void create() throws Exception { Iterator it = cacheLoaders.iterator(); Iterator cfgIt = cacheLoaderConfigs.iterator(); while (it.hasNext() && cfgIt.hasNext()) { CacheLoader cl = it.next(); CacheLoaderConfig.IndividualCacheLoaderConfig cfg = cfgIt.next(); cl.setConfig(cfg); registry.wireDependencies(cl); cl.create(); } } @Override public void start() throws Exception { for (CacheLoader cacheLoader : cacheLoaders) { cacheLoader.start(); } } @Override public void stop() { for (CacheLoader cacheLoader : cacheLoaders) { cacheLoader.stop(); } } @Override public void destroy() { for (CacheLoader cacheLoader : cacheLoaders) { cacheLoader.destroy(); } } /** * No-op, as this class doesn't directly use the ERegionManager. */ @Override public void setRegionManager(RegionManager manager) { // no-op -- we don't do anything with the region manager } @Override public void loadEntireState(ObjectOutputStream os) throws Exception { Iterator i = cacheLoaders.iterator(); Iterator cfgs = cacheLoaderConfigs.iterator(); while (i.hasNext() && cfgs.hasNext()) { CacheLoader l = i.next(); CacheLoaderConfig.IndividualCacheLoaderConfig cfg = cfgs.next(); if (cfg.isFetchPersistentState()) { l.loadEntireState(os); break; } } } @Override public void loadState(Fqn subtree, ObjectOutputStream os) throws Exception { Iterator i = cacheLoaders.iterator(); Iterator cfgs = cacheLoaderConfigs.iterator(); while (i.hasNext() && cfgs.hasNext()) { CacheLoader l = i.next(); CacheLoaderConfig.IndividualCacheLoaderConfig cfg = cfgs.next(); if (cfg.isFetchPersistentState()) { l.loadState(subtree, os); break; } } } @Override public void storeEntireState(ObjectInputStream is) throws Exception { Iterator i = writeCacheLoaders.iterator(); Iterator cfgs = cacheLoaderConfigs.iterator(); while (i.hasNext()) { CacheLoader l = i.next(); CacheLoaderConfig.IndividualCacheLoaderConfig cfg = cfgs.next(); if (cfg.isFetchPersistentState()) { l.storeEntireState(is); break; } } } @Override public void storeState(Fqn subtree, ObjectInputStream is) throws Exception { Iterator i = writeCacheLoaders.iterator(); Iterator cfgs = cacheLoaderConfigs.iterator(); while (i.hasNext()) { CacheLoader l = i.next(); CacheLoaderConfig.IndividualCacheLoaderConfig cfg = cfgs.next(); if (cfg.isFetchPersistentState()) { l.storeState(subtree, is); break; } } } /** * Returns the number of cache loaders in the chain. */ public int getSize() { return cacheLoaders.size(); } /** * Returns a List of individual cache loaders configured. */ public List getCacheLoaders() { return Collections.unmodifiableList(cacheLoaders); } /** * Adds a cache loader to the chain (always added at the end of the chain) * * @param l the cache loader to add * @param cfg and its configuration */ public void addCacheLoader(CacheLoader l, CacheLoaderConfig.IndividualCacheLoaderConfig cfg) { synchronized (this) { cacheLoaderConfigs.add(cfg); cacheLoaders.add(l); if (!cfg.isIgnoreModifications()) { writeCacheLoaders.add(l); } } } @Override public String toString() { StringBuilder buf = new StringBuilder("ChainingCacheLoader{"); Iterator i = cacheLoaders.iterator(); Iterator c = cacheLoaderConfigs.iterator(); int count = 0; while (i.hasNext() && c.hasNext()) { CacheLoader loader = i.next(); CacheLoaderConfig.IndividualCacheLoaderConfig cfg = c.next(); buf.append(++count); buf.append(": IgnoreMods? "); buf.append(cfg.isIgnoreModifications()); buf.append(" CLoader: "); buf.append(loader); buf.append("; "); } buf.append("}"); return buf.toString(); } public void purgeIfNecessary() throws Exception { Iterator loaders = cacheLoaders.iterator(); Iterator configs = cacheLoaderConfigs.iterator(); while (loaders.hasNext() && configs.hasNext()) { CacheLoader myLoader = loaders.next(); CacheLoaderConfig.IndividualCacheLoaderConfig myConfig = configs.next(); if (!myConfig.isIgnoreModifications() && myConfig.isPurgeOnStartup()) myLoader.remove(Fqn.ROOT); } } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/loader/JDBCCacheLoaderOld.java0000644000175000017500000002271511067143565027745 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.loader; import org.apache.commons.logging.LogFactory; import org.jboss.cache.Fqn; import org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * JDBC CacheLoader implementation. *

        * This implementation uses one table. The table consists of three columns: *

          *
        • text column for fqn (which is also a primary key)
        • *
        • blob column for attributes (can contain null)
        • *
        • text column for parent fqn (can contain null)
        • *
        *

        * The configuration options are: *

        * Table configuration *

          *
        • cache.jdbc.table.name - the table name (default is jbosscache)
        • *
        • cache.jdbc.table.create - should be true or false, indicates whether to create the table at start phase
        • *
        • cache.jdbc.table.drop - should be true or false, indicates whether to drop the table at stop phase
        • *
        • cache.jdbc.table.primarykey - the name for the table primary key (default is jbosscache_pk)
        • *
        • cache.jdbc.fqn.column - the name for the fqn column (default is fqn)
        • *
        • cache.jdbc.fqn.type - the type for the fqn column (default is varchar(255))
        • *
        • cache.jdbc.node.column - the name for the node's contents column (default is node)
        • *
        • cache.jdbc.node.type - the type for the node's contents column (default is blob)
        • *
        • cache.jdbc.parent.column - the name for the parent fqn column (default is parent)
        • *
        *

        * DataSource configuration *

          *
        • cache.jdbc.datasource - the JNDI name of the datasource
        • *
        *

        * JDBC driver configuration (used when DataSource is not configured) *

          *
        • cache.jdbc.driver - fully qualified JDBC driver name
        • *
        • cache.jdbc.url - URL to connect to the database
        • *
        • cache.jdbc.user - the username to use to connect to the database
        • *
        • cache.jdbc.password - the password to use to connect to the database
        • *
        * * @author Alexey Loubyansky * @author Hany Mesha * @author Galder Zamarreno * @version $Revision: 6802 $ * @deprecated please use the {@link org.jboss.cache.loader.JDBCCacheLoader}. */ @Deprecated @SuppressWarnings("deprecation") public class JDBCCacheLoaderOld extends AdjListJDBCCacheLoader { private JDBCCacheLoaderOldConfig config; public JDBCCacheLoaderOld() { log = LogFactory.getLog(getClass()); } @Override public AdjListJDBCCacheLoaderConfig processConfig(IndividualCacheLoaderConfig base) { if (base instanceof JDBCCacheLoaderOldConfig) { config = (JDBCCacheLoaderOldConfig) base; } else { config = new JDBCCacheLoaderOldConfig(base); } return config; } public IndividualCacheLoaderConfig getConfig() { return config; } /** * Adds/overrides a value in a node for a key. * If the node does not exist yet, the node will be created. * If parent nodes do not exist for the node, empty parent nodes will be created. * * @param name node's fqn * @param key attribute's key * @param value attribute's value * @return old value associated with the attribute's key or null if there was no value previously * associated with the attribute's key * @throws Exception */ public Object put(Fqn name, Object key, Object value) throws Exception { Map oldNode = loadNode(name); Object oldValue; Map node; if (oldNode == null || oldNode == NULL_NODE_IN_ROW) { node = new HashMap(); } else { node = oldNode; } oldValue = node.put(key, value); if (oldNode != null) { updateNode(name, node); } else { if (name.size() > 1) { for (int i = 1; i < name.size(); ++i) { final Fqn parent = name.getAncestor(i); if (!exists(parent)) { insertNode(parent, null, false); } } } insertNode(name, node, false); } return oldValue; } /** * Adds attributes from the passed in map to the existing node. * If there is no node for the fqn, a new node will be created. * * @param name node's fqn * @param attributes attributes * @throws Exception */ public void put(Fqn name, Map attributes) throws Exception { put(name, attributes, false); } /** * Removes a node and all its children. * Uses the same connection for all the db work. * * @param name node's fqn * @throws Exception */ public void remove(Fqn name) throws Exception { Connection con = null; PreparedStatement ps = null; try { if (name.size() == 0) { if (log.isDebugEnabled()) { log.debug("executing sql: " + config.getDeleteAllSql()); } con = cf.getConnection(); ps = con.prepareStatement(config.getDeleteAllSql()); int deletedRows = ps.executeUpdate(); if (log.isDebugEnabled()) { log.debug("total rows deleted: " + deletedRows); } } else { StringBuilder sql = new StringBuilder(300); sql.append("delete from ").append(config.getTable()).append(" where fqn in ("); //sql2.append("delete from " + table + " where fqn=? or parent in ("); List fqns = new ArrayList(); addChildrenToDeleteSql(name.toString(), sql, fqns); sql.append(')'); if (fqns.size() == 1) { if (log.isDebugEnabled()) { log.debug("executing sql: " + config.getDeleteNodeSql() + "(" + name + ")"); } con = cf.getConnection(); ps = con.prepareStatement(config.getDeleteNodeSql()); ps.setString(1, name.toString()); } else { if (log.isDebugEnabled()) { log.debug("executing sql: " + sql + " " + fqns); } con = cf.getConnection(); ps = con.prepareStatement(sql.toString()); for (int i = 0; i < fqns.size(); ++i) { ps.setString(i + 1, (String) fqns.get(i)); } } int deletedRows = ps.executeUpdate(); if (log.isDebugEnabled()) { log.debug("total rows deleted: " + deletedRows); } } } catch (SQLException e) { reportAndRethrowError("Failed to remove node " + name, e); } finally { safeClose(ps); cf.close(con); } } // Private private void addChildrenToDeleteSql(String name, StringBuilder sql, List fqns) throws SQLException { // for now have to use connection per method, i.e. can't pass the same connection to recursive // invocations because buggy PointBase driver invalidates result sets. Connection con = null; PreparedStatement selChildrenPs = null; ResultSet rs = null; try { if (log.isDebugEnabled()) { log.debug("executing sql: " + config.getSelectChildFqnsSql() + "(" + name + ")"); } con = cf.getConnection(); selChildrenPs = con.prepareStatement(config.getSelectChildFqnsSql()); selChildrenPs.setString(1, name); rs = selChildrenPs.executeQuery(); if (rs.next()) { do { String childStr = rs.getString(1); addChildrenToDeleteSql(childStr, sql, fqns); } while (rs.next()); } if (fqns.size() == 0) { sql.append("?"); } else { sql.append(", ?"); } fqns.add(name); } finally { safeClose(rs); safeClose(selChildrenPs); cf.close(con); } } @Override public void put(Fqn name, Map attributes, boolean override) throws Exception { // JBCACHE-769 -- make a defensive copy Map attrs = (attributes == null ? null : new HashMap(attributes)); Map oldNode = loadNode(name); if (oldNode != null) { if (!override && oldNode != NULL_NODE_IN_ROW && attrs != null) { attrs.putAll(oldNode); } updateNode(name, attrs); } else { if (name.size() > 1) { for (int i = 1; i < name.size(); ++i) { final Fqn parent = name.getAncestor(i); if (!exists(parent)) { insertNode(parent, null, false); } } } insertNode(name, attrs, false); } } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/loader/ClusteredCacheLoader.java0000644000175000017500000002544511021545744030534 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.loader; import net.jcip.annotations.ThreadSafe; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.CacheStatus; import org.jboss.cache.Fqn; import org.jboss.cache.Modification; import org.jboss.cache.NodeSPI; import org.jboss.cache.RegionManager; import org.jboss.cache.ReplicationException; import org.jboss.cache.commands.DataCommand; import org.jboss.cache.commands.read.ExistsCommand; import org.jboss.cache.commands.read.GetChildrenNamesCommand; import org.jboss.cache.commands.read.GetDataMapCommand; import org.jboss.cache.commands.read.GetKeyValueCommand; import org.jboss.cache.commands.remote.ClusteredGetCommand; import org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig; import org.jboss.cache.factories.CommandsFactory; import org.jboss.cache.factories.annotations.Inject; import org.jboss.cache.lock.StripedLock; import org.jgroups.Address; import org.jgroups.blocks.GroupRequest; import org.jgroups.blocks.RspFilter; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; /** * A cache loader that consults other members in the cluster for values. Does * not propagate update methods since replication should take care of this. A * timeout property is required, a long that * specifies in milliseconds how long to wait for results before returning a * null. * * @author Manik Surtani (manik@jboss.org) */ @ThreadSafe public class ClusteredCacheLoader extends AbstractCacheLoader { private static final Log log = LogFactory.getLog(ClusteredCacheLoader.class); private static final boolean trace = log.isTraceEnabled(); private StripedLock lock = new StripedLock(); private ClusteredCacheLoaderConfig config; private CommandsFactory commandsFactory; /** * A test to check whether the cache is in it's started state. If not, calls should not be made as the channel may * not have properly started, blocks due to state transfers may be in progress, etc. * * @return true if the cache is in it's STARTED state. */ protected boolean isCacheReady() { return cache.getCacheStatus() == CacheStatus.STARTED; } @Inject public void setCommandsFactory(CommandsFactory commandsFactory) { this.commandsFactory = commandsFactory; } /** * Sets the configuration. * A property timeout is used as the timeout value. */ public void setConfig(IndividualCacheLoaderConfig base) { if (base instanceof ClusteredCacheLoaderConfig) { this.config = (ClusteredCacheLoaderConfig) base; } else { config = new ClusteredCacheLoaderConfig(base); } } public IndividualCacheLoaderConfig getConfig() { return config; } public Set getChildrenNames(Fqn fqn) throws Exception { if (!isCacheReady() || !cache.getInvocationContext().isOriginLocal()) return Collections.emptySet(); lock.acquireLock(fqn, true); try { GetChildrenNamesCommand command = commandsFactory.buildGetChildrenNamesCommand(fqn); Object resp = callRemote(command); return (Set) resp; } finally { lock.releaseLock(fqn); } } @SuppressWarnings("deprecation") private Object callRemote(DataCommand dataCommand) throws Exception { if (trace) log.trace("cache=" + cache.getLocalAddress() + "; calling with " + dataCommand); ClusteredGetCommand clusteredGet = commandsFactory.buildClusteredGetCommand(false, dataCommand); List resps; // JBCACHE-1186 resps = cache.getRPCManager().callRemoteMethods(null, clusteredGet, GroupRequest.GET_ALL, config.getTimeout(), new ResponseValidityFilter(cache.getMembers(), cache.getLocalAddress()), false); if (resps == null) { if (log.isInfoEnabled()) log.info("No replies to call " + dataCommand + ". Perhaps we're alone in the cluster?"); throw new ReplicationException("No replies to call " + dataCommand + ". Perhaps we're alone in the cluster?"); } else { // test for and remove exceptions Iterator i = resps.iterator(); Object result = null; while (i.hasNext()) { Object o = i.next(); if (o instanceof Exception) { if (log.isDebugEnabled()) log.debug("Found remote exception among responses - removing from responses list", (Exception) o); } else if (o != null) { // keep looping till we find a FOUND answer. List clusteredGetResp = (List) o; // found? if (clusteredGetResp.get(0)) { result = clusteredGetResp.get(1); break; } } else if (!cache.getConfiguration().isUseRegionBasedMarshalling()) { throw new IllegalStateException("Received unexpected null response to " + clusteredGet); } // else region was inactive on peer; // keep looping to see if anyone else responded } if (trace) log.trace("got responses " + resps); return result; } } public Map get(Fqn name) throws Exception { return get0(name); } protected Map get0(Fqn name) throws Exception { // DON'T make a remote call if this is a remote call in the first place - leads to deadlocks - JBCACHE-1103 if (!isCacheReady() || !cache.getInvocationContext().isOriginLocal()) return Collections.emptyMap(); lock.acquireLock(name, true); try { GetDataMapCommand command = commandsFactory.buildGetDataMapCommand(name); Object resp = callRemote(command); return (Map) resp; } finally { lock.releaseLock(name); } } public boolean exists(Fqn name) throws Exception { // DON'T make a remote call if this is a remote call in the first place - leads to deadlocks - JBCACHE-1103 if (!isCacheReady() || !cache.getInvocationContext().isOriginLocal()) return false; lock.acquireLock(name, false); try { ExistsCommand command = commandsFactory.buildExistsNodeCommand(name); Object resp = callRemote(command); return resp != null && (Boolean) resp; } finally { lock.releaseLock(name); } } public Object put(Fqn name, Object key, Object value) throws Exception { // DON'T make a remote call if this is a remote call in the first place - leads to deadlocks - JBCACHE-1103 if (!isCacheReady() || !cache.getInvocationContext().isOriginLocal()) return null; lock.acquireLock(name, true); try { NodeSPI n = cache.peek(name, false); if (n == null) { GetKeyValueCommand command = commandsFactory.buildGetKeyValueCommand(name, key, true); return callRemote(command); } else { // dont bother with a remote call return n.getDirect(key); } } finally { lock.releaseLock(name); } } /** * Does nothing; replication handles put. */ public void put(Fqn name, Map attributes) throws Exception { } /** * Does nothing; replication handles put. */ @Override public void put(List modifications) throws Exception { } /** * Fetches the remove value, does not remove. Replication handles * removal. */ public Object remove(Fqn name, Object key) throws Exception { // DON'T make a remote call if this is a remote call in the first place - leads to deadlocks - JBCACHE-1103 if (!isCacheReady() || !cache.getInvocationContext().isOriginLocal()) return false; lock.acquireLock(name, true); try { NodeSPI n = cache.peek(name, true); if (n == null) { GetKeyValueCommand command = commandsFactory.buildGetKeyValueCommand(name, key, true); return callRemote(command); } else { // dont bother with a remote call return n.getDirect(key); } } finally { lock.releaseLock(name); } } /** * Does nothing; replication handles removal. */ public void remove(Fqn name) throws Exception { // do nothing } /** * Does nothing; replication handles removal. */ public void removeData(Fqn name) throws Exception { } /** * Does nothing. */ @Override public void prepare(Object tx, List modifications, boolean one_phase) throws Exception { } /** * Does nothing. */ @Override public void commit(Object tx) throws Exception { } /** * Does nothing. */ @Override public void rollback(Object tx) { } @Override public void loadEntireState(ObjectOutputStream os) throws Exception { //intentional no-op } @Override public void loadState(Fqn subtree, ObjectOutputStream os) throws Exception { // intentional no-op } @Override public void storeEntireState(ObjectInputStream is) throws Exception { // intentional no-op } @Override public void storeState(Fqn subtree, ObjectInputStream is) throws Exception { // intentional no-op } @Override public void setRegionManager(RegionManager manager) { } public static class ResponseValidityFilter implements RspFilter { private int numValidResponses = 0; private List
        pendingResponders; public ResponseValidityFilter(List
        expected, Address localAddress) { this.pendingResponders = new ArrayList
        (expected); // We'll never get a response from ourself this.pendingResponders.remove(localAddress); } public boolean isAcceptable(Object object, Address address) { pendingResponders.remove(address); if (object instanceof List) { List response = (List) object; Boolean foundResult = (Boolean) response.get(0); if (foundResult) numValidResponses++; } // always return true to make sure a response is logged by the JGroups RpcDispatcher. return true; } public boolean needMoreResponses() { return numValidResponses < 1 && pendingResponders.size() > 0; } } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/loader/FileCacheLoader.java0000755000175000017500000003615111032145203027444 0ustar twernertwernerpackage org.jboss.cache.loader; import net.jcip.annotations.ThreadSafe; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.Fqn; import org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig; import org.jboss.cache.lock.StripedLock; import org.jboss.util.stream.MarshalledValueInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Simple file-based CacheLoader implementation. Nodes are directories, attributes of a node is a file in the directory *

        * The FileCacheLoader has some severe limitations which restrict it's use in a production * environment, or if used in such an environment, it should be used with due care and sufficient * understanding of these limitations. *

          *
        • Due to the way the FileCacheLoader represents a tree structure on disk (directories and files) traversal is inefficient for deep trees.
        • *
        • Usage on shared filesystems like NFS, Windows shares, etc. should be avoided as these do not implement proper file locking and can cause data corruption.
        • *
        • Usage with an isolation level of NONE can cause corrupt writes as multiple threads attempt to write to the same file.
        • *
        • File systems are inherently not transactional, so when attempting to use your cache in a transactional context, failures when writing to the file (which happens during the commit phase) cannot be recovered.
        • *
        *

        * As a rule of thumb, it is recommended that the FileCacheLoader not be used in a highly concurrent, * transactional or stressful environment, and it's use is restricted to testing. *

        * In terms of concurrency, file systems are notoriously inconsistent in their implementations of concurrent locks. To get around * this and to meet the thread safety contracts set out in {@link CacheLoader}, this implementation uses a {@link org.jboss.cache.lock.StripedLock} * * @author Bela Ban * @author Galder Zamarreno * @author Manik Surtani * @version $Id: FileCacheLoader.java 6120 2008-06-30 11:58:59Z manik.surtani@jboss.com $ */ @ThreadSafe public class FileCacheLoader extends AbstractCacheLoader { File root = null; String rootPath = null; private static final Log log = LogFactory.getLog(FileCacheLoader.class); protected final StripedLock lock = new StripedLock(); private FileCacheLoaderConfig config; /** * CacheImpl data file. */ public static final String DATA = "data.dat"; /** * CacheImpl directory suffix. */ public static final String DIR_SUFFIX = "fdb"; /** * For full path, check '*' '<' '>' '|' '"' '?' Regex: [\*<>|"?] */ public static final Pattern PATH_PATTERN = Pattern.compile("[\\*<>|\"?]"); /** * For fqn, check '*' '<' '>' '|' '"' '?' and also '\' '/' and ':' */ public static final Pattern FQN_PATTERN = Pattern.compile("[\\\\\\/:*<>|\"?]"); private static boolean isOldWindows; static { float osVersion; try { osVersion = Float.parseFloat(System.getProperty("os.version").trim()); } catch (Exception e) { osVersion = -1; } // 4.x is windows NT/2000 and 5.x is XP. isOldWindows = System.getProperty("os.name").toLowerCase().startsWith("windows") && osVersion < 4; } public FileCacheLoader() { } public void setConfig(IndividualCacheLoaderConfig base) { if (base instanceof FileCacheLoaderConfig) { this.config = (FileCacheLoaderConfig) base; } else if (base != null) { this.config = new FileCacheLoaderConfig(base); } String location = this.config != null ? this.config.getLocation() : null; if (location != null && location.length() > 0) { root = new File(location); rootPath = root.getAbsolutePath() + File.separator; } } public IndividualCacheLoaderConfig getConfig() { return config; } @Override public void create() throws Exception { lock.acquireLock(Fqn.ROOT, true); try { if (root == null) { String tmpLocation = System.getProperty("java.io.tmpdir", "C:\\tmp"); root = new File(tmpLocation); rootPath = root.getAbsolutePath() + File.separator; } if (!root.exists()) { if (log.isTraceEnabled()) { log.trace("Creating cache loader location " + root); } if (config.isCheckCharacterPortability()) { /* Before creating the root, check whether the path is character portable. Anything that comes after is part of the fqn which is inspected later. */ isCharacterPortableLocation(root.getAbsolutePath()); } boolean created = root.mkdirs(); if (!created) { throw new IOException("Unable to create cache loader location " + root); } } if (!root.isDirectory()) { throw new IOException("Cache loader location [" + root + "] is not a directory!"); } } finally { lock.releaseLock(Fqn.ROOT); } } public Set getChildrenNames(Fqn fqn) throws Exception { lock.acquireLock(fqn, true); try { File parent = getDirectory(fqn, false); if (parent == null) return null; File[] children = parent.listFiles(); Set s = new HashSet(); for (File child : children) { if (child.isDirectory() && child.getName().endsWith(DIR_SUFFIX)) { String child_name = child.getName(); child_name = child_name.substring(0, child_name.lastIndexOf(DIR_SUFFIX) - 1); s.add(child_name); } } return s.size() == 0 ? null : s; } finally { lock.releaseLock(fqn); } } public Map get(Fqn fqn) throws Exception { lock.acquireLock(fqn, true); try { return loadAttributes(fqn); } finally { lock.releaseLock(fqn); } } public boolean exists(Fqn fqn) throws Exception { lock.acquireLock(fqn, true); try { File f = getDirectory(fqn, false); return f != null; } finally { lock.releaseLock(fqn); } } public Object put(Fqn fqn, Object key, Object value) throws Exception { lock.acquireLock(fqn, true); try { Object retval; Map m = loadAttributes(fqn); if (m == null) m = new HashMap(); retval = m.put(key, value); storeAttributes(fqn, m); return retval; } finally { lock.releaseLock(fqn); } } public void put(Fqn fqn, Map attributes) throws Exception { put(fqn, attributes, false); } @Override public void put(Fqn fqn, Map attributes, boolean erase) throws Exception { lock.acquireLock(fqn, true); try { Map m = erase ? new HashMap() : loadAttributes(fqn); if (m == null) m = new HashMap(); if (attributes != null) { m.putAll(attributes); } storeAttributes(fqn, m); } finally { lock.releaseLock(fqn); } } void put(Fqn fqn) throws Exception { getDirectory(fqn, true); } public Object remove(Fqn fqn, Object key) throws Exception { lock.acquireLock(fqn, true); try { Object retval; Map m = loadAttributes(fqn); if (m == null) return null; retval = m.remove(key); storeAttributes(fqn, m); return retval; } finally { lock.releaseLock(fqn); } } public void remove(Fqn fqn) throws Exception { lock.acquireLock(fqn, true); try { File dir = getDirectory(fqn, false); if (dir != null) { boolean flag = removeDirectory(dir, true); if (!flag) { log.warn("failed removing " + fqn); } } } finally { lock.releaseLock(fqn); } } public void removeData(Fqn fqn) throws Exception { lock.acquireLock(fqn, true); try { File f = getDirectory(fqn, false); if (f != null) { File data = new File(f, DATA); if (data.exists()) { boolean flag = data.delete(); if (!flag) { log.warn("failed removing file " + data.getName()); } } } } finally { lock.releaseLock(fqn); } } /* ----------------------- Private methods ------------------------ */ File getDirectory(Fqn fqn, boolean create) throws IOException { File f = new File(getFullPath(fqn)); if (!f.exists()) { if (create) { boolean make = f.mkdirs(); if (!make) throw new IOException("Unable to mkdirs " + f); } else { return null; } } return f; } /** * Recursively removes this and all subdirectories, plus all DATA files in them. To prevent damage, we only * remove files that are named DATA (data.dat) and directories which end in ".fdb". If there is a dir or file * that isn't named this way, the recursive removal will fail * * @return true if directory was removed, * false if not. */ boolean removeDirectory(File dir, boolean include_start_dir) { boolean success = true; File[] subdirs = dir.listFiles(); for (File file : subdirs) { if (file.isFile() && file.getName().equals(DATA)) { if (!file.delete()) { success = false; } continue; } if (file.isDirectory() && file.getName().endsWith(DIR_SUFFIX)) { if (!removeDirectory(file, false)) { success = false; } if (!file.delete()) { success = false; } } } if (include_start_dir && !dir.equals(root)) { if (dir.delete()) { return success; } success = false; } return success; } String getFullPath(Fqn fqn) { StringBuilder sb = new StringBuilder(rootPath); for (int i = 0; i < fqn.size(); i++) { Object tmp = fqn.get(i); // This is where we convert from Object to String! String tmp_dir = tmp.toString(); // returns tmp.this if it's a String sb.append(tmp_dir).append(".").append(DIR_SUFFIX).append(File.separator); } return sb.toString(); } protected Map loadAttributes(Fqn fqn) throws Exception { File f = getDirectory(fqn, false); if (f == null) return null; // i.e., this node does not exist. // this node exists so we should never return a null after this... at worst case, an empty HashMap. File child = new File(f, DATA); if (!child.exists()) return new HashMap(0); // no node attribs exist hence the empty HashMap. //if(!child.exists()) return null; Map m; try { //m = (Map) unmarshall(child); m = (Map) regionAwareUnmarshall(fqn, child); } catch (FileNotFoundException fnfe) { // child no longer exists! m = Collections.emptyMap(); } return m; } protected void storeAttributes(Fqn fqn, Map attrs) throws Exception { regionAwareMarshall(fqn, attrs); } @Override protected void doMarshall(Fqn fqn, Object toMarshall) throws Exception { Map attrs = (Map) toMarshall; File f = getDirectory(fqn, true); File child = new File(f, DATA); if (!child.exists()) { if (config.isCheckCharacterPortability()) { /* Check whether the entire file path (root + fqn + data file name), is length portable */ isLengthPortablePath(child.getAbsolutePath()); /* Check whether the fqn tree we're trying to store could contain non portable characters */ isCharacterPortableTree(fqn); } if (!child.createNewFile()) { throw new IOException("Unable to create file: " + child); } } FileOutputStream fileOut = new FileOutputStream(child); ObjectOutputStream output = new ObjectOutputStream(fileOut); getMarshaller().objectToObjectStream(attrs, output); output.close(); } @Override protected Object doUnmarshall(Fqn fqn, Object fromFile) throws Exception { FileInputStream fileIn = new FileInputStream((File) fromFile); ObjectInputStream input = new MarshalledValueInputStream(fileIn); Object unmarshalledObj = getMarshaller().objectFromObjectStream(input); input.close(); return unmarshalledObj; } protected boolean isCharacterPortableLocation(String fileAbsolutePath) { Matcher matcher = PATH_PATTERN.matcher(fileAbsolutePath); if (matcher.find()) { log.warn("Cache loader location ( " + fileAbsolutePath + " ) contains one of these characters: '*' '<' '>' '|' '\"' '?'"); log.warn("Directories containing these characters are illegal in some operative systems and could lead to portability issues"); return false; } return true; } protected boolean isCharacterPortableTree(Fqn fqn) { List elements = fqn.peekElements(); // Don't assume the Fqn is composed of Strings!! for (Object anElement : elements) { // getFullPath converts Object to String via toString(), so we do too Matcher matcher = FQN_PATTERN.matcher(anElement.toString()); if (matcher.find()) { log.warn("One of the Fqn ( " + fqn + " ) elements contains one of these characters: '*' '<' '>' '|' '\"' '?' '\\' '/' ':' "); log.warn("Directories containing these characters are illegal in some operating systems and could lead to portability issues"); return false; } } return true; } protected boolean isLengthPortablePath(String absoluteFqnPath) { if (isOldWindows && absoluteFqnPath.length() > 255) { log.warn("The full absolute path to the fqn that you are trying to store is bigger than 255 characters, this could lead to problems on certain Windows systems: " + absoluteFqnPath); return false; } return true; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/loader/ClusteredCacheLoaderConfig.java0000644000175000017500000000356511004125045031646 0ustar twernertwernerpackage org.jboss.cache.loader; import org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig; import org.jboss.cache.config.Dynamic; import java.util.Properties; public class ClusteredCacheLoaderConfig extends IndividualCacheLoaderConfig { /** * The serialVersionUID */ private static final long serialVersionUID = -3425487656984237468L; @Dynamic private long timeout = 10000; public ClusteredCacheLoaderConfig() { setClassName(ClusteredCacheLoader.class.getName()); } /** * For use by {@link ClusteredCacheLoader}. * * @param base generic config object created by XML parsing. */ ClusteredCacheLoaderConfig(IndividualCacheLoaderConfig base) { setClassName(ClusteredCacheLoader.class.getName()); populateFromBaseConfig(base); } public long getTimeout() { return timeout; } public void setTimeout(long timeout) { testImmutability("timeout"); this.timeout = timeout; } @Override public void setProperties(Properties props) { super.setProperties(props); try { timeout = Long.valueOf(props.getProperty("timeout")); } catch (Exception e) { log.info("Using default value for config property 'timeout' - " + timeout); } } @Override public boolean equals(Object obj) { if (obj instanceof ClusteredCacheLoaderConfig && equalsExcludingProperties(obj)) { ClusteredCacheLoaderConfig other = (ClusteredCacheLoaderConfig) obj; return (this.timeout == other.timeout); } return false; } @Override public int hashCode() { return 31 * hashCodeExcludingProperties() + (int) timeout; } @Override public ClusteredCacheLoaderConfig clone() throws CloneNotSupportedException { return (ClusteredCacheLoaderConfig) super.clone(); } }jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/loader/CacheLoaderManager.java0000644000175000017500000003577611021204713030150 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.loader; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.CacheException; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.RegionManager; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig; import org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig.SingletonStoreConfig; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.ConfigurationException; import org.jboss.cache.factories.ComponentRegistry; import org.jboss.cache.factories.annotations.Inject; import org.jboss.cache.factories.annotations.Start; import org.jboss.cache.factories.annotations.Stop; import org.jboss.cache.util.reflect.ReflectionUtil; import java.util.ArrayList; import java.util.Iterator; import java.util.Set; import java.util.StringTokenizer; /** * Manages all cache loader functionality. This class is typically initialised with an XML DOM Element, * represeting a cache loader configuration, or a {@link org.jboss.cache.config.CacheLoaderConfig} object. *

        * Usage: *

        * * CacheLoaderManager manager = new CacheLoaderManager(); * manager.setConfig(myXmlSnippet, myTreeCache); * CacheLoader loader = manager.getCacheLoader(); * *

        * The XML configuration passed in would typically look like: *

        * * * false * / *

        * * org.jboss.cache.loader.FileCacheLoader * true * false * false * * location=/tmp/file * * * * ]]> * * * @author Manik Surtani (manik@jboss.org) * @author Galder Zamarreno */ public class CacheLoaderManager { private static final Log log = LogFactory.getLog(CacheLoaderManager.class); private CacheLoaderConfig config; private CacheSPI cache; private CacheLoader loader; private boolean fetchPersistentState; private Configuration configuration; private RegionManager regionManager; private ComponentRegistry registry; @Inject public void injectDependencies(CacheSPI cache, Configuration configuration, RegionManager regionManager, ComponentRegistry registry) { this.regionManager = regionManager; this.config = configuration.getCacheLoaderConfig(); this.cache = cache; this.configuration = configuration; this.registry = registry; if (config != null) { try { loader = createCacheLoader(); } catch (Exception e) { throw new CacheException("Unable to create cache loaders", e); } } } /** * Sets a configuration object and creates a cacheloader accordingly. Primarily used for testing. * * @param config * @param cache * @throws CacheException */ public void setConfig(CacheLoaderConfig config, CacheSPI cache, Configuration configuration) throws CacheException { this.config = config == null ? configuration.getCacheLoaderConfig() : config; this.cache = cache; this.configuration = configuration; if (config != null) { try { loader = createCacheLoader(); } catch (Exception e) { throw new CacheException("Unable to create cache loaders", e); } } } /** * Creates the cache loader based on a cache loader config passed in. * * @return a configured cacheloader * @throws IllegalAccessException * @throws InstantiationException * @throws ClassNotFoundException */ private CacheLoader createCacheLoader() throws Exception { CacheLoader tmpLoader; // if we only have a single cache loader configured in the chaining cacheloader then // don't use a chaining cache loader at all. ArrayList finalConfigs = new ArrayList(); // also if we are using passivation then just directly use the first cache loader. if (config.useChainingCacheLoader()) { // create chaining cache loader. tmpLoader = new ChainingCacheLoader(); ChainingCacheLoader ccl = (ChainingCacheLoader) tmpLoader; Iterator it = config.getIndividualCacheLoaderConfigs().iterator(); // only one cache loader may have fetchPersistentState to true. int numLoadersWithFetchPersistentState = 0; while (it.hasNext()) { CacheLoaderConfig.IndividualCacheLoaderConfig cfg = (CacheLoaderConfig.IndividualCacheLoaderConfig) it.next(); if (cfg.isFetchPersistentState()) { numLoadersWithFetchPersistentState++; fetchPersistentState = true; } if (numLoadersWithFetchPersistentState > 1) { throw new Exception("Invalid cache loader configuration!! Only ONE cache loader may have fetchPersistentState set to true. Cache will not start!"); } assertNotSingletonAndShared(cfg); CacheLoader l = createCacheLoader(cfg, cache); cfg = l.getConfig(); finalConfigs.add(cfg); // Only loaders that deal w/ state transfer factor into // whether the overall chain supports ExtendedCacheLoader ccl.addCacheLoader(l, cfg); } } else { CacheLoaderConfig.IndividualCacheLoaderConfig cfg = config.getIndividualCacheLoaderConfigs().get(0); tmpLoader = createCacheLoader(cfg, cache); finalConfigs.add(tmpLoader.getConfig() == null ? cfg : tmpLoader.getConfig()); fetchPersistentState = cfg.isFetchPersistentState(); assertNotSingletonAndShared(cfg); } // Update the config with those actually used by the loaders ReflectionUtil.setValue(config, "accessible", true); config.setIndividualCacheLoaderConfigs(finalConfigs); return tmpLoader; } private void assertNotSingletonAndShared(IndividualCacheLoaderConfig cfg) { SingletonStoreConfig ssc = cfg.getSingletonStoreConfig(); if (ssc != null && ssc.isSingletonStoreEnabled() && config.isShared()) throw new ConfigurationException("Invalid cache loader configuration!! If a cache loader is configured as a singleton, the cache loader cannot be shared in a cluster!"); } /** * Creates the cache loader based on the configuration. * * @param cfg * @param cache * @return a cache loader * @throws Exception */ @SuppressWarnings("deprecation") private CacheLoader createCacheLoader(CacheLoaderConfig.IndividualCacheLoaderConfig cfg, CacheSPI cache) throws Exception { // create loader CacheLoader tmpLoader = cfg.getCacheLoader() == null ? createInstance(cfg.getClassName()) : cfg.getCacheLoader(); if (tmpLoader != null) { // async? if (cfg.isAsync()) { CacheLoader asyncDecorator; asyncDecorator = new AsyncCacheLoader(tmpLoader); tmpLoader = asyncDecorator; } if (cfg.isIgnoreModifications()) { AbstractDelegatingCacheLoader readOnlyDecorator; readOnlyDecorator = new ReadOnlyDelegatingCacheLoader(tmpLoader); tmpLoader = readOnlyDecorator; } // singleton? SingletonStoreConfig ssc = cfg.getSingletonStoreConfig(); if (ssc != null && ssc.isSingletonStoreEnabled()) { Object decorator = createInstance(ssc.getSingletonStoreClass()); /* class providing singleton store functionality must extend AbstractDelegatingCacheLoader so that * underlying cacheloader can be set. */ if (decorator instanceof AbstractDelegatingCacheLoader) { AbstractDelegatingCacheLoader singletonDecorator = (AbstractDelegatingCacheLoader) decorator; /* set the cache loader to where calls will be delegated by the class providing the singleton * store functionality. */ singletonDecorator.setCacheLoader(tmpLoader); tmpLoader = singletonDecorator; } else { throw new Exception("Invalid cache loader configuration!! Singleton store implementation class must extend org.jboss.cache.loader.AbstractDelegatingCacheLoader"); } } // load props tmpLoader.setConfig(cfg); setCacheInLoader(cache, tmpLoader); // we should not be creating/starting the cache loader here - this should be done in the separate // startCacheLoader() method. // tmpLoader.create(); // tmpLoader.start(); if (configuration != null && configuration.isUseRegionBasedMarshalling()) { tmpLoader.setRegionManager(regionManager); } } return tmpLoader; } /** * Sets the cache instance associated with the given cache loader. This method was created for testing purpouses * so that it can be overriden in the mock version of the CacheLoaderManager. * * @param c instance of cache to be set in cache loader * @param loader cache loader to which assign the cache instance */ protected void setCacheInLoader(CacheSPI c, CacheLoader loader) { loader.setCache(c); } private CacheLoader createInstance(String className) throws ClassNotFoundException, IllegalAccessException, InstantiationException { if (log.isTraceEnabled()) log.trace("instantiating class " + className); Class cl = Thread.currentThread().getContextClassLoader().loadClass(className); return (CacheLoader) cl.newInstance(); } /** * Performs a preload on the cache based on the cache loader preload configs used when configuring the cache. * * @throws Exception */ @Start(priority = 50) public void preloadCache() throws CacheException { if (loader != null) { if (config.getPreload() == null || config.getPreload().equals("")) return; if (log.isDebugEnabled()) log.debug("preloading transient state from cache loader " + loader); StringTokenizer st = new StringTokenizer(config.getPreload(), ","); String tok; Fqn fqn; long start, stop, total; start = System.currentTimeMillis(); while (st.hasMoreTokens()) { tok = st.nextToken(); fqn = Fqn.fromString(tok.trim()); if (log.isTraceEnabled()) log.trace("preloading " + fqn); preload(fqn, true, true); } stop = System.currentTimeMillis(); total = stop - start; if (log.isDebugEnabled()) { log.debug("preloading transient state from cache loader was successful (in " + total + " milliseconds)"); } } } /** * Preloads a specific Fqn into the cache from the configured cacheloader * * @param fqn fqn to preload * @param preloadParents whether we preload parents * @param preloadChildren whether we preload children * @throws CacheException if we are unable to preload */ public void preload(Fqn fqn, boolean preloadParents, boolean preloadChildren) throws CacheException { cache.getInvocationContext().getOptionOverrides().setSkipDataGravitation(true); cache.getInvocationContext().getOptionOverrides().setSkipCacheStatusCheck(true); // 1. Load the attributes first // but this will go down the entire damn chain!! :S cache.get(fqn, "bla"); // 2. Then load the parents if (preloadParents) { Fqn tmp_fqn = Fqn.ROOT; for (int i = 0; i < fqn.size() - 1; i++) { tmp_fqn = Fqn.fromRelativeElements(tmp_fqn, fqn.get(i)); cache.get(tmp_fqn, "bla"); } } if (preloadChildren) { // 3. Then recursively for all child nodes, preload them as well Set children; try { children = loader.getChildrenNames(fqn); } catch (Exception e) { throw new CacheException("Unable to preload from cache loader", e); } if (children != null) { for (Object aChildren : children) { String child_name = (String) aChildren; Fqn child_fqn = Fqn.fromRelativeElements(fqn, child_name); preload(child_fqn, false, true); } } } } /** * Returns the configuration element of the cache loaders */ public CacheLoaderConfig getCacheLoaderConfig() { return config; } /** * Returns the cache loader */ public CacheLoader getCacheLoader() { return loader; } /** * Tests if we're using passivation */ public boolean isPassivation() { return config.isPassivation(); } /** * Returns true if at least one of the configured cache loaders has set fetchPersistentState to true. */ public boolean isFetchPersistentState() { return fetchPersistentState; } @Stop public void stopCacheLoader() { if (loader != null) { // stop the cache loader loader.stop(); // destroy the cache loader loader.destroy(); } } @Start public void startCacheLoader() throws CacheException { if (config == null) config = configuration.getCacheLoaderConfig(); if (config != null && loader == null) { try { loader = createCacheLoader(); } catch (Exception e) { throw new CacheException("Unable to create cache loaders", e); } } if (loader != null) { try { // wire any deps. registry.wireDependencies(loader); // create the cache loader loader.create(); // start the cache loader loader.start(); purgeLoaders(false); } catch (Exception e) { throw new CacheException("Unable to start cache loaders", e); } } } public void purgeLoaders(boolean force) throws Exception { if ((loader instanceof ChainingCacheLoader) && !force) { ((ChainingCacheLoader) loader).purgeIfNecessary(); } else { CacheLoaderConfig.IndividualCacheLoaderConfig first = getCacheLoaderConfig().getFirstCacheLoaderConfig(); if (force || (first != null && first.isPurgeOnStartup())) { loader.remove(Fqn.ROOT); } } } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/ReplicationException.java0000644000175000017500000000132311032145203027362 0ustar twernertwerner// $Id: ReplicationException.java 6120 2008-06-30 11:58:59Z manik.surtani@jboss.com $ /* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache; /** * Thrown when a replication problem occurred */ public class ReplicationException extends CacheException { private static final long serialVersionUID = 33172388691879866L; public ReplicationException() { super(); } public ReplicationException(Throwable cause) { super(cause); } public ReplicationException(String msg) { super(msg); } public ReplicationException(String msg, Throwable cause) { super(msg, cause); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/NodeFactory.java0000644000175000017500000001133411041076631025462 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.CommandsFactory; import org.jboss.cache.factories.ComponentFactory; import org.jboss.cache.factories.annotations.Inject; import org.jboss.cache.factories.annotations.Start; import org.jboss.cache.interceptors.InterceptorChain; import org.jboss.cache.invocation.InvocationContextContainer; import org.jboss.cache.invocation.NodeInvocationDelegate; import org.jboss.cache.lock.LockStrategyFactory; import org.jboss.cache.optimistic.TransactionWorkspace; import org.jboss.cache.optimistic.WorkspaceNode; import org.jboss.cache.optimistic.WorkspaceNodeImpl; import java.util.Map; /** * Generates new nodes based on the {@link CacheSPI} configuration. * * @author Manik Surtani (manik@jboss.org) */ public class NodeFactory extends ComponentFactory { private CacheSPI cache; private boolean optimistic; private Configuration configuration; private InvocationContextContainer invocationContextContainer; private InterceptorChain interceptorChain; private CommandsFactory commandsFactory; private LockStrategyFactory lockStrategyFactory; @Override protected T construct(Class componentType) { throw new UnsupportedOperationException("Should never be called!"); } public static enum NodeType { UNVERSIONED_NODE, VERSIONED_NODE, WORKSPACE_NODE } /** * Constructs an instance of the factory */ public NodeFactory(CacheSPI cache) { this.cache = cache; init(); } public NodeFactory() { } @Inject private void injectDependencies(CacheSPI cache, Configuration configuration, InvocationContextContainer invocationContextContainer, InterceptorChain interceptorChain, CommandsFactory commandsFactory, LockStrategyFactory lockStrategyFactory) { this.cache = cache; this.configuration = configuration; this.invocationContextContainer = invocationContextContainer; this.interceptorChain = interceptorChain; this.commandsFactory = commandsFactory; this.lockStrategyFactory = lockStrategyFactory; } /** * Initialises the node factory with the configuration from the cache. */ @Start public void init() { optimistic = configuration.isNodeLockingOptimistic(); } /** * Creates a new {@link Node} instance. * * @param childName the new node's name * @param fqn the new node's Fqn * @param parent the new node's parent * @param data the new node's attribute map * @param mapSafe true if param data can safely * be directly assigned to the new node's data field; * false if param data's contents * should be copied into the new node's data field. * @return the new node */ public NodeSPI createDataNode(Object childName, Fqn fqn, NodeSPI parent, Map data, boolean mapSafe) { UnversionedNode un = optimistic ? new VersionedNode(fqn, parent, data, cache) : new UnversionedNode(childName, fqn, data, mapSafe, cache); // always assume that new nodes do not have data loaded un.setDataLoaded(false); NodeInvocationDelegate nid = new NodeInvocationDelegate(un); // Too slow to have these autowired for now. Look at manually wiring them. nid.initialize(configuration, invocationContextContainer, componentRegistry, interceptorChain); nid.injectDependencies(cache); un.injectDependencies(cache, commandsFactory, lockStrategyFactory); // componentRegistry.wireDependencies(nid); // componentRegistry.wireDependencies(un); // back ref un.setDelegate(nid); return nid; } public Node createNode(Object childName, Node parent, Map data) { return createNodeOfType(parent, childName, parent, data); } public Node createNodeOfType(Node template, Object childName, Node parent, Map data) { // not a workspace node. return createDataNode(childName, Fqn.fromRelativeElements(parent.getFqn(), childName), (NodeSPI) parent, data, false); } public WorkspaceNode createWorkspaceNode(NodeSPI dataNode, TransactionWorkspace workspace) { return new WorkspaceNodeImpl(dataNode, workspace); } public NodeSPI createRootDataNode() { return createDataNode(null, Fqn.ROOT, null, null, false); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/RPCManagerImpl.java0000644000175000017500000005556611032145203026015 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.commands.ReplicableCommand; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.RuntimeConfig; import org.jboss.cache.factories.ComponentRegistry; import org.jboss.cache.factories.annotations.Inject; import org.jboss.cache.factories.annotations.Start; import org.jboss.cache.factories.annotations.Stop; import org.jboss.cache.interceptors.InterceptorChain; import org.jboss.cache.invocation.InvocationContextContainer; import org.jboss.cache.lock.LockManager; import org.jboss.cache.lock.LockUtil; import org.jboss.cache.lock.TimeoutException; import org.jboss.cache.marshall.CommandAwareRpcDispatcher; import org.jboss.cache.marshall.InactiveRegionAwareRpcDispatcher; import org.jboss.cache.marshall.Marshaller; import org.jboss.cache.notifications.Notifier; import org.jboss.cache.remoting.jgroups.ChannelMessageListener; import org.jboss.cache.statetransfer.StateTransferManager; import org.jboss.cache.transaction.GlobalTransaction; import org.jboss.cache.transaction.TransactionTable; import org.jboss.cache.util.ThreadGate; import org.jboss.cache.util.reflect.ReflectionUtil; import org.jgroups.Address; import org.jgroups.Channel; import org.jgroups.ChannelException; import org.jgroups.ChannelFactory; import org.jgroups.ExtendedMembershipListener; import org.jgroups.JChannel; import org.jgroups.StateTransferException; import org.jgroups.View; import org.jgroups.blocks.GroupRequest; import org.jgroups.blocks.RspFilter; import org.jgroups.util.Rsp; import org.jgroups.util.RspList; import javax.transaction.TransactionManager; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.Vector; /** * Manager that handles all RPC calls between JBoss Cache instances * * @author Manik Surtani (manik@jboss.org) */ public class RPCManagerImpl implements RPCManager { private Channel channel; private final Log log = LogFactory.getLog(RPCManagerImpl.class); private List

        members; private final Object coordinatorLock = new Object(); /** * True if this Cache is the coordinator. */ private volatile boolean coordinator = false; /** * Thread gate used to block Dispatcher during JGroups FLUSH protocol */ private final ThreadGate flushBlockGate = new ThreadGate(); /** * JGroups RpcDispatcher in use. */ private CommandAwareRpcDispatcher rpcDispatcher = null; /** * JGroups message listener. */ private ChannelMessageListener messageListener; private Configuration configuration; private Notifier notifier; private CacheSPI spi; private InvocationContextContainer invocationContextContainer; private final boolean trace = log.isTraceEnabled(); private Marshaller marshaller; private TransactionManager txManager; private TransactionTable txTable; private InterceptorChain interceptorChain; private boolean isUsingBuddyReplication; private boolean isInLocalMode; private ComponentRegistry componentRegistry; private LockManager lockManager; @Inject private void setupDependencies(ChannelMessageListener messageListener, Configuration configuration, Notifier notifier, CacheSPI spi, Marshaller marshaller, TransactionTable txTable, TransactionManager txManager, InvocationContextContainer container, InterceptorChain interceptorChain, ComponentRegistry componentRegistry, LockManager lockManager) { this.messageListener = messageListener; this.configuration = configuration; this.notifier = notifier; this.spi = spi; this.marshaller = marshaller; this.txManager = txManager; this.txTable = txTable; this.invocationContextContainer = container; this.interceptorChain = interceptorChain; this.componentRegistry = componentRegistry; this.lockManager = lockManager; } // ------------ START: Lifecycle methods ------------ @Start(priority = 15) public void start() { switch (configuration.getCacheMode()) { case LOCAL: log.debug("cache mode is local, will not create the channel"); isInLocalMode = true; isUsingBuddyReplication = false; break; case REPL_SYNC: case REPL_ASYNC: case INVALIDATION_ASYNC: case INVALIDATION_SYNC: isInLocalMode = false; isUsingBuddyReplication = configuration.getBuddyReplicationConfig() != null && configuration.getBuddyReplicationConfig().isEnabled(); if (log.isDebugEnabled()) log.debug("Cache mode is " + configuration.getCacheMode()); boolean fetchState = shouldFetchStateOnStartup(); initialiseChannelAndRpcDispatcher(fetchState); if (fetchState) { try { long start = System.currentTimeMillis(); // connect and state transfer channel.connect(configuration.getClusterName(), null, null, configuration.getStateRetrievalTimeout()); //if I am not the only and the first member than wait for a state to arrive if (getMembers().size() > 1) messageListener.waitForState(); if (log.isDebugEnabled()) log.debug("connected, state was retrieved successfully (in " + (System.currentTimeMillis() - start) + " milliseconds)"); } catch (StateTransferException ste) { // make sure we disconnect from the channel before we throw this exception! // JBCACHE-761 disconnect(); throw new CacheException("Unable to fetch state on startup", ste); } catch (ChannelException e) { throw new CacheException("Unable to connect to JGroups channel", e); } catch (Exception ex) { throw new CacheException("Unable to fetch state on startup", ex); } } else { //otherwise just connect try { channel.connect(configuration.getClusterName()); } catch (ChannelException e) { throw new CacheException("Unable to connect to JGroups channel", e); } } if (log.isInfoEnabled()) log.info("Cache local address is " + getLocalAddress()); } } public void disconnect() { if (channel != null && channel.isOpen()) { log.info("Disconnecting and closing the Channel"); channel.disconnect(); channel.close(); } } @Stop(priority = 8) public void stop() { try { disconnect(); } catch (Exception toLog) { log.error("Problem closing channel; setting it to null", toLog); } channel = null; configuration.getRuntimeConfig().setChannel(null); if (rpcDispatcher != null) { log.info("Stopping the RpcDispatcher"); rpcDispatcher.stop(); } if (members != null) members = null; coordinator = false; rpcDispatcher = null; } /** * @return true if we need to fetch state on startup. I.e., initiate a state transfer. */ private boolean shouldFetchStateOnStartup() { boolean loaderFetch = configuration.getCacheLoaderConfig() != null && configuration.getCacheLoaderConfig().isFetchPersistentState(); return !configuration.isInactiveOnStartup() && !isUsingBuddyReplication && (configuration.isFetchInMemoryState() || loaderFetch); } @SuppressWarnings("deprecation") private void initialiseChannelAndRpcDispatcher(boolean fetchState) throws CacheException { channel = configuration.getRuntimeConfig().getChannel(); if (channel == null) { // Try to create a multiplexer channel channel = getMultiplexerChannel(); if (channel != null) { ReflectionUtil.setValue(configuration, "accessible", true); configuration.setUsingMultiplexer(true); if (log.isDebugEnabled()) log.debug("Created Multiplexer Channel for cache cluster " + configuration.getClusterName() + " using stack " + configuration.getMultiplexerStack()); } else { try { if (configuration.getClusterConfig() == null) { log.debug("setting cluster properties to default value"); channel = new JChannel(configuration.getDefaultClusterConfig()); } else { if (trace) { log.trace("Cache cluster properties: " + configuration.getClusterConfig()); } channel = new JChannel(configuration.getClusterConfig()); } } catch (ChannelException e) { throw new CacheException(e); } } configuration.getRuntimeConfig().setChannel(channel); } // Channel.LOCAL *must* be set to false so we don't see our own messages - otherwise invalidations targeted at // remote instances will be received by self. channel.setOpt(Channel.LOCAL, false); channel.setOpt(Channel.AUTO_RECONNECT, true); channel.setOpt(Channel.AUTO_GETSTATE, fetchState); channel.setOpt(Channel.BLOCK, true); // always use the InactiveRegionAwareRpcDispatcher - exceptions due to regions not being active should not propagate to remote // nodes as errors. - Manik // but only if we are using region based marshalling?!?? if (configuration.isUseRegionBasedMarshalling()) { rpcDispatcher = new InactiveRegionAwareRpcDispatcher(channel, messageListener, new MembershipListenerAdaptor(), spi, invocationContextContainer, interceptorChain, componentRegistry); } else { rpcDispatcher = new CommandAwareRpcDispatcher(channel, messageListener, new MembershipListenerAdaptor(), invocationContextContainer, invocationContextContainer, interceptorChain, componentRegistry); } rpcDispatcher.setRequestMarshaller(marshaller); rpcDispatcher.setResponseMarshaller(marshaller); } public Channel getChannel() { return channel; } private JChannel getMultiplexerChannel() throws CacheException { String stackName = configuration.getMultiplexerStack(); RuntimeConfig rtc = configuration.getRuntimeConfig(); ChannelFactory channelFactory = rtc.getMuxChannelFactory(); JChannel muxchannel = null; if (channelFactory != null) { try { muxchannel = (JChannel) channelFactory.createMultiplexerChannel(stackName, configuration.getClusterName()); } catch (Exception e) { throw new CacheException("Failed to create multiplexed channel using stack " + stackName, e); } } return muxchannel; } private void removeLocksForDeadMembers(NodeSPI node, List deadMembers) { Set deadOwners = new HashSet(); Object owner = lockManager.getWriteOwner(node); if (isLockOwnerDead(owner, deadMembers)) deadOwners.add((GlobalTransaction) owner); for (Object readOwner : lockManager.getReadOwners(node)) { if (isLockOwnerDead(readOwner, deadMembers)) deadOwners.add((GlobalTransaction) readOwner); } for (GlobalTransaction deadOwner : deadOwners) { boolean localTx = deadOwner.getAddress().equals(getLocalAddress()); boolean broken = LockUtil.breakTransactionLock(node, lockManager, deadOwner, localTx, txTable, txManager); if (broken && trace) log.trace("Broke lock for node " + node.getFqn() + " held by " + deadOwner); } // Recursively unlock children for (Object child : node.getChildrenDirect()) { removeLocksForDeadMembers((NodeSPI) child, deadMembers); } } private boolean isLockOwnerDead(Object owner, List deadMembers) { boolean result = false; if (owner != null && owner instanceof GlobalTransaction) { Object addr = ((GlobalTransaction) owner).getAddress(); result = deadMembers.contains(addr); } return result; } // ------------ END: Lifecycle methods ------------ // ------------ START: RPC call methods ------------ public List callRemoteMethods(Vector
        recipients, ReplicableCommand command, int mode, long timeout, boolean useOutOfBandMessage) throws Exception { return callRemoteMethods(recipients, command, mode, timeout, null, useOutOfBandMessage); } public List callRemoteMethods(Vector
        recipients, ReplicableCommand command, boolean synchronous, long timeout, boolean useOutOfBandMessage) throws Exception { return callRemoteMethods(recipients, command, synchronous ? GroupRequest.GET_ALL : GroupRequest.GET_NONE, timeout, useOutOfBandMessage); } public List callRemoteMethods(Vector
        recipients, ReplicableCommand command, int mode, long timeout, RspFilter responseFilter, boolean useOutOfBandMessage) throws Exception { // short circuit if we don't have an RpcDispatcher! if (rpcDispatcher == null) return null; int modeToUse = mode; int preferredMode; if ((preferredMode = spi.getInvocationContext().getOptionOverrides().getGroupRequestMode()) > -1) modeToUse = preferredMode; if (trace) log.trace("callRemoteMethods(): valid members are " + recipients + " methods: " + command + " Using OOB? " + useOutOfBandMessage); if (channel.flushSupported() && !flushBlockGate.await(configuration.getStateRetrievalTimeout())) { throw new TimeoutException("State retrieval timed out waiting for flush unblock."); } useOutOfBandMessage = false; RspList rsps = rpcDispatcher.invokeRemoteCommands(recipients, command, modeToUse, timeout, isUsingBuddyReplication, useOutOfBandMessage, responseFilter); if (mode == GroupRequest.GET_NONE) return Collections.emptyList();// async case if (trace) log.trace("(" + getLocalAddress() + "): responses for method " + command.getClass().getSimpleName() + ":\n" + rsps); // short-circuit no-return-value calls. if (rsps == null) return Collections.emptyList(); List retval = new ArrayList(rsps.size()); for (Rsp rsp : rsps.values()) { if (rsp.wasSuspected() || !rsp.wasReceived()) { CacheException ex; if (rsp.wasSuspected()) { ex = new SuspectException("Suspected member: " + rsp.getSender()); } else { ex = new TimeoutException("Replication timeout for " + rsp.getSender()); } retval.add(new ReplicationException("rsp=" + rsp, ex)); } else { Object value = rsp.getValue(); if (value instanceof Exception && !(value instanceof ReplicationException)) { // if we have any application-level exceptions make sure we throw them!! if (trace) log.trace("Recieved exception'" + value + "' from " + rsp.getSender()); throw (Exception) value; } retval.add(value); } } return retval; } // ------------ START: Partial state transfer methods ------------ public void fetchPartialState(List
        sources, Fqn sourceTarget, Fqn integrationTarget) throws Exception { String encodedStateId = sourceTarget + StateTransferManager.PARTIAL_STATE_DELIMITER + integrationTarget; fetchPartialState(sources, encodedStateId); } public void fetchPartialState(List
        sources, Fqn subtree) throws Exception { if (subtree == null) { throw new IllegalArgumentException("Cannot fetch partial state. Null subtree."); } fetchPartialState(sources, subtree.toString()); } private void fetchPartialState(List
        sources, String stateId) throws Exception { if (sources == null || sources.isEmpty() || stateId == null) { // should this really be throwing an exception? Are there valid use cases where partial state may not be available? - Manik // Yes -- cache is configured LOCAL but app doesn't know it -- Brian //throw new IllegalArgumentException("Cannot fetch partial state, targets are " + sources + " and stateId is " + stateId); if (log.isWarnEnabled()) log.warn("Cannot fetch partial state, targets are " + sources + " and stateId is " + stateId); return; } List
        targets = new LinkedList
        (sources); //skip *this* node as a target targets.remove(getLocalAddress()); if (targets.isEmpty()) { // Definitely no exception here -- this happens every time the 1st node in the // cluster activates a region!! -- Brian if (log.isDebugEnabled()) log.debug("Cannot fetch partial state. There are no target members specified"); return; } if (log.isDebugEnabled()) log.debug("Node " + getLocalAddress() + " fetching partial state " + stateId + " from members " + targets); boolean successfulTransfer = false; for (Address target : targets) { if (log.isDebugEnabled()) log.debug("Node " + getLocalAddress() + " fetching partial state " + stateId + " from member " + target); messageListener.setStateSet(false); successfulTransfer = channel.getState(target, stateId, configuration.getStateRetrievalTimeout()); if (successfulTransfer) { try { messageListener.waitForState(); } catch (Exception transferFailed) { successfulTransfer = false; } } if (log.isDebugEnabled()) log.debug("Node " + getLocalAddress() + " fetching partial state " + stateId + " from member " + target + (successfulTransfer ? " successful" : " failed")); if (successfulTransfer) break; } if (!successfulTransfer) { if (log.isDebugEnabled()) log.debug("Node " + getLocalAddress() + " could not fetch partial state " + stateId + " from any member " + targets); } } // ------------ END: Partial state transfer methods ------------ // ------------ START: Informational methods ------------ public Address getLocalAddress() { return channel != null ? channel.getLocalAddress() : null; } public List
        getMembers() { if (isInLocalMode) return null; if (members == null) return Collections.emptyList(); else return members; } public boolean isCoordinator() { return coordinator; } public Address getCoordinator() { if (channel == null) { return null; } synchronized (coordinatorLock) { while (members == null || members.isEmpty()) { log.debug("getCoordinator(): waiting on viewAccepted()"); try { coordinatorLock.wait(); } catch (InterruptedException e) { log.error("getCoordinator(): Interrupted while waiting for members to be set", e); break; } } return members != null && members.size() > 0 ? members.get(0) : null; } } // ------------ END: Informational methods ------------ /*----------------------- MembershipListener ------------------------*/ protected class MembershipListenerAdaptor implements ExtendedMembershipListener { public void viewAccepted(View newView) { Vector
        newMembers = newView.getMembers(); if (log.isInfoEnabled()) log.info("Received new cluster view: " + newView); synchronized (coordinatorLock) { boolean needNotification = false; if (newMembers != null) { if (members != null) { // we had a membership list before this event. Check to make sure we haven't lost any members, // and if so, determine what members have been removed // and roll back any tx and break any locks List
        removed = new ArrayList
        (members); removed.removeAll(newMembers); removeLocksForDeadMembers(spi.getRoot(), removed); } members = new ArrayList
        (newMembers); // defensive copy. needNotification = true; } // Now that we have a view, figure out if we are the coordinator coordinator = (members != null && members.size() != 0 && members.get(0).equals(getLocalAddress())); // now notify listeners - *after* updating the coordinator. - JBCACHE-662 if (needNotification && notifier != null) { InvocationContext ctx = spi.getInvocationContext(); notifier.notifyViewChange(newView, ctx); } // Wake up any threads that are waiting to know about who the coordinator is coordinatorLock.notifyAll(); } } /** * Called when a member is suspected. */ public void suspect(Address suspected_mbr) { } /** * Indicates that a channel has received a BLOCK event from FLUSH protocol. */ public void block() { flushBlockGate.close(); if (log.isDebugEnabled()) log.debug("Block received at " + getLocalAddress()); notifier.notifyCacheBlocked(true); notifier.notifyCacheBlocked(false); if (log.isDebugEnabled()) log.debug("Block processed at " + getLocalAddress()); } /** * Indicates that a channel has received a UNBLOCK event from FLUSH protocol. */ public void unblock() { if (log.isDebugEnabled()) log.debug("UnBlock received at " + getLocalAddress()); notifier.notifyCacheUnblocked(true); notifier.notifyCacheUnblocked(false); if (log.isDebugEnabled()) log.debug("UnBlock processed at " + getLocalAddress()); flushBlockGate.open(); } } /*------------------- End of MembershipListener ----------------------*/ }jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/transaction/0000755000175000017500000000000011376174017024735 5ustar twernertwerner././@LongLink0000000000000000000000000000014600000000000011566 Lustar rootrootjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/transaction/JBossTransactionManagerLookup.javajbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/transaction/JBossTransactionManagerLookup.jav0000644000175000017500000000116110727544301033345 0ustar twernertwernerpackage org.jboss.cache.transaction; import javax.naming.InitialContext; import javax.transaction.TransactionManager; /** * Uses JNDI to look-up the * {@link TransactionManager} instance from "java:/TransactionManager". * * @author Bela Ban, Aug 26 2003 * @version $Id: JBossTransactionManagerLookup.java 4834 2007-12-11 17:28:33Z manik.surtani@jboss.com $ */ public class JBossTransactionManagerLookup implements TransactionManagerLookup { public TransactionManager getTransactionManager() throws Exception { return (TransactionManager) new InitialContext().lookup("java:/TransactionManager"); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/transaction/TransactionTable.java0000644000175000017500000002734411032145203031027 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.transaction; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.CacheException; import org.jboss.cache.InvocationContext; import org.jboss.cache.RPCManager; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.annotations.Inject; import org.jboss.cache.factories.annotations.NonVolatile; import org.jboss.cache.factories.annotations.Start; import org.jgroups.Address; import javax.transaction.Status; import javax.transaction.SystemException; import javax.transaction.Transaction; import javax.transaction.TransactionManager; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * Maintains the mapping between a local {@link Transaction} and a {@link GlobalTransaction}. * Also stores {@link TransactionEntry} instances under a given transaction. * * @author Bela Ban Apr 14, 2003 * @version $Revision: 6120 $ */ @NonVolatile public class TransactionTable { private static final Log log = LogFactory.getLog(TransactionTable.class); private static final boolean trace = log.isTraceEnabled(); /** * Mapping between local (javax.transaction.Transaction) * and GlobalTransactions. * New: a local TX can have a number of GTXs */ protected final Map tx2gtxMap = new ConcurrentHashMap(); /** * Mappings between GlobalTransactions and modifications. */ protected final Map gtx2EntryMap = new ConcurrentHashMap(); protected final Map gtx2TxMap = new ConcurrentHashMap(); private TransactionManager transactionManager = null; private RPCManager rpcManager; private boolean isOptimisticLocking; private Configuration configuration; @Inject public void initialize(TransactionManager transactionManager, RPCManager rpcManager, Configuration configuration) { this.transactionManager = transactionManager; this.rpcManager = rpcManager; this.configuration = configuration; } @Start public void start() { isOptimisticLocking = configuration.isNodeLockingOptimistic(); } /** * Returns the number of local transactions. */ public int getNumLocalTransactions() { return tx2gtxMap.size(); } /** * Returns the number of global transactions. */ public int getNumGlobalTransactions() { return gtx2EntryMap.size(); } /** * Returns the global transaction associated with the local transaction. * Returns null if tx is null or it was not found. */ public GlobalTransaction get(Transaction tx) { if (tx == null) return null; return tx2gtxMap.get(tx); } /** * Returns the local transaction associated with a GlobalTransaction. * * @param gtx The GlobalTransaction * @return Transaction. The local transaction associated with a given * GlobalTransaction). This will be null if no local transaction is * associated with a given GTX */ public Transaction getLocalTransaction(GlobalTransaction gtx) { if (gtx == null) return null; return gtx2TxMap.get(gtx); } /** * If assers exists is true and the coresponding local transaction is null an IllegalStateExcetpion is being thrown. */ public Transaction getLocalTransaction(GlobalTransaction gtx, boolean assertExists) { Transaction ltx = getLocalTransaction(gtx); if (!assertExists) { return ltx; } if (ltx != null) { if (log.isDebugEnabled()) log.debug("Found local TX=" + ltx + ", global TX=" + gtx); return ltx; } else { throw new IllegalStateException(" found no local TX for global TX " + gtx); } } /** * Associates the global transaction with the local transaction. */ public void put(Transaction tx, GlobalTransaction gtx) { if (tx == null) { log.error("key (Transaction) is null"); return; } tx2gtxMap.put(tx, gtx); gtx2TxMap.put(gtx, tx); } /** * Returns the local transaction entry for the global transaction. * Returns null if tx is null or it was not found. */ public TransactionEntry get(GlobalTransaction gtx) { return gtx != null ? gtx2EntryMap.get(gtx) : null; } /** * Associates the global transaction with a transaction entry. */ public void put(GlobalTransaction tx, TransactionEntry entry) { if (tx == null) { log.error("key (GlobalTransaction) is null"); return; } gtx2EntryMap.put(tx, entry); } /** * Removes a global transation, returns the old transaction entry. */ public TransactionEntry remove(GlobalTransaction tx) { if (tx == null) return null; gtx2TxMap.remove(tx); return gtx2EntryMap.remove(tx); } /** * Removes a local transation, returns the global transaction entry. */ public GlobalTransaction remove(Transaction tx) { if (tx == null) return null; return tx2gtxMap.remove(tx); } public void remove(GlobalTransaction gtx, Transaction tx) { gtx2TxMap.remove(gtx); gtx2EntryMap.remove(gtx); tx2gtxMap.remove(tx); } /** * Returns summary debug information. */ @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(tx2gtxMap.size()).append(" mappings, "); sb.append(gtx2EntryMap.size()).append(" transactions"); return sb.toString(); } /** * Returns detailed debug information. */ public String toString(boolean printDetails) { if (!printDetails) return toString(); StringBuilder sb = new StringBuilder(); sb.append("LocalTransactions: ").append(tx2gtxMap.size()).append("\n"); sb.append("GlobalTransactions: ").append(gtx2EntryMap.size()).append("\n"); sb.append("tx2gtxMap:\n"); for (Map.Entry entry : tx2gtxMap.entrySet()) { sb.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n"); } sb.append("gtx2EntryMap:\n"); for (Map.Entry entry : gtx2EntryMap.entrySet()) { sb.append(entry.getKey()).append(": ").append(entry.getValue()).append("\n"); } return sb.toString(); } /** * Returns the transaction associated with the current thread. * If a local transaction exists, but doesn't yet have a mapping to a * GlobalTransaction, a new GlobalTransaction will be created and mapped to * the local transaction. Note that if a local transaction exists, but is * not ACTIVE or PREPARING, null is returned. * * @return A GlobalTransaction, or null if no (local) transaction was associated with the current thread */ public GlobalTransaction getCurrentTransaction() { return getCurrentTransaction(true); } /** * Returns the transaction associated with the thread; optionally creating * it if is does not exist. */ public GlobalTransaction getCurrentTransaction(boolean createIfNotExists) { Transaction tx; if ((tx = getLocalTransaction()) == null) {// no transaction is associated with the current thread return null; } if (!isValid(tx)) {// we got a non-null transaction, but it is not active anymore int status = -1; try { status = tx.getStatus(); } catch (SystemException e) { // do nothing } // JBCACHE-982 -- don't complain if COMMITTED if (status != Status.STATUS_COMMITTED) { log.warn("status is " + status + " (not ACTIVE or PREPARING); returning null)"); } else { log.trace("status is COMMITTED; returning null"); } return null; } return getCurrentTransaction(tx, createIfNotExists); } /** * Returns the transaction associated with the current thread. We get the * initial context and a reference to the TransactionManager to get the * transaction. This method is used by {@link #getCurrentTransaction()} */ protected Transaction getLocalTransaction() { if (transactionManager == null) { return null; } try { return transactionManager.getTransaction(); } catch (Throwable t) { return null; } } /** * Returns true if transaction is ACTIVE, false otherwise */ public static boolean isActive(Transaction tx) { if (tx == null) return false; int status; try { status = tx.getStatus(); return status == Status.STATUS_ACTIVE; } catch (SystemException e) { return false; } } /** * Returns true if transaction is PREPARING, false otherwise */ public static boolean isPreparing(Transaction tx) { if (tx == null) return false; int status; try { status = tx.getStatus(); return status == Status.STATUS_PREPARING; } catch (SystemException e) { return false; } } /** * Return s true of tx's status is ACTIVE or PREPARING * * @param tx * @return true if the tx is active or preparing */ public static boolean isValid(Transaction tx) { return isActive(tx) || isPreparing(tx); } /** * Tests whether the caller is in a valid transaction. If not, will throw a CacheException. */ public static void assertTransactionValid(InvocationContext ctx) { Transaction tx = ctx.getTransaction(); if (!isValid(tx)) try { throw new CacheException("Invalid transaction " + tx + ", status = " + (tx == null ? null : tx.getStatus())); } catch (SystemException e) { throw new CacheException("Exception trying to analyse status of transaction " + tx, e); } } /** * Returns the global transaction for this local transaction. */ public GlobalTransaction getCurrentTransaction(Transaction tx) { return getCurrentTransaction(tx, true); } /** * Returns the global transaction for this local transaction. * * @param createIfNotExists if true, if a global transaction is not found; one is created */ public GlobalTransaction getCurrentTransaction(Transaction tx, boolean createIfNotExists) { // removed synchronization on txTable because underlying implementation is thread safe // and JTA spec (section 3.4.3 Thread of Control, par 2) says that only one thread may // operate on the transaction at one time so no concern about 2 threads trying to call // this method for the same Transaction instance at the same time // GlobalTransaction gtx = get(tx); if (gtx == null && createIfNotExists) { Address addr = rpcManager.getLocalAddress(); gtx = GlobalTransaction.create(addr); put(tx, gtx); TransactionEntry ent; try { ent = isOptimisticLocking ? new OptimisticTransactionEntry(tx) : new TransactionEntry(tx); } catch (Exception e) { throw new CacheException("Unable to create a transaction entry!", e); } put(gtx, ent); if (trace) { log.trace("created new GTX: " + gtx + ", local TX=" + tx); } } return gtx; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/transaction/DummyContextFactory.java0000644000175000017500000000240211004116445031553 0ustar twernertwernerpackage org.jboss.cache.transaction; import javax.naming.Context; import javax.naming.NamingException; import javax.naming.spi.InitialContextFactory; import java.util.Hashtable; /** * @author bela * Date: May 15, 2003 * Time: 6:22:02 PM */ public class DummyContextFactory implements InitialContextFactory { static Context instance = null; /** * Creates an Initial Context for beginning name resolution. * Special requirements of this context are supplied * using environment. *

        * The environment parameter is owned by the caller. * The implementation will not modify the object or keep a reference * to it, although it may keep a reference to a clone or copy. * * @param environment The possibly null environment * specifying information to be used in the creation * of the initial context. * @return A non-null initial context object that implements the Context * interface. * @throws NamingException If cannot create an initial context. */ public Context getInitialContext(Hashtable environment) throws NamingException { if (instance == null) instance = new DummyContext(); return instance; } } ././@LongLink0000000000000000000000000000015000000000000011561 Lustar rootrootjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/transaction/JBossStandaloneJTAManagerLookup.javajbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/transaction/JBossStandaloneJTAManagerLookup.j0000644000175000017500000000366110700470066033164 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * Copyright 2005, JBoss Inc., and individual contributors as indicated * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.cache.transaction; import java.lang.reflect.Method; import javax.transaction.TransactionManager; import javax.transaction.UserTransaction; /** * JTA standalone TM lookup. * * @author Jason T. Greene */ public class JBossStandaloneJTAManagerLookup implements TransactionManagerLookup { private Method manager, user; public JBossStandaloneJTAManagerLookup() { try { manager = Class.forName("com.arjuna.ats.jta.TransactionManager").getMethod("transactionManager"); user = Class.forName("com.arjuna.ats.jta.UserTransaction").getMethod("userTransaction"); } catch (Exception e) { throw new RuntimeException(e); } } public TransactionManager getTransactionManager() throws Exception { return (TransactionManager) manager.invoke(null); } public UserTransaction getUserTransaction() throws Exception { return (UserTransaction) user.invoke(null); } }jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/transaction/OptimisticTransactionEntry.java0000644000175000017500000000316511031017265033146 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.transaction; import org.jboss.cache.optimistic.TransactionWorkspace; import org.jboss.cache.optimistic.TransactionWorkspaceImpl; import javax.transaction.RollbackException; import javax.transaction.SystemException; import javax.transaction.Transaction; /** * Subclasses the {@link TransactionEntry} class to add a {@link TransactionWorkspace}. Used with optimistic locking * where each call is assigned a trasnaction and a transaction workspace. * * @author Manik Surtani (manik@jboss.org) * @author Steve Woodcock (stevew@jofti.com) */ public class OptimisticTransactionEntry extends TransactionEntry { private TransactionWorkspace transactionWorkSpace = new TransactionWorkspaceImpl(); public OptimisticTransactionEntry(Transaction tx) throws SystemException, RollbackException { super(tx); } @Override public String toString() { StringBuilder sb = new StringBuilder(super.toString()); sb.append("\nworkspace: ").append(transactionWorkSpace); return sb.toString(); } /** * @return Returns the transactionWorkSpace. */ public TransactionWorkspace getTransactionWorkSpace() { return transactionWorkSpace; } /** * @param transactionWorkSpace The transactionWorkSpace to set. */ public void setTransactionWorkSpace(TransactionWorkspace transactionWorkSpace) { this.transactionWorkSpace = transactionWorkSpace; } } ././@LongLink0000000000000000000000000000015000000000000011561 Lustar rootrootjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/transaction/GenericTransactionManagerLookup.javajbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/transaction/GenericTransactionManagerLookup.j0000644000175000017500000001306011017260120033336 0ustar twernertwernerpackage org.jboss.cache.transaction; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.transaction.TransactionManager; import java.lang.reflect.Method; /** * A transaction manager lookup class that attempts to locate a TransactionManager. * A variety of different classes and JNDI locations are tried, for servers * such as: *

          *
        • JBoss *
        • JRun4 *
        • Resin *
        • Orion *
        • JOnAS *
        • BEA Weblogic *
        • Websphere 4.0, 5.0, 5.1, 6.0 *
        • Sun, Glassfish *
        * If a transaction manager is not found, returns a {@link DummyTransactionManager}. * * @author Markus Plesser * @version $Id: GenericTransactionManagerLookup.java 5895 2008-05-28 13:38:24Z manik.surtani@jboss.com $ */ public class GenericTransactionManagerLookup implements TransactionManagerLookup { private static final Log log = LogFactory.getLog(GenericTransactionManagerLookup.class); /** * JNDI lookups performed? */ private static boolean lookupDone = false; /** * No JNDI available? */ private static boolean lookupFailed = false; /** * The JVM TransactionManager found. */ private static TransactionManager tm = null; /** * JNDI locations for TransactionManagers we know of */ private static String[][] knownJNDIManagers = { {"java:/TransactionManager", "JBoss, JRun4"}, {"java:comp/TransactionManager", "Resin 3.x"}, {"java:appserver/TransactionManager", "Sun Glassfish"}, {"java:pm/TransactionManager", "Borland, Sun"}, {"javax.transaction.TransactionManager", "BEA WebLogic"}, {"java:comp/UserTransaction", "Resin, Orion, JOnAS (JOTM)"}, }; /** * WebSphere 5.1 and 6.0 TransactionManagerFactory */ private static final String WS_FACTORY_CLASS_5_1 = "com.ibm.ws.Transaction.TransactionManagerFactory"; /** * WebSphere 5.0 TransactionManagerFactory */ private static final String WS_FACTORY_CLASS_5_0 = "com.ibm.ejs.jts.jta.TransactionManagerFactory"; /** * WebSphere 4.0 TransactionManagerFactory */ private static final String WS_FACTORY_CLASS_4 = "com.ibm.ejs.jts.jta.JTSXA"; /** * Get the systemwide used TransactionManager * * @return TransactionManager */ public TransactionManager getTransactionManager() { if (!lookupDone) doLookups(); if (tm != null) return tm; if (lookupFailed) { //fall back to a dummy from JBossCache tm = DummyTransactionManager.getInstance(); log.warn("Falling back to DummyTransactionManager from JBossCache"); } return tm; } /** * Try to figure out which TransactionManager to use */ @SuppressWarnings("unchecked") private static void doLookups() { if (lookupFailed) return; InitialContext ctx; try { ctx = new InitialContext(); } catch (NamingException e) { log.error("Failed creating initial JNDI context", e); lookupFailed = true; return; } //probe jndi lookups first for (String[] knownJNDIManager : knownJNDIManagers) { Object jndiObject; try { if (log.isDebugEnabled()) log.debug("Trying to lookup TransactionManager for " + knownJNDIManager[1]); jndiObject = ctx.lookup(knownJNDIManager[0]); } catch (NamingException e) { log.debug("Failed to perform a lookup for [" + knownJNDIManager[0] + " (" + knownJNDIManager[1] + ")]"); continue; } if (jndiObject instanceof TransactionManager) { tm = (TransactionManager) jndiObject; log.debug("Found TransactionManager for " + knownJNDIManager[1]); return; } } //try to find websphere lookups since we came here Class clazz; try { log.debug("Trying WebSphere 5.1: " + WS_FACTORY_CLASS_5_1); clazz = Class.forName(WS_FACTORY_CLASS_5_1); log.debug("Found WebSphere 5.1: " + WS_FACTORY_CLASS_5_1); } catch (ClassNotFoundException ex) { try { log.debug("Trying WebSphere 5.0: " + WS_FACTORY_CLASS_5_0); clazz = Class.forName(WS_FACTORY_CLASS_5_0); log.debug("Found WebSphere 5.0: " + WS_FACTORY_CLASS_5_0); } catch (ClassNotFoundException ex2) { try { log.debug("Trying WebSphere 4: " + WS_FACTORY_CLASS_4); clazz = Class.forName(WS_FACTORY_CLASS_4); log.debug("Found WebSphere 4: " + WS_FACTORY_CLASS_4); } catch (ClassNotFoundException ex3) { log.debug("Couldn't find any WebSphere TransactionManager factory class, neither for WebSphere version 5.1 nor 5.0 nor 4"); lookupFailed = true; return; } } } try { Class[] signature = null; Object[] args = null; Method method = clazz.getMethod("getTransactionManager", signature); tm = (TransactionManager) method.invoke(null, args); } catch (Exception ex) { log.error("Found WebSphere TransactionManager factory class [" + clazz.getName() + "], but couldn't invoke its static 'getTransactionManager' method", ex); } } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/transaction/TransactionManagerLookup.java0000644000175000017500000000130210625013553032537 0ustar twernertwernerpackage org.jboss.cache.transaction; import org.jboss.cache.Cache; import org.jboss.cache.config.Configuration; import javax.transaction.TransactionManager; /** * Factory interface, allows {@link Cache} to use different transactional systems. * Names of implementors of this class can be configured using * {@link Configuration#setTransactionManagerLookupClass}. * * @author Bela Ban, Aug 26 2003 * @version $Id: TransactionManagerLookup.java 3892 2007-05-23 10:28:59Z msurtani $ */ public interface TransactionManagerLookup { /** * Returns a new TransactionManager. * * @throws Exception if lookup failed */ TransactionManager getTransactionManager() throws Exception; } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/transaction/DummyUserTransaction.java0000644000175000017500000001160210545541242031733 0ustar twernertwernerpackage org.jboss.cache.transaction; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import javax.transaction.HeuristicMixedException; import javax.transaction.HeuristicRollbackException; import javax.transaction.NotSupportedException; import javax.transaction.RollbackException; import javax.transaction.Status; import javax.transaction.Synchronization; import javax.transaction.SystemException; import javax.transaction.UserTransaction; import java.util.ArrayList; import java.util.List; /** * @author bela * @version $Revision: 3254 $ * Date: May 15, 2003 * Time: 4:20:17 PM */ public class DummyUserTransaction implements UserTransaction, java.io.Serializable { int status = Status.STATUS_UNKNOWN; static final Log logger_ = LogFactory.getLog(DummyUserTransaction.class); DummyTransactionManager tm_; private static final long serialVersionUID = -6568400755677046127L; /** * List */ List l = new ArrayList(); public DummyUserTransaction(DummyTransactionManager tm) { tm_ = tm; } /** * Starts a new transaction, and associate it with the calling thread. * * @throws NotSupportedException If the calling thread is already * associated with a transaction, and nested transactions are * not supported. * @throws SystemException If the transaction service fails in an * unexpected way. */ public void begin() throws NotSupportedException, SystemException { tm_.begin(); status = Status.STATUS_ACTIVE; } /** * Attempt to commit this transaction. * * @throws RollbackException If the transaction was marked for rollback * only, the transaction is rolled back and this exception is * thrown. * @throws SystemException If the transaction service fails in an * unexpected way. * @throws HeuristicMixedException If a heuristic decision was made and * some some parts of the transaction have been committed while * other parts have been rolled back. * @throws HeuristicRollbackException If a heuristic decision to roll * back the transaction was made. * @throws SecurityException If the caller is not allowed to commit this * transaction. */ public void commit() throws RollbackException, HeuristicMixedException, HeuristicRollbackException, SecurityException, SystemException { tm_.commit(); status = Status.STATUS_COMMITTED; } /** * Rolls back this transaction. * * @throws IllegalStateException If the transaction is in a state * where it cannot be rolled back. This could be because the * transaction is no longer active, or because it is in the * {@link Status#STATUS_PREPARED prepared state}. * @throws SystemException If the transaction service fails in an * unexpected way. */ public void rollback() throws IllegalStateException, SystemException { tm_.rollback(); status = Status.STATUS_ROLLEDBACK; } /** * Mark the transaction so that the only possible outcome is a rollback. * * @throws IllegalStateException If the transaction is not in an active * state. * @throws SystemException If the transaction service fails in an * unexpected way. */ public void setRollbackOnly() throws IllegalStateException, SystemException { tm_.setRollbackOnly(); } /** * Get the status of the transaction. * * @return The status of the transaction. This is one of the * {@link Status} constants. * @throws SystemException If the transaction service fails in an * unexpected way. */ public int getStatus() throws SystemException { return tm_.getStatus(); } /** * Change the transaction timeout for transactions started by the calling * thread with the {@link #begin()} method. * * @param seconds The new timeout value, in seconds. If this parameter * is 0, the timeout value is reset to the default * value. * @throws SystemException If the transaction service fails in an * unexpected way. */ public void setTransactionTimeout(int seconds) throws SystemException { throw new SystemException("not supported"); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/transaction/TransactionEntry.java0000644000175000017500000002602411031017265031100 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.transaction; import net.jcip.annotations.ThreadSafe; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.Fqn; import org.jboss.cache.Modification; import org.jboss.cache.commands.ReversibleCommand; import org.jboss.cache.config.Option; import org.jboss.cache.interceptors.OrderedSynchronizationHandler; import org.jboss.cache.lock.IdentityLock; import org.jboss.cache.lock.NodeLock; import javax.transaction.RollbackException; import javax.transaction.SystemException; import javax.transaction.Transaction; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; /** * Information associated with a {@link GlobalTransaction} about the transaction state. *

        * A TransactionEntry maintains: *

          *
        • Handle to local Transactions: there can be more than 1 local TX associated with a GlobalTransaction *
        • List of modifications ({@link Modification}) *
        • List of nodes that were created as part of lock acquisition. These nodes can be * safely deleted when a transaction is rolled back *
        • List of locks ({@link IdentityLock}) that have been acquired by * this transaction so far *
        * * @author Bela Ban Apr 14, 2003 * @version $Revision: 6072 $ */ @ThreadSafe public class TransactionEntry { private static final Log log = LogFactory.getLog(TransactionEntry.class); private static final boolean trace = log.isTraceEnabled(); /** * Local transaction */ private Transaction ltx = null; private Option option; private OrderedSynchronizationHandler orderedSynchronizationHandler; private boolean forceAsyncReplication = false; private boolean forceSyncReplication = false; /** * List<ReversibleCommand> of modifications ({@link ReversibleCommand}). They will be replicated on TX commit */ private List modificationList; /** * A list of modifications that have been encountered with a LOCAL mode option. These will be removed from the modification list during replication. */ private List localModifications; /** * LinkedHashSet of locks acquired by the transaction. We use * a LinkedHashSet because we need efficient Set semantics (same lock can * be added multiple times) but also need guaranteed ordering for use * by lock release code (see JBCCACHE-874). */ private final LinkedHashSet locks = new LinkedHashSet(); /** * A list of dummy uninitialised nodes created by the cache loader interceptor to load data for a * given node in this tx. */ private List dummyNodesCreatedByCacheLoader; /** * List of nodes that have been removed by the transaction */ private final List removedNodes = new LinkedList(); public TransactionEntry(Transaction tx) throws SystemException, RollbackException { ltx = tx; orderedSynchronizationHandler = new OrderedSynchronizationHandler(tx); } /** * Adds a modification to the modification list. */ public void addModification(ReversibleCommand command) { if (command == null) return; if (modificationList == null) modificationList = new LinkedList(); modificationList.add(command); } /** * Returns all modifications. */ public List getModifications() { if (modificationList == null) return Collections.emptyList(); return modificationList; } /** * Adds a modification to the local modification list. */ public void addLocalModification(ReversibleCommand command) { if (command == null) return; if (localModifications == null) localModifications = new LinkedList(); localModifications.add(command); } /** * Returns all modifications that have been invoked with the LOCAL cache mode option. These will also be in the standard modification list. */ public List getLocalModifications() { if (localModifications == null) return Collections.emptyList(); return localModifications; } /** * Adds the node that has been removed. * * @param fqn */ public void addRemovedNode(Fqn fqn) { removedNodes.add(fqn); } /** * Gets the list of removed nodes. */ public List getRemovedNodes() { return new ArrayList(removedNodes); } /** * Sets the local transaction for this entry. */ public void setTransaction(Transaction tx) { ltx = tx; } /** * Returns a local transaction associated with this TransactionEntry */ public Transaction getTransaction() { return ltx; } /** * Adds a lock to the end of the lock list, if it isn't already present. */ public void addLock(NodeLock l) { if (l != null) { synchronized (locks) { locks.add(l); } } } /** * Add multiple locks to the lock list. * * @param newLocks Collection */ public void addLocks(Collection newLocks) { if (newLocks != null) { synchronized (locks) { locks.addAll(newLocks); } } } /** * Returns the locks in use. * * @return a defensive copy of the internal data structure. */ public List getLocks() { synchronized (locks) { return new ArrayList(locks); } } /** * Gets the value of the forceAsyncReplication flag. Used by ReplicationInterceptor and OptimisticReplicationInterceptor * when dealing with {@link org.jboss.cache.Cache#putForExternalRead(org.jboss.cache.Fqn,Object,Object)} within * a transactional context. * * @return true if the forceAsyncReplication flag is set to true. */ public boolean isForceAsyncReplication() { return forceAsyncReplication; } /** * Sets the value of the forceAsyncReplication flag. Used by ReplicationInterceptor and OptimisticReplicationInterceptor * when dealing with {@link org.jboss.cache.Cache#putForExternalRead(org.jboss.cache.Fqn,Object,Object)} within * a transactional context. Also used by OptimisticReplicationInterceptor when dealing * with {@link org.jboss.cache.config.Option#setForceAsynchronous(boolean)} in a * non-transactional context (i.e. with an implicit transaction). * * @param forceAsyncReplication value of forceAsyncReplication */ public void setForceAsyncReplication(boolean forceAsyncReplication) { this.forceAsyncReplication = forceAsyncReplication; if (forceAsyncReplication) { forceSyncReplication = false; } } /** * Gets the value of the forceSyncReplication flag. Used by ReplicationInterceptor and OptimisticReplicationInterceptor * when dealing with {@link org.jboss.cache.Cache#putForExternalRead(org.jboss.cache.Fqn,Object,Object)} within * a transactional context. * * @return true if the forceAsyncReplication flag is set to true. */ public boolean isForceSyncReplication() { return forceSyncReplication; } /** * Sets the value of the forceSyncReplication flag. Used by ReplicationInterceptor and OptimisticReplicationInterceptor * when dealing with {@link org.jboss.cache.Cache#putForExternalRead(org.jboss.cache.Fqn,Object,Object)} within * a transactional context. * * @param forceSyncReplication value of forceSyncReplication */ public void setForceSyncReplication(boolean forceSyncReplication) { this.forceSyncReplication = forceSyncReplication; if (forceSyncReplication) { forceAsyncReplication = false; } } /** * Posts all undo operations to the CacheImpl. */ public void undoOperations() { if (modificationList == null) { if (trace) log.trace("Modification list is null, no modifications in this transaction!"); return; } if (trace) log.trace("undoOperations " + modificationList); ArrayList copy; // synchronized (modificationList) // { // no need to sync? Only one thread would access a transaction at any given time? copy = new ArrayList(modificationList); // } for (ListIterator i = copy.listIterator(copy.size()); i.hasPrevious();) { Object undoOp = i.previous(); ReversibleCommand txCommand = (ReversibleCommand) undoOp; if (log.isDebugEnabled()) log.debug("Calling rollback() on command " + undoOp); txCommand.rollback(); } } /** * Returns debug information about this transaction. */ @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("TransactionEntry\nmodificationList: ").append(modificationList); synchronized (locks) { sb.append("\nlocks: ").append(locks); } return sb.toString(); } public void loadUninitialisedNode(Fqn fqn) { if (dummyNodesCreatedByCacheLoader == null) dummyNodesCreatedByCacheLoader = new LinkedList(); dummyNodesCreatedByCacheLoader.add(fqn); } public List getDummyNodesCreatedByCacheLoader() { return dummyNodesCreatedByCacheLoader; } /** * Sets a transaction-scope option override * * @param o */ public void setOption(Option o) { this.option = o; } /** * Retrieves a transaction scope option override */ public Option getOption() { return this.option; } public OrderedSynchronizationHandler getOrderedSynchronizationHandler() { return orderedSynchronizationHandler; } public void setOrderedSynchronizationHandler(OrderedSynchronizationHandler orderedSynchronizationHandler) { this.orderedSynchronizationHandler = orderedSynchronizationHandler; } /** * Returns true if modifications were registered to either modificationList or to class loader modifications list. */ public boolean hasModifications() { return modificationList != null && !modificationList.isEmpty(); } /** * @return true if any modifications have been invoked with cache mode being LOCAL. */ public boolean hasLocalModifications() { return localModifications != null && !localModifications.isEmpty(); } /** * Cleans up internal state */ public void reset() { orderedSynchronizationHandler = null; if (modificationList != null) modificationList = null; if (localModifications != null) localModifications = null; option = null; locks.clear(); if (dummyNodesCreatedByCacheLoader != null) dummyNodesCreatedByCacheLoader.clear(); removedNodes.clear(); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/transaction/DummyContext.java0000644000175000017500000005260511032145203030230 0ustar twernertwernerpackage org.jboss.cache.transaction; import javax.naming.Binding; import javax.naming.Context; import javax.naming.Name; import javax.naming.NameClassPair; import javax.naming.NameParser; import javax.naming.NamingEnumeration; import javax.naming.NamingException; import java.util.HashMap; import java.util.Hashtable; /** * @author bela * Date: May 15, 2003 * Time: 6:21:37 PM */ public class DummyContext implements Context { HashMap bindings = new HashMap(); /** * Retrieves the named object. * If name is empty, returns a new instance of this context * (which represents the same naming context as this context, but its * environment may be modified independently and it may be accessed * concurrently). * * @param name the name of the object to look up * @return the object bound to name * @throws NamingException if a naming exception is encountered * @see #lookup(String) * @see #lookupLink(Name) */ public Object lookup(Name name) throws NamingException { return null; } /** * Retrieves the named object. * See {@link #lookup(Name)} for details. * * @param name the name of the object to look up * @return the object bound to name * @throws NamingException if a naming exception is encountered */ public Object lookup(String name) throws NamingException { return bindings.get(name); } /** * Binds a name to an object. * All intermediate contexts and the target context (that named by all * but terminal atomic component of the name) must already exist. * * @param name the name to bind; may not be empty * @param obj the object to bind; possibly null * @throws NameAlreadyBoundException if name is already bound * @throws InvalidAttributesException if object did not supply all mandatory attributes * @throws NamingException if a naming exception is encountered * @see #bind(String,Object) * @see #rebind(Name,Object) * @see DirContext#bind(Name,Object, * Attributes) */ public void bind(Name name, Object obj) throws NamingException { } /** * Binds a name to an object. * See {@link #bind(Name,Object)} for details. * * @param name the name to bind; may not be empty * @param obj the object to bind; possibly null * @throws NameAlreadyBoundException if name is already bound * @throws InvalidAttributesException if object did not supply all mandatory attributes * @throws NamingException if a naming exception is encountered */ public void bind(String name, Object obj) throws NamingException { bindings.put(name, obj); } /** * Binds a name to an object, overwriting any existing binding. * All intermediate contexts and the target context (that named by all * but terminal atomic component of the name) must already exist. *

        *

        If the object is a DirContext, any existing attributes * associated with the name are replaced with those of the object. * Otherwise, any existing attributes associated with the name remain * unchanged. * * @param name the name to bind; may not be empty * @param obj the object to bind; possibly null * @throws InvalidAttributesException if object did not supply all mandatory attributes * @throws NamingException if a naming exception is encountered * @see #rebind(String,Object) * @see #bind(Name,Object) * @see DirContext#rebind(Name,Object, * Attributes) * @see DirContext */ public void rebind(Name name, Object obj) throws NamingException { } /** * Binds a name to an object, overwriting any existing binding. * See {@link #rebind(Name,Object)} for details. * * @param name the name to bind; may not be empty * @param obj the object to bind; possibly null * @throws InvalidAttributesException if object did not supply all mandatory attributes * @throws NamingException if a naming exception is encountered */ public void rebind(String name, Object obj) throws NamingException { bindings.put(name, obj); } /** * Unbinds the named object. * Removes the terminal atomic name in name * from the target context--that named by all but the terminal * atomic part of name. *

        *

        This method is idempotent. * It succeeds even if the terminal atomic name * is not bound in the target context, but throws * NameNotFoundException * if any of the intermediate contexts do not exist. *

        *

        Any attributes associated with the name are removed. * Intermediate contexts are not changed. * * @param name the name to unbind; may not be empty * @throws NameNotFoundException if an intermediate context does not exist * @throws NamingException if a naming exception is encountered * @see #unbind(String) */ public void unbind(Name name) throws NamingException { } /** * Unbinds the named object. * See {@link #unbind(Name)} for details. * * @param name the name to unbind; may not be empty * @throws NameNotFoundException if an intermediate context does not exist * @throws NamingException if a naming exception is encountered */ public void unbind(String name) throws NamingException { bindings.remove(name); } /** * Binds a new name to the object bound to an old name, and unbinds * the old name. Both names are relative to this context. * Any attributes associated with the old name become associated * with the new name. * Intermediate contexts of the old name are not changed. * * @param oldName the name of the existing binding; may not be empty * @param newName the name of the new binding; may not be empty * @throws NameAlreadyBoundException if newName is already bound * @throws NamingException if a naming exception is encountered * @see #rename(String,String) * @see #bind(Name,Object) * @see #rebind(Name,Object) */ public void rename(Name oldName, Name newName) throws NamingException { } /** * Binds a new name to the object bound to an old name, and unbinds * the old name. * See {@link #rename(Name,Name)} for details. * * @param oldName the name of the existing binding; may not be empty * @param newName the name of the new binding; may not be empty * @throws NameAlreadyBoundException if newName is already bound * @throws NamingException if a naming exception is encountered */ public void rename(String oldName, String newName) throws NamingException { } /** * Enumerates the names bound in the named context, along with the * class names of objects bound to them. * The contents of any subcontexts are not included. *

        *

        If a binding is added to or removed from this context, * its effect on an enumeration previously returned is undefined. * * @param name the name of the context to list * @return an enumeration of the names and class names of the * bindings in this context. Each element of the * enumeration is of type NameClassPair. * @throws NamingException if a naming exception is encountered * @see #list(String) * @see #listBindings(Name) * @see NameClassPair */ public NamingEnumeration list(Name name) throws NamingException { return null; } /** * Enumerates the names bound in the named context, along with the * class names of objects bound to them. * See {@link #list(Name)} for details. * * @param name the name of the context to list * @return an enumeration of the names and class names of the * bindings in this context. Each element of the * enumeration is of type NameClassPair. * @throws NamingException if a naming exception is encountered */ public NamingEnumeration list(String name) throws NamingException { return null; } /** * Enumerates the names bound in the named context, along with the * objects bound to them. * The contents of any subcontexts are not included. *

        *

        If a binding is added to or removed from this context, * its effect on an enumeration previously returned is undefined. * * @param name the name of the context to list * @return an enumeration of the bindings in this context. * Each element of the enumeration is of type * Binding. * @throws NamingException if a naming exception is encountered * @see #listBindings(String) * @see #list(Name) * @see Binding */ public NamingEnumeration listBindings(Name name) throws NamingException { return null; } /** * Enumerates the names bound in the named context, along with the * objects bound to them. * See {@link #listBindings(Name)} for details. * * @param name the name of the context to list * @return an enumeration of the bindings in this context. * Each element of the enumeration is of type * Binding. * @throws NamingException if a naming exception is encountered */ public NamingEnumeration listBindings(String name) throws NamingException { return null; } /** * Destroys the named context and removes it from the namespace. * Any attributes associated with the name are also removed. * Intermediate contexts are not destroyed. *

        *

        This method is idempotent. * It succeeds even if the terminal atomic name * is not bound in the target context, but throws * NameNotFoundException * if any of the intermediate contexts do not exist. *

        *

        In a federated naming system, a context from one naming system * may be bound to a name in another. One can subsequently * look up and perform operations on the foreign context using a * composite name. However, an attempt destroy the context using * this composite name will fail with * NotContextException, because the foreign context is not * a "subcontext" of the context in which it is bound. * Instead, use unbind() to remove the * binding of the foreign context. Destroying the foreign context * requires that the destroySubcontext() be performed * on a context from the foreign context's "native" naming system. * * @param name the name of the context to be destroyed; may not be empty * @throws NameNotFoundException if an intermediate context does not exist * @throws NotContextException if the name is bound but does not name a * context, or does not name a context of the appropriate type * @throws ContextNotEmptyException if the named context is not empty * @throws NamingException if a naming exception is encountered * @see #destroySubcontext(String) */ public void destroySubcontext(Name name) throws NamingException { } /** * Destroys the named context and removes it from the namespace. * See {@link #destroySubcontext(Name)} for details. * * @param name the name of the context to be destroyed; may not be empty * @throws NameNotFoundException if an intermediate context does not exist * @throws NotContextException if the name is bound but does not name a * context, or does not name a context of the appropriate type * @throws ContextNotEmptyException if the named context is not empty * @throws NamingException if a naming exception is encountered */ public void destroySubcontext(String name) throws NamingException { } /** * Creates and binds a new context. * Creates a new context with the given name and binds it in * the target context (that named by all but terminal atomic * component of the name). All intermediate contexts and the * target context must already exist. * * @param name the name of the context to create; may not be empty * @return the newly created context * @throws NameAlreadyBoundException if name is already bound * @throws InvalidAttributesException if creation of the subcontext requires specification of * mandatory attributes * @throws NamingException if a naming exception is encountered * @see #createSubcontext(String) * @see DirContext#createSubcontext */ public Context createSubcontext(Name name) throws NamingException { return null; } /** * Creates and binds a new context. * See {@link #createSubcontext(Name)} for details. * * @param name the name of the context to create; may not be empty * @return the newly created context * @throws NameAlreadyBoundException if name is already bound * @throws InvalidAttributesException if creation of the subcontext requires specification of * mandatory attributes * @throws NamingException if a naming exception is encountered */ public Context createSubcontext(String name) throws NamingException { return null; } /** * Retrieves the named object, following links except * for the terminal atomic component of the name. * If the object bound to name is not a link, * returns the object itself. * * @param name the name of the object to look up * @return the object bound to name, not following the * terminal link (if any). * @throws NamingException if a naming exception is encountered * @see #lookupLink(String) */ public Object lookupLink(Name name) throws NamingException { return null; } /** * Retrieves the named object, following links except * for the terminal atomic component of the name. * See {@link #lookupLink(Name)} for details. * * @param name the name of the object to look up * @return the object bound to name, not following the * terminal link (if any) * @throws NamingException if a naming exception is encountered */ public Object lookupLink(String name) throws NamingException { return null; } /** * Retrieves the parser associated with the named context. * In a federation of namespaces, different naming systems will * parse names differently. This method allows an application * to get a parser for parsing names into their atomic components * using the naming convention of a particular naming system. * Within any single naming system, NameParser objects * returned by this method must be equal (using the equals() * test). * * @param name the name of the context from which to get the parser * @return a name parser that can parse compound names into their atomic * components * @throws NamingException if a naming exception is encountered * @see #getNameParser(String) * @see CompoundName */ public NameParser getNameParser(Name name) throws NamingException { return null; } /** * Retrieves the parser associated with the named context. * See {@link #getNameParser(Name)} for details. * * @param name the name of the context from which to get the parser * @return a name parser that can parse compound names into their atomic * components * @throws NamingException if a naming exception is encountered */ public NameParser getNameParser(String name) throws NamingException { return null; } /** * Composes the name of this context with a name relative to * this context. * Given a name (name) relative to this context, and * the name (prefix) of this context relative to one * of its ancestors, this method returns the composition of the * two names using the syntax appropriate for the naming * system(s) involved. That is, if name names an * object relative to this context, the result is the name of the * same object, but relative to the ancestor context. None of the * names may be null. *

        * For example, if this context is named "wiz.com" relative * to the initial context, then *

            * 	composeName("east", "wiz.com")	
        * might return "east.wiz.com". * If instead this context is named "org/research", then *
            * 	composeName("user/jane", "org/research")	
        * might return "org/research/user/jane" while *
            * 	composeName("user/jane", "research")	
        * returns "research/user/jane". * * @param name a name relative to this context * @param prefix the name of this context relative to one of its ancestors * @return the composition of prefix and name * @throws NamingException if a naming exception is encountered * @see #composeName(String,String) */ public Name composeName(Name name, Name prefix) throws NamingException { return null; } /** * Composes the name of this context with a name relative to * this context. * See {@link #composeName(Name,Name)} for details. * * @param name a name relative to this context * @param prefix the name of this context relative to one of its ancestors * @return the composition of prefix and name * @throws NamingException if a naming exception is encountered */ public String composeName(String name, String prefix) throws NamingException { return null; } /** * Adds a new environment property to the environment of this * context. If the property already exists, its value is overwritten. * See class description for more details on environment properties. * * @param propName the name of the environment property to add; may not be null * @param propVal the value of the property to add; may not be null * @return the previous value of the property, or null if the property was * not in the environment before * @throws NamingException if a naming exception is encountered * @see #getEnvironment() * @see #removeFromEnvironment(String) */ public Object addToEnvironment(String propName, Object propVal) throws NamingException { return null; } /** * Removes an environment property from the environment of this * context. See class description for more details on environment * properties. * * @param propName the name of the environment property to remove; may not be null * @return the previous value of the property, or null if the property was * not in the environment * @throws NamingException if a naming exception is encountered * @see #getEnvironment() * @see #addToEnvironment(String,Object) */ public Object removeFromEnvironment(String propName) throws NamingException { return null; } /** * Retrieves the environment in effect for this context. * See class description for more details on environment properties. *

        *

        The caller should not make any changes to the object returned: * their effect on the context is undefined. * The environment of this context may be changed using * addToEnvironment() and removeFromEnvironment(). * * @return the environment of this context; never null * @throws NamingException if a naming exception is encountered * @see #addToEnvironment(String,Object) * @see #removeFromEnvironment(String) */ public Hashtable getEnvironment() throws NamingException { return null; } /** * Closes this context. * This method releases this context's resources immediately, instead of * waiting for them to be released automatically by the garbage collector. *

        *

        This method is idempotent: invoking it on a context that has * already been closed has no effect. Invoking any other method * on a closed context is not allowed, and results in undefined behaviour. * * @throws NamingException if a naming exception is encountered */ public void close() throws NamingException { } /** * Retrieves the full name of this context within its own namespace. *

        *

        Many naming services have a notion of a "full name" for objects * in their respective namespaces. For example, an LDAP entry has * a distinguished name, and a DNS record has a fully qualified name. * This method allows the client application to retrieve this name. * The string returned by this method is not a JNDI composite name * and should not be passed directly to context methods. * In naming systems for which the notion of full name does not * make sense, OperationNotSupportedException is thrown. * * @return this context's name in its own namespace; never null * @throws OperationNotSupportedException if the naming system does * not have the notion of a full name * @throws NamingException if a naming exception is encountered * @since 1.3 */ public String getNameInNamespace() throws NamingException { return null; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/transaction/DummyBaseTransactionManager.java0000755000175000017500000002156210545541242033173 0ustar twernertwernerpackage org.jboss.cache.transaction; import javax.transaction.HeuristicMixedException; import javax.transaction.HeuristicRollbackException; import javax.transaction.InvalidTransactionException; import javax.transaction.NotSupportedException; import javax.transaction.RollbackException; import javax.transaction.Status; import javax.transaction.SystemException; import javax.transaction.Transaction; import javax.transaction.TransactionManager; /** * @author bela * @version $Revision: 3254 $ * Date: May 15, 2003 * Time: 4:11:37 PM */ public class DummyBaseTransactionManager implements TransactionManager, java.io.Serializable { static ThreadLocal thread_local = new ThreadLocal(); private static final long serialVersionUID = -6716097342564237376l; /** * Starts a new transaction, and associate it with the calling thread. * * @throws javax.transaction.NotSupportedException * If the calling thread is already * associated with a transaction, and nested transactions are * not supported. * @throws javax.transaction.SystemException * If the transaction service fails in an * unexpected way. */ public void begin() throws NotSupportedException, SystemException { Transaction currentTx; if ((currentTx = getTransaction()) != null) throw new NotSupportedException(Thread.currentThread() + " is already associated with a transaction (" + currentTx + ")"); DummyTransaction tx = new DummyTransaction(this); setTransaction(tx); } /** * Commit the transaction associated with the calling thread. * * @throws javax.transaction.RollbackException * If the transaction was marked for rollback * only, the transaction is rolled back and this exception is * thrown. * @throws IllegalStateException If the calling thread is not associated * with a transaction. * @throws javax.transaction.SystemException * If the transaction service fails in an * unexpected way. * @throws javax.transaction.HeuristicMixedException * If a heuristic decision was made and * some some parts of the transaction have been committed while * other parts have been rolled back. * @throws javax.transaction.HeuristicRollbackException * If a heuristic decision to roll * back the transaction was made. * @throws SecurityException If the caller is not allowed to commit this * transaction. */ public void commit() throws RollbackException, HeuristicMixedException, HeuristicRollbackException, SecurityException, IllegalStateException, SystemException { int status; Transaction tx = getTransaction(); if (tx == null) throw new IllegalStateException("thread not associated with transaction"); status = tx.getStatus(); if (status == Status.STATUS_MARKED_ROLLBACK) throw new RollbackException(); tx.commit(); // Disassociate tx from thread. setTransaction(null); } /** * Rolls back the transaction associated with the calling thread. * * @throws IllegalStateException If the transaction is in a state * where it cannot be rolled back. This could be because the * calling thread is not associated with a transaction, or * because it is in the * {@link javax.transaction.Status#STATUS_PREPARED prepared state}. * @throws SecurityException If the caller is not allowed to roll back * this transaction. * @throws javax.transaction.SystemException * If the transaction service fails in an * unexpected way. */ public void rollback() throws IllegalStateException, SecurityException, SystemException { Transaction tx = getTransaction(); if (tx == null) throw new IllegalStateException("no transaction associated with thread"); tx.rollback(); // Disassociate tx from thread. setTransaction(null); } /** * Mark the transaction associated with the calling thread for rollback * only. * * @throws IllegalStateException If the transaction is in a state * where it cannot be rolled back. This could be because the * calling thread is not associated with a transaction, or * because it is in the * {@link javax.transaction.Status#STATUS_PREPARED prepared state}. * @throws javax.transaction.SystemException * If the transaction service fails in an * unexpected way. */ public void setRollbackOnly() throws IllegalStateException, SystemException { Transaction tx = getTransaction(); if (tx == null) throw new IllegalStateException("no transaction associated with calling thread"); tx.setRollbackOnly(); } /** * Get the status of the transaction associated with the calling thread. * * @return The status of the transaction. This is one of the * {@link javax.transaction.Status} constants. If no transaction is associated * with the calling thread, * {@link javax.transaction.Status#STATUS_NO_TRANSACTION} is returned. * @throws javax.transaction.SystemException * If the transaction service fails in an * unexpected way. */ public int getStatus() throws SystemException { Transaction tx = getTransaction(); return tx != null ? tx.getStatus() : Status.STATUS_NO_TRANSACTION; } /** * Get the transaction associated with the calling thread. * * @return The transaction associated with the calling thread, or * null if the calling thread is not associated * with a transaction. * @throws javax.transaction.SystemException * If the transaction service fails in an * unexpected way. */ public Transaction getTransaction() throws SystemException { return thread_local.get(); } /** * Change the transaction timeout for transactions started by the calling * thread with the {@link #begin()} method. * * @param seconds The new timeout value, in seconds. If this parameter * is 0, the timeout value is reset to the default * value. * @throws javax.transaction.SystemException * If the transaction service fails in an * unexpected way. */ public void setTransactionTimeout(int seconds) throws SystemException { throw new SystemException("not supported"); } /** * Suspend the association the calling thread has to a transaction, * and return the suspended transaction. * When returning from this method, the calling thread is no longer * associated with a transaction. * * @return The transaction that the calling thread was associated with, * or null if the calling thread was not associated * with a transaction. * @throws javax.transaction.SystemException * If the transaction service fails in an * unexpected way. */ public Transaction suspend() throws SystemException { Transaction retval = getTransaction(); setTransaction(null); return retval; } /** * Resume the association of the calling thread with the given * transaction. * * @param tx The transaction to be associated with the calling thread. * @throws javax.transaction.InvalidTransactionException * If the argument does not represent * a valid transaction. * @throws IllegalStateException If the calling thread is already * associated with a transaction. * @throws javax.transaction.SystemException * If the transaction service fails in an * unexpected way. */ public void resume(Transaction tx) throws InvalidTransactionException, IllegalStateException, SystemException { setTransaction(tx); } /** * Just used for unit tests * * @param tx */ public void setTransaction(Transaction tx) { thread_local.set(tx); } } ././@LongLink0000000000000000000000000000015200000000000011563 Lustar rootrootjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/transaction/BatchModeTransactionManagerLookup.javajbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/transaction/BatchModeTransactionManagerLookup0000644000175000017500000000077010562447404033404 0ustar twernertwernerpackage org.jboss.cache.transaction; import javax.transaction.TransactionManager; /** * Returns an instance of {@link BatchModeTransactionManager}. * * @author Bela Ban Sept 5 2003 * @version $Id: BatchModeTransactionManagerLookup.java 3550 2007-02-07 22:13:24Z genman $ */ public class BatchModeTransactionManagerLookup implements TransactionManagerLookup { public TransactionManager getTransactionManager() throws Exception { return BatchModeTransactionManager.getInstance(); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/transaction/GlobalTransaction.java0000644000175000017500000000612111031017265031173 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.transaction; import org.jgroups.Address; import java.io.Externalizable; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; /** * Uniquely identifies a transaction that spans all nodes in a cluster. This is used when * replicating all modifications in a transaction; the PREPARE and COMMIT (or ROLLBACK) * messages have to have a unique identifier to associate the changes with
        * * @author Bela Ban Apr 12, 2003 * @author Manik Surtani (manik@jboss.org) * @version $Revision: 6072 $ */ public class GlobalTransaction implements Externalizable { private static final long serialVersionUID = 8011434781266976149L; private static long sid = 0; private Address addr = null; private long id = -1; private transient boolean remote = false; // cache the hashcode private transient int hash_code = -1; // in the worst case, hashCode() returns 0, then increases, so we're safe here /** * empty ctor used by externalization */ public GlobalTransaction() { } private GlobalTransaction(Address addr) { this.addr = addr; id = newId(); } private static synchronized long newId() { return ++sid; } public static GlobalTransaction create(Address addr) { return new GlobalTransaction(addr); } public Object getAddress() { return addr; } public void setAddress(Address address) { addr = address; } public long getId() { return id; } @Override public int hashCode() { if (hash_code == -1) { hash_code = (addr != null ? addr.hashCode() : 0) + (int) id; } return hash_code; } @Override public boolean equals(Object other) { if (this == other) return true; if (!(other instanceof GlobalTransaction)) return false; GlobalTransaction otherGtx = (GlobalTransaction) other; boolean aeq = (addr == null) ? (otherGtx.addr == null) : addr.equals(otherGtx.addr); return aeq && (id == otherGtx.id); } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("GlobalTransaction:<").append(addr).append(">:").append(id); return sb.toString(); } public void writeExternal(ObjectOutput out) throws IOException { out.writeObject(addr); out.writeLong(id); // out.writeInt(hash_code); } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { addr = (Address) in.readObject(); id = in.readLong(); hash_code = -1; } /** * @return Returns the remote. */ public boolean isRemote() { return remote; } /** * @param remote The remote to set. */ public void setRemote(boolean remote) { this.remote = remote; } public void setId(long id) { this.id = id; } } ././@LongLink0000000000000000000000000000014600000000000011566 Lustar rootrootjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/transaction/DummyTransactionManagerLookup.javajbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/transaction/DummyTransactionManagerLookup.jav0000644000175000017500000000134410700470066033420 0ustar twernertwernerpackage org.jboss.cache.transaction; import javax.transaction.TransactionManager; import javax.transaction.UserTransaction; /** * Returns an instance of {@link DummyTransactionManager}. * * @author Bela Ban Sept 5 2003 * @version $Id: DummyTransactionManagerLookup.java 4526 2007-10-02 16:22:14Z jason.greene@jboss.com $ */ public class DummyTransactionManagerLookup implements TransactionManagerLookup { public TransactionManager getTransactionManager() throws Exception { return DummyTransactionManager.getInstance(); } public UserTransaction getUserTransaction() { return DummyTransactionManager.getUserTransaction(); } public void cleanup() { DummyTransactionManager.destroy(); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/transaction/DummyTransactionManager.java0000644000175000017500000000416310727544301032374 0ustar twernertwernerpackage org.jboss.cache.transaction; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.NamingException; import java.util.Properties; /** * Simple transaction manager implementation that maintains transaction state * in memory only. * * @author bela * @version $Revision: 4834 $ * Date: May 15, 2003 * Time: 4:11:37 PM */ public class DummyTransactionManager extends DummyBaseTransactionManager { static DummyTransactionManager instance = null; static DummyUserTransaction utx = null; static Log log = LogFactory.getLog(DummyTransactionManager.class); private static final long serialVersionUID = 4396695354693176535L; public static DummyTransactionManager getInstance() { if (instance == null) { instance = new DummyTransactionManager(); utx = new DummyUserTransaction(instance); try { Properties p = new Properties(); p.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.cache.transaction.DummyContextFactory"); Context ctx = new InitialContext(p); ctx.bind("java:/TransactionManager", instance); ctx.bind("UserTransaction", utx); } catch (NamingException e) { log.error("binding of DummyTransactionManager failed", e); } } return instance; } public static DummyUserTransaction getUserTransaction() { getInstance(); return utx; } public static void destroy() { if (instance == null) return; try { Properties p = new Properties(); p.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.cache.transaction.DummyContextFactory"); Context ctx = new InitialContext(p); ctx.unbind("java:/TransactionManager"); ctx.unbind("UserTransaction"); } catch (NamingException e) { log.error("unbinding of DummyTransactionManager failed", e); } instance.setTransaction(null); instance = null; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/transaction/BatchModeTransactionManager.java0000755000175000017500000000205110727544301033124 0ustar twernertwernerpackage org.jboss.cache.transaction; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * Not really a transaction manager in the truest sense of the word. Only used to batch up operations. Proper * transactional symantics of rollbacks and recovery are NOT used here. This is used by PojoCache. * * @author bela * @version $Revision: 4834 $ * Date: May 15, 2003 * Time: 4:11:37 PM */ public class BatchModeTransactionManager extends DummyBaseTransactionManager { static BatchModeTransactionManager instance = null; static Log log = LogFactory.getLog(BatchModeTransactionManager.class); private static final long serialVersionUID = 5656602677430350961L; public static BatchModeTransactionManager getInstance() { if (instance == null) { instance = new BatchModeTransactionManager(); } return instance; } public static void destroy() { if (instance == null) return; instance.setTransaction(null); instance = null; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/transaction/DummyTransaction.java0000644000175000017500000002477711075447240031117 0ustar twernertwernerpackage org.jboss.cache.transaction; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import javax.transaction.HeuristicMixedException; import javax.transaction.HeuristicRollbackException; import javax.transaction.RollbackException; import javax.transaction.Status; import javax.transaction.Synchronization; import javax.transaction.SystemException; import javax.transaction.Transaction; import javax.transaction.xa.XAResource; import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; /** * @author bela * @version $Revision: 6962 $ * Date: May 15, 2003 * Time: 4:20:17 PM */ public class DummyTransaction implements Transaction { private int status = Status.STATUS_UNKNOWN; private static final Log log = LogFactory.getLog(DummyTransaction.class); DummyBaseTransactionManager tm_; protected final Set participants = new CopyOnWriteArraySet(); private boolean trace; public DummyTransaction(DummyBaseTransactionManager tm) { tm_ = tm; status = Status.STATUS_ACTIVE; trace = log.isTraceEnabled(); } /** * Attempt to commit this transaction. * * @throws RollbackException If the transaction was marked for rollback * only, the transaction is rolled back and this exception is * thrown. * @throws SystemException If the transaction service fails in an * unexpected way. * @throws HeuristicMixedException If a heuristic decision was made and * some some parts of the transaction have been committed while * other parts have been rolled back. * @throws HeuristicRollbackException If a heuristic decision to roll * back the transaction was made. * @throws SecurityException If the caller is not allowed to commit this * transaction. */ public void commit() throws RollbackException, HeuristicMixedException, HeuristicRollbackException, SecurityException, SystemException { boolean doCommit; status = Status.STATUS_PREPARING; try { boolean outcome = notifyBeforeCompletion(); // status=Status.STATUS_PREPARED; if (outcome && status != Status.STATUS_MARKED_ROLLBACK) { status = Status.STATUS_COMMITTING; doCommit = true; } else { status = Status.STATUS_ROLLING_BACK; doCommit = false; } notifyAfterCompletion(doCommit ? Status.STATUS_COMMITTED : Status.STATUS_MARKED_ROLLBACK); status = doCommit ? Status.STATUS_COMMITTED : Status.STATUS_MARKED_ROLLBACK; if (!doCommit) throw new RollbackException("outcome is " + outcome + " status: " + status); } finally { // Disassociate tx from thread. tm_.setTransaction(null); } } /** * Rolls back this transaction. * * @throws IllegalStateException If the transaction is in a state * where it cannot be rolled back. This could be because the * transaction is no longer active, or because it is in the * {@link Status#STATUS_PREPARED prepared state}. * @throws SystemException If the transaction service fails in an * unexpected way. */ public void rollback() throws IllegalStateException, SystemException { try { // JBCACHE-360 -- to match JBossTM (and presumable the spec) a // rollback transaction should have status ROLLEDBACK before // calling afterCompletion(). //status=Status.STATUS_ROLLING_BACK; status = Status.STATUS_ROLLEDBACK; notifyAfterCompletion(Status.STATUS_ROLLEDBACK); } catch (Throwable t) { // do nothing } status = Status.STATUS_ROLLEDBACK; // Disassociate tx from thread. tm_.setTransaction(null); } /** * Mark the transaction so that the only possible outcome is a rollback. * * @throws IllegalStateException If the transaction is not in an active * state. * @throws SystemException If the transaction service fails in an * unexpected way. */ public void setRollbackOnly() throws IllegalStateException, SystemException { status = Status.STATUS_MARKED_ROLLBACK; } /** * Get the status of the transaction. * * @return The status of the transaction. This is one of the * {@link Status} constants. * @throws SystemException If the transaction service fails in an * unexpected way. */ public int getStatus() throws SystemException { return status; } /** * Change the transaction timeout for transactions started by the calling * thread with the {@link DummyTransactionManager#begin()} method. * * @param seconds The new timeout value, in seconds. If this parameter * is 0, the timeout value is reset to the default * value. * @throws SystemException If the transaction service fails in an * unexpected way. */ public void setTransactionTimeout(int seconds) throws SystemException { throw new SystemException("not supported"); } /** * Enlist an XA resource with this transaction. * * @return true if the resource could be enlisted with * this transaction, otherwise false. * @throws RollbackException If the transaction is marked for rollback * only. * @throws IllegalStateException If the transaction is in a state * where resources cannot be enlisted. This could be because the * transaction is no longer active, or because it is in the * {@link Status#STATUS_PREPARED prepared state}. * @throws SystemException If the transaction service fails in an * unexpected way. */ public boolean enlistResource(XAResource xaRes) throws RollbackException, IllegalStateException, SystemException { throw new SystemException("not supported"); } /** * Delist an XA resource from this transaction. * * @return true if the resource could be delisted from * this transaction, otherwise false. * @throws IllegalStateException If the transaction is in a state * where resources cannot be delisted. This could be because the * transaction is no longer active. * @throws SystemException If the transaction service fails in an * unexpected way. */ public boolean delistResource(XAResource xaRes, int flag) throws IllegalStateException, SystemException { throw new SystemException("not supported"); } /** * Register a {@link Synchronization} callback with this transaction. * * @throws RollbackException If the transaction is marked for rollback * only. * @throws IllegalStateException If the transaction is in a state * where {@link Synchronization} callbacks cannot be registered. * This could be because the transaction is no longer active, * or because it is in the * {@link Status#STATUS_PREPARED prepared state}. * @throws SystemException If the transaction service fails in an * unexpected way. */ public void registerSynchronization(Synchronization sync) throws RollbackException, IllegalStateException, SystemException { if (sync == null) throw new IllegalArgumentException("null synchronization " + this); switch (status) { case Status.STATUS_ACTIVE: case Status.STATUS_PREPARING: break; case Status.STATUS_PREPARED: throw new IllegalStateException("already prepared. " + this); case Status.STATUS_COMMITTING: throw new IllegalStateException("already started committing. " + this); case Status.STATUS_COMMITTED: throw new IllegalStateException("already committed. " + this); case Status.STATUS_MARKED_ROLLBACK: throw new RollbackException("already marked for rollback " + this); case Status.STATUS_ROLLING_BACK: throw new RollbackException("already started rolling back. " + this); case Status.STATUS_ROLLEDBACK: throw new RollbackException("already rolled back. " + this); case Status.STATUS_NO_TRANSACTION: throw new IllegalStateException("no transaction. " + this); case Status.STATUS_UNKNOWN: throw new IllegalStateException("unknown state " + this); default: throw new IllegalStateException("illegal status: " + status + " tx=" + this); } if (trace) { log.trace("registering synchronization handler " + sync); } participants.add(sync); } void setStatus(int new_status) { status = new_status; } protected boolean notifyBeforeCompletion() { boolean retval = true; for (Synchronization s : participants) { if (trace) { log.trace("processing beforeCompletion for " + s); } try { s.beforeCompletion(); } catch (Throwable t) { retval = false; log.error("beforeCompletion() failed for " + s, t); } } return retval; } protected void notifyAfterCompletion(int status) { for (Synchronization s : participants) { if (trace) { log.trace("processing afterCompletion for " + s); } try { s.afterCompletion(status); } catch (Throwable t) { log.error("afterCompletion() failed for " + s, t); } } participants.clear(); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/Region.java0000644000175000017500000001447111041076631024475 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.EvictionPolicyConfig; import org.jboss.cache.config.EvictionRegionConfig; import org.jboss.cache.eviction.EvictedEventNode; import org.jboss.cache.eviction.EvictionPolicy; import org.jboss.cache.eviction.LRUPolicy; /** * Defines characteristics such as class loading and eviction of {@link org.jboss.cache.Node}s belonging to a Region in a {@link Cache}. * A Region is described by an {@link #getFqn() Fqn} relative to the root of the cache. * All nodes and child nodes of this Fqn belong to this region. *

        * If a region is to be recognised as an eviction region (region of type {@link Type#EVICTION} then * it must have an {@link org.jboss.cache.config.EvictionPolicyConfig} set using {@link #setEvictionPolicy(org.jboss.cache.config.EvictionPolicyConfig)}. *

        * Similarly, to be recognised as a marshalling region (region of type {@link Type#MARSHALLING} then it must have a * {@link ClassLoader} registered using {@link #registerContextClassLoader(ClassLoader)}. *

        * Note that a single region can be both an eviction and marshalling region at the same time. *

        * * @author Manik Surtani (manik@jboss.org) * @see org.jboss.cache.RegionManager * @since 2.0.0 */ public interface Region extends Comparable, Cloneable { /** * Types of regions. */ static enum Type { EVICTION, MARSHALLING, ANY } /** * Region status */ static enum Status { ACTIVATING, ACTIVE, INACTIVATING, INACTIVE } /** * Registers a specific {@link ClassLoader} for this region, * overridding the default cache class loader. * * @param classLoader specific class loader */ void registerContextClassLoader(ClassLoader classLoader); /** * @return the cache-wide configuration * @since 2.1.0 */ Configuration getCacheConfiguration(); /** * Unregisters a registered {@link ClassLoader}s for this region. */ void unregisterContextClassLoader(); /** * Activates this region for replication. * By default, the entire cache is activated for replication at start-up. * * @throws RegionNotEmptyException if the {@link Fqn} that represents this region already exists and contains data or children. */ void activate() throws RegionNotEmptyException; /** * Activates this region for replication, but if the {@link Fqn} that represents this region already exists and * either contains data or children, no state transfers take place. The region is simply marked as active in this * case. */ void activateIfEmpty(); /** * Deactivates this region from being replicated. */ void deactivate(); /** * Sets this region as active - this only marks a flag * and does not actually activates or * deactivates this region. Use {@link #activate()} * and {@link #deactivate()} for the full process. * * @param b */ void setActive(boolean b); /** * Returns true if this region has been activated. * * @return true if this region has been activated. */ boolean isActive(); /** * Returns the configured {@link ClassLoader} for this region. * * @return a ClassLoader */ ClassLoader getClassLoader(); /** * Configures an eviction policy for this region. * * @param evictionPolicyConfig configuration to set */ void setEvictionPolicy(EvictionPolicyConfig evictionPolicyConfig); /** * Returns an eviction policy configuration. * * @return an eviction policy configuration */ EvictionPolicyConfig getEvictionPolicyConfig(); /** * Returns an eviction policy. * * @return an eviction policy */ EvictionPolicy getEvictionPolicy(); /** * Returns an eviction region configuration for this region. * * @return an eviction region configuration */ EvictionRegionConfig getEvictionRegionConfig(); /** * Clears the node event queue used for processing eviction. * * @see #nodeEventQueueSize() */ void resetEvictionQueues(); /** * Returns the size of the node event queue, used by the eviction thread. * * @return number of events */ int nodeEventQueueSize(); /** * Returns the most recent {@link org.jboss.cache.eviction.EvictedEventNode} added to the event queue by * {@link #putNodeEvent(EvictedEventNode)}. * * @return the last {@link org.jboss.cache.eviction.EvictedEventNode}, or null if no more events exist */ EvictedEventNode takeLastEventNode(); /** * Adds an {@link org.jboss.cache.eviction.EvictedEventNode} to the internal queue for processing * by the eviction thread. * * @param event event to add */ void putNodeEvent(EvictedEventNode event); /** * Marks a {@link org.jboss.cache.Node} as currently in use, by adding an event to the eviction queue. * If there is an {@link EvictionPolicy} associated with this region, and * it respects this event (e.g., {@link LRUPolicy} does), then the {@link org.jboss.cache.Node} will not * be evicted until {@link #unmarkNodeCurrentlyInUse(Fqn)} is invoked. *

        * This mechanism can be used to prevent eviction of data that the application * is currently using, in the absence of any locks on the {@link org.jboss.cache.Node} where the * data is stored. * * @param fqn Fqn of the node. * @see #unmarkNodeCurrentlyInUse(Fqn) */ void markNodeCurrentlyInUse(Fqn fqn, long timeout); /** * Adds an event to the eviction queue indicating that a node is no longer in use. * * @param fqn Fqn of the node. * @see #markNodeCurrentlyInUse(Fqn,long) */ void unmarkNodeCurrentlyInUse(Fqn fqn); /** * Returns the {@link org.jboss.cache.Fqn} of this region. * * @return the Fqn */ Fqn getFqn(); /** * A mechanism to set status of a region, more fine grained control than just setActive(); * * @param status status of the region * @since 2.1.0 */ void setStatus(Status status); /** * @return the region's status */ Status getStatus(); Region clone(Fqn cloneFqn); } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/Node.java0000755000175000017500000002763011012410153024130 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache; import net.jcip.annotations.ThreadSafe; import java.util.Map; import java.util.Set; /** * A Node is a {@link Fqn named} logical grouping of data in the JBoss {@link Cache}. * A node should be used to contain data for a single data record, for example * information about a particular person or account. *

        * One purpose of grouping cache data into separate nodes is to minimize transaction * locking interference, and increase concurrency. So for example, when multiple threads or * possibly distributed caches are acccessing different accounts simultaneously. *

        * Another is that when making changes to this node, its data might be kept in a single * database row or file on disk. (Persisted via the use of a {@link org.jboss.cache.loader.CacheLoader}.) *

        * A node has references to its children, parent (each node except the root - defined by {@link Fqn#ROOT} - has * a single parent) and data contained within the node (as key/value pairs). The * data access methods are similar to the collections {@link Map} interface, * but some are read-only or return copies of the underlying the data. *

        * * @author Manik Surtani (manik@jboss.org) * @see Cache * @since 2.0.0 */ @ThreadSafe public interface Node { /** * Returns the parent node. * If this is the root node, this method returns null. * * @return the parent node, or null if this is the root node */ Node getParent(); /** * Returns an immutable set of children nodes. * * @return an immutable {@link Set} of child nodes. Empty {@link Set} if there aren't any children. */ Set> getChildren(); /** * Returns an immutable set of children node names. * * @return an immutable {@link Set} of child node names. Empty {@link Set} if there aren't any children. */ Set getChildrenNames(); /** * Returns a map containing the data in this {@link Node}. * * @return a {@link Map} containing the data in this {@link Node}. If there is no data, an empty {@link Map} is returned. The {@link Map} returned is always immutable. */ Map getData(); /** * Returns a {@link Set} containing the data in this {@link Node}. * * @return a {@link Set} containing the data in this {@link Node}. If there is no data, an empty {@link Set} is returned. The {@link Set} returned is always immutable. */ Set getKeys(); /** * Returns the {@link Fqn} which represents the location of this {@link Node} in the cache structure. The {@link Fqn} returned is absolute. * * @return The {@link Fqn} which represents the location of this {@link Node} in the cache structure. The {@link Fqn} returned is absolute. */ Fqn getFqn(); /** * Adds a child node with the given {@link Fqn} under the current node. Returns the newly created node. *

        * If the child exists returns the child node anyway. Guaranteed to return a non-null node. *

        * The {@link Fqn} passed in is relative to the current node. The new child node will have an absolute fqn * calculated as follows:

        new Fqn(getFqn(), f)
        . See {@link Fqn} for the operation of this constructor. * * @param f {@link Fqn} of the child node, relative to the current node. * @return the newly created node, or the existing node if one already exists. */ Node addChild(Fqn f); /** * Removes a child node specified by the given relative {@link Fqn}. *

        * If you wish to remove children based on absolute {@link Fqn}s, use the {@link Cache} interface instead. * * @param f {@link Fqn} of the child node, relative to the current node. * @return true if the node was found and removed, false otherwise */ boolean removeChild(Fqn f); /** * Removes a child node specified by the given name. * * @param childName name of the child node, directly under the current node. * @return true if the node was found and removed, false otherwise */ boolean removeChild(Object childName); /** * Returns the child node * * @param f {@link Fqn} of the child node * @return null if the child does not exist. */ Node getChild(Fqn f); /** * @param name name of the child * @return a direct child of the current node. */ Node getChild(Object name); /** * Associates the specified value with the specified key for this node. * If this node previously contained a mapping for this key, the old value is replaced by the specified value. * * @param key key with which the specified value is to be associated. * @param value value to be associated with the specified key. * @return Returns the old value contained under this key. Null if key doesn't exist. */ V put(K key, V value); /** * If the specified key is not already associated with a value, associate it with the given value, and returns the * Object (if any) that occupied the space, or null. *

        * Equivalent to calling *

            *   if (!node.getKeys().contains(key))
            *     return node.put(key, value);
            *   else
            *     return node.get(key);
            * 
        *

        * except that this is atomic. * * @param key key with which the specified value is to be associated. * @param value value to be associated with the specified key. * @return previous value associated with specified key, or null if there was no mapping for key. */ V putIfAbsent(K key, V value); /** * Replace entry for key only if currently mapped to some value. * Acts as *

            * if ((node.getKeys().contains(key))
            * {
            *     return node.put(key, value);
            * }
            * else
            *     return null;
            * 
        *

        * except that this is atomic. * * @param key key with which the specified value is associated. * @param value value to be associated with the specified key. * @return previous value associated with specified key, or null * if there was no mapping for key. */ V replace(K key, V value); /** * Replace entry for key only if currently mapped to given value. * Acts as *

            * if (node.get(key).equals(oldValue))
            * {
            *     node.put(key, newValue);
            *     return true;
            * }
            * else
            *     return false;
            * 
        *

        * except that this is atomic. * * @param key key with which the specified value is associated. * @param oldValue value expected to be associated with the specified key. * @param newValue value to be associated with the specified key. * @return true if the value was replaced */ boolean replace(K key, V oldValue, V newValue); /** * Copies all of the mappings from the specified map to this node's map. * If any data exists, existing keys are overwritten with the keys in the new map. * The behavior is equivalent to: *

            * Node node;
            * for (Map.Entry me : map.entrySet())
            *   node.put(me.getKey(), me.getValue());
            * 
        * * @param map map to copy from */ void putAll(Map map); /** * Similar to {@link #putAll(java.util.Map)} except that it removes any entries that exists in * the data map first. Note that this happens atomically, under a single lock. This is the analogous * to doing a {@link #clearData()} followed by a {@link #putAll(java.util.Map)} in the same transaction. * * @param map map to copy from */ void replaceAll(Map map); /** * Returns the value to which this node maps the specified key. * Returns null if the node contains no mapping for this key. * * @param key key of the data to return * @return the value to which this node maps the specified key, or null if the map contains no mapping for this key */ V get(K key); /** * Removes the mapping for this key from this node if it is present. * Returns the value to which the node previously associated the key, * or null if the node contained no mapping for this key * * @param key key whose mapping is to be removed * @return previous value associated with specified key, or null * if there was no mapping for key */ V remove(K key); /** * Removes all mappings from the node's data map. */ void clearData(); /** * @return the number of elements (key/value pairs) in the node's data map. */ int dataSize(); /** * Returns true if the child node denoted by the relative {@link Fqn} passed in exists. * * @param f {@link Fqn} relative to the current node of the child you are testing the existence of. * @return true if the child node denoted by the relative {@link Fqn} passed in exists. */ boolean hasChild(Fqn f); /** * Returns true if the child node denoted by the Object name passed in exists. * * @param o name of the child, relative to the current node * @return true if the child node denoted by the name passed in exists. */ boolean hasChild(Object o); /** * Tests if a node reference is still valid. A node reference may become invalid if it has been removed, invalidated * or moved, either locally or remotely. If a node is invalid, it should be fetched again from the cache or a valid * parent node. Operations on invalid nodes will throw a {@link org.jboss.cache.NodeNotValidException}. * * @return true if the node is valid. */ boolean isValid(); /** * Nodes marked resident would be ignored by the eviction algorithms. E.g. if the algorithm is * "keep LRU 10 nodes" - the resident nodes won't be counted within those 10 nodes, * and also won't be evicted when the threshold is reached. * N.B. calling this method won't have any effect on node's eviction, e.g. we won't consider this node as being * 'used' in a LRU scenario. If the cache is used in a replicated environment then the resident property is NOT * replicated across the cluster. Also the property is not transactionable. */ boolean isResident(); /** * @see #isResident() */ void setResident(boolean resident); /** * Tests whether this node is configured to be exclusively locked when inserting or removing children. *

        * The default * value for this is what is configured in the LockParentForChildInsertRemove configuration property, * programatically reachable by querying {@link org.jboss.cache.config.Configuration#isLockParentForChildInsertRemove()} *

        * This can also be configured on a per-node basis using {@link #setLockForChildInsertRemove(boolean)} * * @return true if the node is configured to be exclusively locked for child insertions and removal, false otherwise. * @since 2.1.0 */ boolean isLockForChildInsertRemove(); /** * Configures the behaviour of how this node is locked when adding/removing children. * * @param lockForChildInsertRemove if true, exclusive locks will be obtained when children are added/removed. If * false, a shared "read lock" will be obtained instead. * @since 2.1.0 */ void setLockForChildInsertRemove(boolean lockForChildInsertRemove); /** * Method that releases object references of cached objects held in the cache by serializing them to byte buffers. * Cached objects are lazily deserialized when accessed again, based on the calling thread's context class loader. *

        * This can be expensive, based on the effort required to serialize cached objects. *

        * * @param recursive if true, child nodes will have their object references released as well. */ void releaseObjectReferences(boolean recursive); } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/eviction/0000755000175000017500000000000011376174005024225 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/eviction/LFUPolicy.java0000644000175000017500000000123111005374170026665 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.eviction; /** * Least Frequently Used Eviction Policy. * * @author Daniel Huang - dhuang@jboss.org - 10/2005 * @version $Revision: 5730 $ */ public class LFUPolicy extends BaseEvictionPolicy { private LFUAlgorithm algorithm; public LFUPolicy() { super(); algorithm = new LFUAlgorithm(); } public EvictionAlgorithm getEvictionAlgorithm() { return algorithm; } public Class getEvictionConfigurationClass() { return LFUConfiguration.class; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/eviction/SortedEvictionQueue.java0000644000175000017500000000066411005374170031036 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.eviction; /** * Sorted Eviction Queue implementation. * * @author Daniel Huang (dhuang@jboss.org) * @version $Revision: 5730 $ */ public interface SortedEvictionQueue extends EvictionQueue { /** * Provide contract to resort a sorted queue. */ void resortEvictionQueue(); } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/eviction/NullEvictionAlgorithm.java0000755000175000017500000000154710741757474031400 0ustar twernertwerner/** * */ package org.jboss.cache.eviction; import org.jboss.cache.Region; /** * Algorithm for {@link NullEvictionPolicy}. * * @author Brian Stansberry */ public class NullEvictionAlgorithm implements EvictionAlgorithm { /** Singleton instance of this class. */ public static final NullEvictionAlgorithm INSTANCE = new NullEvictionAlgorithm(); /** * Constructs a new NullEvictionAlgorithm. */ private NullEvictionAlgorithm() { } /** * Returns {@link NullEvictionQueue#INSTANCE}. */ public EvictionQueue getEvictionQueue() { return NullEvictionQueue.INSTANCE; } /** No-op */ public void process(Region region) throws EvictionException { // no-op } /** No-op */ public void resetEvictionQueue(Region region) { // no-op } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/eviction/MRUPolicy.java0000644000175000017500000000133211005374170026704 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.eviction; /** * Most Recently Used Policy. *

        * This algorithm will evict the most recently used cache entries from cache. * * @author Daniel Huang (dhuang@jboss.org) * @version $Revision: 5730 $ */ public class MRUPolicy extends BaseEvictionPolicy { private MRUAlgorithm algorithm; public MRUPolicy() { super(); algorithm = new MRUAlgorithm(); } public EvictionAlgorithm getEvictionAlgorithm() { return algorithm; } public Class getEvictionConfigurationClass() { return MRUConfiguration.class; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/eviction/ExpirationConfiguration.java0000755000175000017500000000370110727544301031745 0ustar twernertwernerpackage org.jboss.cache.eviction; import org.jboss.cache.config.Dynamic; /** * Configuration for indicating the Node key for setting a specific eviction time. */ public class ExpirationConfiguration extends EvictionPolicyConfigBase { private static final long serialVersionUID = 47338798734219507L; /** * Default key name for indicating expiration time. */ public static final String EXPIRATION_KEY = "expiration"; /** * Node key name used to indicate the expiration of a node. */ @Dynamic private String expirationKeyName = EXPIRATION_KEY; @Dynamic private boolean warnNoExpirationKey = true; @Dynamic private int timeToLiveSeconds = 0; @Override protected void setEvictionPolicyClassName() { setEvictionPolicyClass(ExpirationPolicy.class.getName()); } /** * Returns the expirationKeyName. * This key should point to a java.lang.Long value in the Node data. */ public String getExpirationKeyName() { return expirationKeyName; } /** * Sets the expirationKeyName. */ public void setExpirationKeyName(String expirationKeyName) { this.expirationKeyName = expirationKeyName; } /** * Returns true if the algorithm should warn if a expiration key is missing for a node. */ public boolean getWarnNoExpirationKey() { return warnNoExpirationKey; } /** * Sets if the algorithm should warn if a expiration key is missing for a node. */ public void setWarnNoExpirationKey(boolean warnNoExpirationKey) { this.warnNoExpirationKey = warnNoExpirationKey; } public int getTimeToLiveSeconds() { return timeToLiveSeconds; } public void setTimeToLiveSeconds(int timeToLiveSeconds) { this.timeToLiveSeconds = timeToLiveSeconds; } @Override public ExpirationConfiguration clone() throws CloneNotSupportedException { return (ExpirationConfiguration) super.clone(); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/eviction/EvictionPolicy.java0000755000175000017500000000561510631150566030041 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.eviction; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.config.EvictionPolicyConfig; /** * Generic eviction policy interface. *

        * None of the Eviction classes are thread safe. It is assumed that an individual instance of an EvictionPolicy/ * EvictionAlgorithm/EvictionQueue/EvictionConfiguration are only operated on by one thread at any given time. * * @author Ben Wang 2-2004 * @author Daniel Huang - dhuang@jboss.org - 10/2005 */ public interface EvictionPolicy { /** * Evict a node form the underlying cache. * * @param fqn DataNode corresponds to this fqn. * @throws Exception */ void evict(Fqn fqn) throws Exception; /** * @return the CacheSPI instance this eviction policy is configured to work on. */ CacheSPI getCache(); /** * Method called to set the cache in this implementation. * * @param cache the cache to set */ void setCache(CacheSPI cache); /** * Get the associated EvictionAlgorithm used by the EvictionPolicy. *

        * This relationship should be 1-1. * * @return An EvictionAlgorithm implementation. */ EvictionAlgorithm getEvictionAlgorithm(); /** * The EvictionPolicyConfig implementation class used by this EvictionPolicy. * * @return EvictionPolicyConfig implementation class. */ Class getEvictionConfigurationClass(); /** * This method will be invoked prior to an event being processed for a node * with the specified Fqn. *

        * This method provides a way to optimize the performance of eviction by * signalling that the node associated with the specified Fqn should not be * subject to normal eviction processing. It can also be used to filter * out certain {@link NodeEventType event types} in which the particular * eviction algorithm has no interest. *

        *

        * If this method returns false then the event is processed normally * and eviction processing for the node continues. As a result, the event * will be added to the {@link org.jboss.cache.Region eviction region's} event queue where * at some later point the particular algorithm of the eviction policy * can use it to decide whether to call {@link #evict(Fqn)}. *

        *

        * If this method returns true, then the event is ignored and will not factor * in any subsequent eviction processing. *

        * * @param fqn The Fqn of the node associated with the event. * @param eventType the type of the event * @return true to ignore events of this type for this Fqn, * false to process events normally. */ boolean canIgnoreEvent(Fqn fqn, NodeEventType eventType); } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/eviction/NodeEventType.java0000644000175000017500000000267610527125277027640 0ustar twernertwerner/* * JBoss, Home of Professional Open Source. * Copyright 2006, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.cache.eviction; /** * Enumeration of the valid event types used to create an * {@link org.jboss.cache.eviction.EvictedEventNode}. * * @author Brian Stansberry * @version $Revision: 2972 $ */ public enum NodeEventType { ADD_NODE_EVENT, REMOVE_NODE_EVENT, VISIT_NODE_EVENT, ADD_ELEMENT_EVENT, REMOVE_ELEMENT_EVENT, MARK_IN_USE_EVENT, UNMARK_USE_EVENT } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/eviction/RegionNameConflictException.java0000755000175000017500000000100110537345073032453 0ustar twernertwernerpackage org.jboss.cache.eviction; /** * MarshRegion name conflicts with pre-existing regions. * * @author Ben Wang 2-2004 */ public class RegionNameConflictException extends Exception { private static final long serialVersionUID = -6796150029431162837L; public RegionNameConflictException() { super(); } public RegionNameConflictException(String msg) { super(msg); } public RegionNameConflictException(String msg, Throwable cause) { super(msg, cause); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/eviction/LRUConfiguration.java0000644000175000017500000000603211032145203030246 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.eviction; import org.jboss.cache.config.ConfigurationException; import org.jboss.cache.config.Dynamic; /** * Configuration implementation for {@link LRUPolicy}. *

        * If configured via XML, expects the following: *

        *

         * 
         *    10000
         *    8
         *    10
         * 
         * 
        * * @author Daniel Huang (dhuang@jboss.org) * @version $Revision: 6120 $ */ public class LRUConfiguration extends EvictionPolicyConfigBase { /** * The serialVersionUID */ private static final long serialVersionUID = -3426716488271559729L; @Dynamic private int timeToLiveSeconds; @Dynamic private int maxAgeSeconds; public LRUConfiguration() { super(); // Force config of ttls setTimeToLiveSeconds(-1); } @Override protected void setEvictionPolicyClassName() { setEvictionPolicyClass(LRUPolicy.class.getName()); } public int getTimeToLiveSeconds() { return timeToLiveSeconds; } public void setTimeToLiveSeconds(int timeToLiveSeconds) { testImmutability("timeToLiveSeconds"); this.timeToLiveSeconds = timeToLiveSeconds; } public int getMaxAgeSeconds() { return maxAgeSeconds; } public void setMaxAgeSeconds(int maxAgeSeconds) { testImmutability("maxAgeSeconds"); this.maxAgeSeconds = maxAgeSeconds; } /** * Requires a positive timeToLiveSeconds value or ConfigurationException * is thrown. */ @Override public void validate() throws ConfigurationException { if (timeToLiveSeconds < 0) { throw new ConfigurationException("timeToLiveSeconds must be " + "configured to a value greater than or equal to 0"); } } @Override public String toString() { StringBuilder str = new StringBuilder(); str.append("LRUConfiguration: timeToLiveSeconds = ").append(getTimeToLiveSeconds()).append(" maxAgeSeconds ="); str.append(getMaxAgeSeconds()).append(" maxNodes =").append(getMaxNodes()); return str.toString(); } @Override public boolean equals(Object obj) { if (super.equals(obj)) { LRUConfiguration other = (LRUConfiguration) obj; return maxAgeSeconds == other.maxAgeSeconds && timeToLiveSeconds == other.timeToLiveSeconds; } return false; } @Override public int hashCode() { int result = super.hashCode(); result = 31 * result + maxAgeSeconds; result = 31 * result + timeToLiveSeconds; return result; } @Override public void reset() { super.reset(); setTimeToLiveSeconds(-1); } @Override public LRUConfiguration clone() throws CloneNotSupportedException { return (LRUConfiguration) super.clone(); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/eviction/FIFOAlgorithm.java0000755000175000017500000000303511006101071027445 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.eviction; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.Region; /** * First-in-first-out algorithm used to evict nodes. * * @author Daniel Huang - dhuang@jboss.org * @author Morten Kvistgaard * @version $Revision: 5772 $ */ public class FIFOAlgorithm extends BaseEvictionAlgorithm { private static final Log log = LogFactory.getLog(FIFOAlgorithm.class); private static final boolean trace = log.isTraceEnabled(); @Override protected EvictionQueue setupEvictionQueue(Region region) throws EvictionException { return new FIFOQueue(); } /** * For FIFO, a node should be evicted if the queue size is >= to the configured maxNodes size. */ @Override protected boolean shouldEvictNode(NodeEntry ne) { // check the minimum time to live and see if we should not evict the node. This check will // ensure that, if configured, nodes are kept alive for at least a minimum period of time. if (isYoungerThanMinimumTimeToLive(ne)) return false; FIFOConfiguration config = (FIFOConfiguration) region.getEvictionPolicyConfig(); if (trace) { log.trace("Deciding whether node in queue " + ne.getFqn() + " requires eviction."); } int size = this.getEvictionQueue().getNumberOfNodes(); return config.getMaxNodes() != 0 && size > config.getMaxNodes(); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/eviction/LFUQueue.java0000644000175000017500000001236111032145203026511 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.eviction; import org.jboss.cache.Fqn; import java.util.Collections; import java.util.Comparator; 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.NoSuchElementException; import java.util.Set; /** * LFUQueue EvictionQueue implementation for LFU Policy. *

        * The queue is sorted in least frequently used order. * * @author Daniel Huang (dhuang@jboss.org) * @version $Revision: 6120 $ */ public class LFUQueue implements SortedEvictionQueue { private Map nodeMap; private LinkedList evictionList; private Comparator comparator; private Set removalQueue; private int numElements = 0; protected LFUQueue() { nodeMap = new HashMap(); comparator = new LFUComparator(); evictionList = new LinkedList(); removalQueue = new HashSet(); } /** * Return the first node to evict. *

        * This method will return the least frequently used entry in the queue. */ public NodeEntry getFirstNodeEntry() { try { NodeEntry ne; while ((ne = evictionList.getFirst()) != null) { if (removalQueue.contains(ne)) { evictionList.removeFirst(); removalQueue.remove(ne); } else { break; } } return ne; } catch (NoSuchElementException e) { return null; } } public NodeEntry getNodeEntry(Fqn fqn) { return nodeMap.get(fqn); } public NodeEntry getNodeEntry(String fqn) { return this.getNodeEntry(Fqn.fromString(fqn)); } public boolean containsNodeEntry(NodeEntry entry) { Fqn fqn = entry.getFqn(); return this.getNodeEntry(fqn) != null; } public void removeNodeEntry(NodeEntry entry) { NodeEntry ne = nodeMap.remove(entry.getFqn()); if (ne != null) { // don't remove directly from the LinkedList otherwise we will incur a O(n) = n // performance penalty for every removal! In the prune method for LFU, we will iterate the LinkedList through ONCE // doing a single O(n) = n operation and removal. This is much preferred over running O(n) = n every single time // remove is called. There is also special logic in the getFirstNodeEntry that will know to check // the removalQueue before returning. this.removalQueue.add(ne); /* if(!evictionList.remove(ne)) { throw new RuntimeException(""); } */ this.numElements -= ne.getNumberOfElements(); } } public void addNodeEntry(NodeEntry entry) { if (!this.containsNodeEntry(entry)) { Fqn fqn = entry.getFqn(); entry.queue = this; nodeMap.put(fqn, entry); evictionList.add(entry); this.numElements += entry.getNumberOfElements(); } } public int getNumberOfNodes() { return nodeMap.size(); } public int getNumberOfElements() { return this.numElements; } public void clear() { nodeMap.clear(); evictionList.clear(); removalQueue.clear(); this.numElements = 0; } public void resortEvictionQueue() { Collections.sort(evictionList, comparator); } public void modifyElementCount(int difference) { this.numElements += difference; } protected void prune() { Iterator it = this.iterate(); while (it.hasNext() && removalQueue.size() > 0) { if (removalQueue.remove(it.next())) { it.remove(); } } } protected final List getEvictionList() { return this.evictionList; } protected final Set getRemovalQueue() { return this.removalQueue; } public Iterator iterate() { return evictionList.iterator(); } /** * Comparator class for LFU. *

        * This class will sort the eviction queue in the correct eviction order. * The top of the list should evict before the bottom of the list. *

        * The sort is based on ascending order of nodeVisits. *

        * Note: this class has a natural ordering that is inconsistent with equals as defined by the java.lang.Comparator * contract. */ protected static class LFUComparator implements Comparator { public int compare(NodeEntry ne1, NodeEntry ne2) { if (ne1.equals(ne2)) { return 0; } int neNodeHits = ne1.getNumberOfNodeVisits(); int ne2NodeHits = ne2.getNumberOfNodeVisits(); if (neNodeHits > ne2NodeHits) { return 1; } else if (neNodeHits < ne2NodeHits) { return -1; } else if (neNodeHits == ne2NodeHits) { return 0; } throw new RuntimeException("Should never reach this condition"); } } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/eviction/LRUQueue.java0000644000175000017500000001055611024235313026534 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.eviction; import org.jboss.cache.Fqn; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; /** * LRU Eviction Queue implementation. *

        * This eviction queue will iterate properly through two sorted lists. * One sorted by maxAge and the other sorted by idleTime. * * @author Daniel Huang (dhuang@jboss.org) * @version $Revision: 5979 $ */ public class LRUQueue implements EvictionQueue { private Map maxAgeQueue; private Map lruQueue; private long alternatingCount = 0; private int numElements = 0; protected LRUQueue() { maxAgeQueue = new LinkedHashMap(); lruQueue = new LinkedHashMap(16, 0.75f, true); } protected void reorderByLRU(Fqn fqn) { // leave the max age queue alone - it is like a fifo. // the lru queue is access ordered. meaning the most recently read item is moved to the bottom of the queue. // simply calling get against it visits it and will cause LinkedHashMap to move it to the bottom of the queue. lruQueue.get(fqn); } public NodeEntry getFirstNodeEntry() { // because the underlying queue is in two differently sorted queues, we alternate between them when calling // a generic getFirstNodeEntry. // we must alternate to keep things balanced when evicting nodes based on the maxNodes attribute. We don't // want to just prune from one queue but rather we want to be able to prune from both. NodeEntry ne; if (alternatingCount % 2 == 0) { ne = this.getFirstLRUNodeEntry(); if (ne == null) { ne = this.getFirstMaxAgeNodeEntry(); } } else { ne = this.getFirstMaxAgeNodeEntry(); if (ne == null) { ne = this.getFirstLRUNodeEntry(); } } alternatingCount++; return ne; } public NodeEntry getFirstLRUNodeEntry() { if (lruQueue.size() > 0) { return lruQueue.values().iterator().next(); } return null; } public NodeEntry getFirstMaxAgeNodeEntry() { if (maxAgeQueue.size() > 0) { return maxAgeQueue.values().iterator().next(); } return null; } public NodeEntry getNodeEntry(Fqn fqn) { return lruQueue.get(fqn); } public NodeEntry getNodeEntry(String fqn) { return this.getNodeEntry(Fqn.fromString(fqn)); } public boolean containsNodeEntry(NodeEntry entry) { return this.maxAgeQueue.containsKey(entry.getFqn()); } protected void removeNodeEntryFromLRU(NodeEntry entry) { Fqn fqn = entry.getFqn(); lruQueue.remove(fqn); } protected void removeNodeEntryFromMaxAge(NodeEntry entry) { Fqn fqn = entry.getFqn(); maxAgeQueue.remove(fqn); } public void removeNodeEntry(NodeEntry entry) { if (!this.containsNodeEntry(entry)) { return; } Fqn fqn = entry.getFqn(); NodeEntry ne1 = lruQueue.remove(fqn); NodeEntry ne2 = maxAgeQueue.remove(fqn); if (ne1 == null || ne2 == null) { throw new RuntimeException("The queues are out of sync."); } this.numElements -= ne1.getNumberOfElements(); } public void addNodeEntry(NodeEntry entry) { if (!this.containsNodeEntry(entry)) { Fqn fqn = entry.getFqn(); entry.queue = this; maxAgeQueue.put(fqn, entry); lruQueue.put(fqn, entry); this.numElements += entry.getNumberOfElements(); } } public int getNumberOfNodes() { return maxAgeQueue.size(); } public int getNumberOfElements() { return this.numElements; } public void clear() { maxAgeQueue.clear(); lruQueue.clear(); this.numElements = 0; } public void modifyElementCount(int difference) { this.numElements += difference; } public Iterator iterate() { return lruQueue.values().iterator(); } protected final Iterator iterateMaxAgeQueue() { return maxAgeQueue.values().iterator(); } protected final Iterator iterateLRUQueue() { return lruQueue.values().iterator(); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/eviction/NullEvictionPolicyConfig.java0000755000175000017500000000155611004116445032013 0ustar twernertwerner/** * */ package org.jboss.cache.eviction; import org.jboss.cache.config.ConfigurationComponent; import org.jboss.cache.config.ConfigurationException; import org.jboss.cache.config.EvictionPolicyConfig; /** * Configuration class for {@link NullEvictionPolicy}. * * @author Brian Stansberry */ public class NullEvictionPolicyConfig extends ConfigurationComponent implements EvictionPolicyConfig { private static final long serialVersionUID = -6591180473728241737L; /** * Returns {@link NullEvictionPolicy}. */ public String getEvictionPolicyClass() { return NullEvictionPolicy.class.getName(); } /** * No-op */ public void reset() { // no-op } /** * No-op */ public void validate() throws ConfigurationException { // no-op } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/eviction/BaseEvictionPolicy.java0000755000175000017500000000200010727544301030615 0ustar twernertwernerpackage org.jboss.cache.eviction; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; /** * Base class implementation of EvictionPolicy and TreeCacheListener. * * @author Ben Wang 2-2004 * @author Daniel Huang - dhuang@jboss.org * @version $Revision: 4834 $ */ public abstract class BaseEvictionPolicy implements EvictionPolicy { protected CacheSPI cache_; /** EvictionPolicy interface implementation */ /** * Evict the node under given Fqn from cache. * * @param fqn The fqn of a node in cache. * @throws Exception */ public void evict(Fqn fqn) throws Exception { cache_.evict(fqn, false); } public void setCache(CacheSPI cache) { this.cache_ = cache; } public CacheSPI getCache() { return this.cache_; } /* * (non-Javadoc) * @see org.jboss.cache.eviction.EvictionPolicy#canIgnoreEvent(org.jboss.cache.Fqn) * */ public boolean canIgnoreEvent(Fqn fqn, NodeEventType eventType) { return false; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/eviction/ExpirationPolicy.java0000755000175000017500000000141310553723170030373 0ustar twernertwernerpackage org.jboss.cache.eviction; import org.jboss.cache.Fqn; /** * Returns the {@link ExpirationAlgorithm} as the policy's algorithm. * * @author rosse */ public class ExpirationPolicy extends BaseEvictionPolicy { private EvictionAlgorithm algorithm; public ExpirationPolicy() { algorithm = new ExpirationAlgorithm(this); } public EvictionAlgorithm getEvictionAlgorithm() { return algorithm; } public Class getEvictionConfigurationClass() { return ExpirationConfiguration.class; } /** * Returns true if it's a visit node event. */ @Override public boolean canIgnoreEvent(Fqn fqn, NodeEventType eventType) { return (eventType == NodeEventType.VISIT_NODE_EVENT); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/eviction/MRUConfiguration.java0000644000175000017500000000330311032145203030245 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.eviction; import org.jboss.cache.config.ConfigurationException; /** * Configuration for {@link MRUPolicy}. *

        * If configured via XML, expects the following: *

        *

         * 
         *   1000
         * 
         * 
        *

        * Requires a "maxNodes" attribute otherwise a ConfigurationException is thrown. * * @author Daniel Huang (dhuang@jboss.org) * @version $Revision: 6120 $ */ public class MRUConfiguration extends EvictionPolicyConfigBase { /** * The serialVersionUID */ private static final long serialVersionUID = -8734577898966155218L; public MRUConfiguration() { super(); // We require that maxNodes is set setMaxNodes(-1); } @Override protected void setEvictionPolicyClassName() { setEvictionPolicyClass(MRUPolicy.class.getName()); } /** * Requires a positive maxNodes value or ConfigurationException * is thrown. */ @Override public void validate() throws ConfigurationException { if (getMaxNodes() < 0) throw new ConfigurationException("maxNodes not configured"); } @Override public String toString() { StringBuilder str = new StringBuilder(); str.append("MRUConfiguration: "). append(" maxNodes =").append(getMaxNodes()); return str.toString(); } @Override public void reset() { setMaxNodes(-1); } @Override public MRUConfiguration clone() throws CloneNotSupportedException { return (MRUConfiguration) super.clone(); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/eviction/EvictionPolicyConfigBase.java0000644000175000017500000000601011032145203031731 0ustar twernertwernerpackage org.jboss.cache.eviction; import org.jboss.cache.config.ConfigurationComponent; import org.jboss.cache.config.ConfigurationException; import org.jboss.cache.config.Dynamic; import org.jboss.cache.config.EvictionPolicyConfig; import org.jboss.cache.util.Util; /** * Base implementation of {@link EvictionPolicyConfig}. Adds properties * for the most commonly used config elements. * * @author Manik Surtani */ public abstract class EvictionPolicyConfigBase extends ConfigurationComponent implements EvictionPolicyConfig { /** * The serialVersionUID */ private static final long serialVersionUID = 4591691674370188932L; private String evictionPolicyClass; @Dynamic private int maxNodes = 0; @Dynamic private int minTimeToLiveSeconds = 0; /** * Can only be instantiated by a subclass. *

        * Calls {@link #setEvictionPolicyClassName()}. */ protected EvictionPolicyConfigBase() { setEvictionPolicyClassName(); } public String getEvictionPolicyClass() { return evictionPolicyClass; } public void setEvictionPolicyClass(String evictionPolicyClass) { testImmutability("evictionPolicyClass"); this.evictionPolicyClass = evictionPolicyClass; } public int getMaxNodes() { return maxNodes; } public void setMaxNodes(int maxNodes) { testImmutability("maxNodes"); this.maxNodes = maxNodes; } public int getMinTimeToLiveSeconds() { return this.minTimeToLiveSeconds; } public void setMinTimeToLiveSeconds(int minTimeToLiveSeconds) { this.minTimeToLiveSeconds = minTimeToLiveSeconds; } public void validate() throws ConfigurationException { // no-op } @Override public boolean equals(Object obj) { if (obj == null) return false; if (this == obj) return true; if (getClass().equals(obj.getClass())) { EvictionPolicyConfigBase other = (EvictionPolicyConfigBase) obj; return this.maxNodes == other.maxNodes && this.minTimeToLiveSeconds == other.minTimeToLiveSeconds && Util.safeEquals(this.evictionPolicyClass, other.evictionPolicyClass); } return false; } @Override public int hashCode() { int result = 17; result = 31 * result + maxNodes; result = 31 * result + minTimeToLiveSeconds; result = 31 * result + (evictionPolicyClass == null ? 0 : evictionPolicyClass.hashCode()); return result; } public void reset() { setEvictionPolicyClass(null); setMaxNodes(0); setMinTimeToLiveSeconds(0); setEvictionPolicyClassName(); } /** * This method should be overridden by subclass implementers to set the default * {@link #setEvictionPolicyClass(String) policy class name} for the subclass. * This will be called when the implementation is constructed, but is also * called in {@link #reset()}. */ abstract protected void setEvictionPolicyClassName(); } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/eviction/NullEvictionQueue.java0000755000175000017500000000434510741757474030535 0ustar twernertwerner/** * */ package org.jboss.cache.eviction; import java.util.Iterator; import java.util.NoSuchElementException; import org.jboss.cache.Fqn; /** * A queue that does nothing. * * @author Brian Stansberry */ public class NullEvictionQueue implements EvictionQueue { /** Singleton instance of this class. */ public static final NullEvictionQueue INSTANCE = new NullEvictionQueue(); /** * Constructs a new NullEvictionQueue. */ private NullEvictionQueue() { } /** No-op */ public void addNodeEntry(NodeEntry entry) { // no-op } /** No-op */ public void clear() { // no-op } /** Returns false */ public boolean containsNodeEntry(NodeEntry entry) { return false; } /** Returns null */ public NodeEntry getFirstNodeEntry() { return null; } /** Returns null */ public NodeEntry getNodeEntry(Fqn fqn) { return null; } /** Returns null */ public NodeEntry getNodeEntry(String fqn) { return null; } /** Returns 0 */ public int getNumberOfElements() { return 0; } /** Returns 0 */ public int getNumberOfNodes() { return 0; } /** * Returns an Iterator whose * hasNext() returns false. */ public Iterator iterate() { return NullQueueIterator.INSTANCE; } /** No-op */ public void modifyElementCount(int difference) { // no-op } /** No-op */ public void removeNodeEntry(NodeEntry entry) { // no-op } static class NullQueueIterator implements Iterator { private static final NullQueueIterator INSTANCE = new NullQueueIterator(); private NullQueueIterator() {} public boolean hasNext() { return false; } public Object next() { throw new NoSuchElementException("No more elements"); } public void remove() { throw new IllegalStateException("Must call next() before remove()"); } } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/eviction/EvictionAlgorithm.java0000755000175000017500000000231510526347750030530 0ustar twernertwernerpackage org.jboss.cache.eviction; import org.jboss.cache.Region; /** * Interface for all eviction algorithms. *

        * Note: None of the Eviction classes are thread safe. It is assumed that an individual instance of an EvictionPolicy/ * EvictionAlgorithm/EvictionQueue/EvictionConfiguration are only operated on by one thread at any given time. * * @author Ben Wang 2-2004 * @author Daniel Huang - dhuang@jboss.org - 10/2005 * @version $Revision: 2925 $ */ public interface EvictionAlgorithm { /** * Entry point for evictin algorithm. This is an api called by the EvictionTimerTask * to process the node events in waiting and actual pruning, if necessary. * * @param region MarshRegion that this algorithm will operate on. */ void process(Region region) throws EvictionException; /** * Reset the whole eviction queue. Queue may needs to be reset due to corrupted state, for example. * * @param region MarshRegion that this algorithm will operate on. */ void resetEvictionQueue(Region region); /** * Get the EvictionQueue implementation used by this algorithm. * * @return the EvictionQueue implementation. */ EvictionQueue getEvictionQueue(); } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/eviction/FIFOQueue.java0000644000175000017500000000451311024235313026611 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.eviction; import org.jboss.cache.Fqn; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; /** * FIFO Eviction Queue implementation for FIFO Policy. * * @author Daniel Huang (dhuang@jboss.org) * @version $Revision: 5979 $ */ public class FIFOQueue implements EvictionQueue { private Map nodeMap; private int numElements = 0; protected FIFOQueue() { nodeMap = new LinkedHashMap(); // We use a LinkedHashMap here because we want to maintain FIFO ordering and still get the benefits of // O(n) = 1 for add/remove/search. } public NodeEntry getFirstNodeEntry() { /* Iterator it = nodeMap.keySet().iterator(); if(it.hasNext()) { return (NodeEntry) nodeMap.get(it.next()); } return null; */ // this code path is *slightly* faster when profiling. 20ms faster iterating over 200000 entries in queue. if (nodeMap.size() > 0) { return nodeMap.values().iterator().next(); } return null; } public NodeEntry getNodeEntry(Fqn fqn) { return nodeMap.get(fqn); } public NodeEntry getNodeEntry(String fqn) { return this.getNodeEntry(Fqn.fromString(fqn)); } public boolean containsNodeEntry(NodeEntry entry) { Fqn fqn = entry.getFqn(); return this.getNodeEntry(fqn) != null; } public void removeNodeEntry(NodeEntry entry) { NodeEntry e = nodeMap.remove(entry.getFqn()); this.numElements -= e.getNumberOfElements(); } public void addNodeEntry(NodeEntry entry) { if (!this.containsNodeEntry(entry)) { entry.queue = this; nodeMap.put(entry.getFqn(), entry); this.numElements += entry.getNumberOfElements(); } } public int getNumberOfNodes() { return nodeMap.size(); } public int getNumberOfElements() { return this.numElements; } public void modifyElementCount(int difference) { this.numElements += difference; } public void clear() { nodeMap.clear(); this.numElements = 0; } public Iterator iterate() { return nodeMap.values().iterator(); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/eviction/BaseEvictionAlgorithm.java0000644000175000017500000004231011020007335031275 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.eviction; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.Fqn; import org.jboss.cache.Region; import org.jboss.cache.config.Configuration; import org.jboss.cache.lock.TimeoutException; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; /** * Abstract Event Processing Eviction Algorithm. * This class is used to implement basic event processing for Eviction Algorithms. * To extend this abstract class to make an Eviction Algorithm, implement the * abstract methods and a policy. * * @author Daniel Huang - dhuang@jboss.org 10/2005 * @author Galder Zamarreno * @version $Revision: 5919 $ */ public abstract class BaseEvictionAlgorithm implements EvictionAlgorithm { private static final Log log = LogFactory.getLog(BaseEvictionAlgorithm.class); private static final boolean trace = log.isTraceEnabled(); /** * Mapped region. */ protected Region region; /** * Contains Fqn instances. */ protected BlockingQueue recycleQueue; /** * Contains NodeEntry instances. */ protected EvictionQueue evictionQueue; protected boolean allowTombstones = false; /** * This method will create an EvictionQueue implementation and prepare it for use. * * @param region MarshRegion to setup an eviction queue for. * @return The created EvictionQueue to be used as the eviction queue for this algorithm. * @throws EvictionException * @see EvictionQueue */ protected abstract EvictionQueue setupEvictionQueue(Region region) throws EvictionException; /** * This method will check whether the given node should be evicted or not. * * @param ne NodeEntry to test eviction for. * @return True if the given node should be evicted. False if the given node should not be evicted. */ protected abstract boolean shouldEvictNode(NodeEntry ne); protected BaseEvictionAlgorithm() { recycleQueue = new LinkedBlockingQueue(500000); } protected void initialize(Region region) throws EvictionException { if (region == null) throw new IllegalArgumentException("region"); this.region = region; evictionQueue = setupEvictionQueue(region); log.debug("initialized: " + this); // hacky temp solution till we have an ioc fwk to inject configuration elements as needed Configuration c = region.getCacheConfiguration(); Configuration.CacheMode cm = c != null ? c.getCacheMode() : Configuration.CacheMode.LOCAL; allowTombstones = c != null && c.isNodeLockingOptimistic() && (cm == Configuration.CacheMode.INVALIDATION_ASYNC || cm == Configuration.CacheMode.INVALIDATION_SYNC); } /** * Process the given region. *

        * Eviction Processing encompasses the following: *

        * - Add/Remove/Visit Nodes * - Prune according to Eviction Algorithm * - Empty/Retry the recycle queue of previously evicted but locked (during actual cache eviction) nodes. * * @param region Cache region to process for eviction. * @throws EvictionException */ public void process(Region region) throws EvictionException { if (this.region == null) { this.initialize(region); } if (trace) { log.trace("process(): region: " + region.getFqn()); } this.processQueues(region); this.emptyRecycleQueue(); this.prune(); } public void resetEvictionQueue(Region region) { } /** * Get the underlying EvictionQueue implementation. * * @return the EvictionQueue used by this algorithm * @see EvictionQueue */ public EvictionQueue getEvictionQueue() { return this.evictionQueue; } /** * Event processing for Evict/Add/Visiting of nodes. *

        * - On AddEvents a new element is added into the eviction queue * - On RemoveEvents, the removed element is removed from the eviction queue. * - On VisitEvents, the visited node has its eviction statistics updated (idleTime, numberOfNodeVisists, etc..) * * @param region Cache region to process for eviction. * @throws EvictionException */ protected void processQueues(Region region) throws EvictionException { EvictedEventNode node; int count = 0; while ((node = region.takeLastEventNode()) != null) { // Fqn fqn = node.getFqn(); count++; switch (node.getEventType()) { case ADD_NODE_EVENT: this.processAddedNodes(node); break; case REMOVE_NODE_EVENT: this.processRemovedNodes(node); break; case VISIT_NODE_EVENT: this.processVisitedNodes(node); break; case ADD_ELEMENT_EVENT: this.processAddedElement(node); break; case REMOVE_ELEMENT_EVENT: this.processRemovedElement(node); break; case MARK_IN_USE_EVENT: this.processMarkInUseNodes(node.getFqn(), node.getInUseTimeout()); break; case UNMARK_USE_EVENT: this.processUnmarkInUseNodes(node.getFqn()); break; default: throw new RuntimeException("Illegal Eviction Event type " + node.getEventType()); } } if (trace) { log.trace("processed " + count + " node events in region: " + region.getFqn()); } } protected void evict(NodeEntry ne) { // NodeEntry ne = evictionQueue.getNodeEntry(fqn); if (ne != null) { evictionQueue.removeNodeEntry(ne); if (!this.evictCacheNode(ne.getFqn())) { try { boolean result = recycleQueue.offer(ne.getFqn(), 5, TimeUnit.SECONDS); if (!result) { log.warn("Unable to add Fqn[" + ne.getFqn() + "] to recycle " + "queue because it's full. This is often sign that " + "evictions are not occurring and nodes that should be " + "evicted are piling up waiting to be evicted."); } } catch (InterruptedException e) { log.debug("InterruptedException", e); } } } } /** * Evict a node from cache. * * @param fqn node corresponds to this fqn * @return True if successful */ protected boolean evictCacheNode(Fqn fqn) { if (trace) { log.trace("Attempting to evict cache node with fqn of " + fqn); } EvictionPolicy policy = region.getEvictionPolicy(); try { policy.evict(fqn); } catch (TimeoutException e) { log.warn("Eviction of " + fqn + " timed out, retrying later"); log.debug(e, e); return false; } catch (Exception e) { log.error("Eviction of " + fqn + " failed", e); return false; } if (trace) { log.trace("Eviction of cache node with fqn of " + fqn + " successful"); } return true; } protected void processMarkInUseNodes(Fqn fqn, long inUseTimeout) throws EvictionException { if (trace) { log.trace("Marking node " + fqn + " as in use with a usage timeout of " + inUseTimeout); } NodeEntry ne = evictionQueue.getNodeEntry(fqn); if (ne != null) { ne.setCurrentlyInUse(true, inUseTimeout); } } protected void processUnmarkInUseNodes(Fqn fqn) throws EvictionException { if (trace) { log.trace("Unmarking node " + fqn + " as in use"); } NodeEntry ne = evictionQueue.getNodeEntry(fqn); if (ne != null) { ne.setCurrentlyInUse(false, 0); } } /** * Convenience method, which calls {@link #processAddedNodes(EvictedEventNode, int, boolean)} using values in the * evictedEventNode for number of added elements and the resetElementCount flag. * * @param evictedEventNode an evictedEventNode to process * @throws EvictionException on problems */ protected void processAddedNodes(EvictedEventNode evictedEventNode) throws EvictionException { processAddedNodes(evictedEventNode, evictedEventNode.getElementDifference()); } protected void processAddedNodes(EvictedEventNode evictedEventNode, int numAddedElements) throws EvictionException { Fqn fqn = evictedEventNode.getFqn(); if (trace) { log.trace("Adding node " + fqn + " with " + numAddedElements + " elements to eviction queue"); } NodeEntry ne = evictionQueue.getNodeEntry(fqn); if (ne != null) { ne.setModifiedTimeStamp(evictedEventNode.getCreationTimestamp()); ne.setNumberOfNodeVisits(ne.getNumberOfNodeVisits() + 1); ne.setNumberOfElements(ne.getNumberOfElements() + numAddedElements); if (trace) { log.trace("Queue already contains " + ne.getFqn() + " processing it as visited"); } processVisitedNodes(evictedEventNode); return; } ne = new NodeEntry(fqn); ne.setModifiedTimeStamp(evictedEventNode.getCreationTimestamp()); ne.setNumberOfNodeVisits(1); ne.setNumberOfElements(numAddedElements); evictionQueue.addNodeEntry(ne); if (trace) { log.trace(ne.getFqn() + " added successfully to eviction queue"); } } /** * Remove a node from cache. *

        * This method will remove the node from the eviction queue as well as * evict the node from cache. *

        * If a node cannot be removed from cache, this method will remove it from the eviction queue * and place the element into the recycleQueue. Each node in the recycle queue will get retried until * proper cache eviction has taken place. *

        * Because EvictionQueues are collections, when iterating them from an iterator, use iterator.remove() * to avoid ConcurrentModificationExceptions. Use the boolean parameter to indicate the calling context. * * @throws EvictionException */ protected void processRemovedNodes(EvictedEventNode evictedEventNode) throws EvictionException { Fqn fqn = evictedEventNode.getFqn(); if (trace) { log.trace("Removing node " + fqn + " from eviction queue and attempting eviction"); } NodeEntry ne = evictionQueue.getNodeEntry(fqn); if (ne != null) { if (allowTombstones) { // don't remove from the queue - deleting a node results in a tombstone which means the nodes // still need to be considered for eviction! return; } else { // a removeNode operation will simply remove the node. Nothing to worry about. evictionQueue.removeNodeEntry(ne); } } else { if (trace) { log.trace("processRemoveNodes(): Can't find node associated with fqn: " + fqn + "Could have been evicted earlier. Will just continue."); } return; } if (trace) { log.trace(fqn + " removed from eviction queue"); } } /** * Visit a node in cache. *

        * This method will update the numVisits and modifiedTimestamp properties of the Node. * These properties are used as statistics to determine eviction (LRU, LFU, MRU, etc..) *

        * *Note* that this method updates Node Entries by reference and does not put them back * into the queue. For some sorted collections, a remove, and a re-add is required to * maintain the sorted order of the elements. * * @throws EvictionException */ protected void processVisitedNodes(EvictedEventNode evictedEventNode) throws EvictionException { Fqn fqn = evictedEventNode.getFqn(); NodeEntry ne = evictionQueue.getNodeEntry(fqn); if (ne == null) { if (log.isDebugEnabled()) { log.debug("Visiting node that was not added to eviction queues. Assuming that it has 1 element."); } this.processAddedNodes(evictedEventNode, 1); return; } // note this method will visit and modify the node statistics by reference! // if a collection is only guaranteed sort order by adding to the collection, // this implementation will not guarantee sort order. ne.setNumberOfNodeVisits(ne.getNumberOfNodeVisits() + 1); ne.setModifiedTimeStamp(evictedEventNode.getCreationTimestamp()); } protected void processRemovedElement(EvictedEventNode evictedEventNode) throws EvictionException { Fqn fqn = evictedEventNode.getFqn(); NodeEntry ne = evictionQueue.getNodeEntry(fqn); if (ne == null) { if (log.isDebugEnabled()) { log.debug("Removing element from " + fqn + " but eviction queue does not contain this node. " + "Ignoring removeElement event."); } return; } ne.setNumberOfElements(ne.getNumberOfElements() - 1); // also treat it as a node visit. ne.setNumberOfNodeVisits(ne.getNumberOfNodeVisits() + 1); ne.setModifiedTimeStamp(evictedEventNode.getCreationTimestamp()); } protected void processAddedElement(EvictedEventNode evictedEventNode) throws EvictionException { Fqn fqn = evictedEventNode.getFqn(); NodeEntry ne = evictionQueue.getNodeEntry(fqn); if (ne == null) { if (trace) { log.trace("Adding element " + fqn + " for a node that doesn't exist yet. Process as an add."); } this.processAddedNodes(evictedEventNode, 1); return; } ne.setNumberOfElements(ne.getNumberOfElements() + 1); // also treat it as a node visit. ne.setNumberOfNodeVisits(ne.getNumberOfNodeVisits() + 1); ne.setModifiedTimeStamp(evictedEventNode.getCreationTimestamp()); // log.error ("*** Processing nodeAdded for fqn " + fqn + " NodeEntry's hashcode is " + ne.hashCode()); } /** * Empty the Recycle Queue. *

        * This method will go through the recycle queue and retry to evict the nodes from cache. * * @throws EvictionException */ protected void emptyRecycleQueue() throws EvictionException { while (true) { Fqn fqn; try { //fqn = (Fqn) recycleQueue.take(); fqn = recycleQueue.poll(0, TimeUnit.SECONDS); } catch (InterruptedException e) { log.debug(e, e); break; } if (fqn == null) { if (trace) { log.trace("Recycle queue is empty"); } break; } if (trace) { log.trace("emptying recycle bin. Evict node " + fqn); } // Still doesn't work if (!evictCacheNode(fqn)) { try { recycleQueue.put(fqn); } catch (InterruptedException e) { log.debug(e, e); } break; } } } protected boolean isNodeInUseAndNotTimedOut(NodeEntry ne) { if (ne.isCurrentlyInUse()) { if (ne.getInUseTimeoutTimestamp() == 0) { return true; } if (System.currentTimeMillis() < ne.getInUseTimeoutTimestamp()) { return true; } } return false; } protected void prune() throws EvictionException { NodeEntry entry; while ((entry = evictionQueue.getFirstNodeEntry()) != null) { if (this.shouldEvictNode(entry)) { this.evict(entry); } else { break; } } } /** * Returns debug information. */ @Override public String toString() { return super.toString() + " reqion=" + region.getFqn() + " recycle=" + recycleQueue.size() + " evict=" + evictionQueue.getNumberOfNodes(); } /** * Tests whether a node entry is younger than the minimum time to live - if one is configured. * * @param entry the node entry being examined * @return true if the node is younger than - or exactly equal to - the minimum time to live, if one is configured for the given region. False otherwise. */ protected boolean isYoungerThanMinimumTimeToLive(NodeEntry entry) { if (region.getEvictionPolicyConfig() instanceof EvictionPolicyConfigBase) { EvictionPolicyConfigBase cfg = (EvictionPolicyConfigBase) region.getEvictionPolicyConfig(); int minTTL = cfg.getMinTimeToLiveSeconds(); return minTTL >= 1 && (entry.getModifiedTimeStamp() + (1000 * minTTL) > System.currentTimeMillis()); } else { log.trace("Eviction policy implementation does not support minimum TTL!"); return false; } } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/eviction/ElementSizePolicy.java0000644000175000017500000000117611005374170030473 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.eviction; /** * @author Daniel Huang * @version $Revison: $ */ public class ElementSizePolicy extends BaseEvictionPolicy { private ElementSizeAlgorithm algorithm; public ElementSizePolicy() { super(); algorithm = new ElementSizeAlgorithm(); } public EvictionAlgorithm getEvictionAlgorithm() { return this.algorithm; } public Class getEvictionConfigurationClass() { return ElementSizeConfiguration.class; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/eviction/LRUPolicy.java0000755000175000017500000000212611005374170026710 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. * Created on March 25 2003 */ package org.jboss.cache.eviction; import org.jboss.cache.CacheSPI; import org.jboss.cache.RegionManager; /** * Provider to provide eviction policy. This one is based on LRU algorithm that a user * can specify either maximum number of nodes or the idle time of a node to be evicted. * * @author Ben Wang 02-2004 * @author Daniel Huang - dhuang@jboss.org * @version $Revision: 5730 $ */ public class LRUPolicy extends BaseEvictionPolicy { protected RegionManager regionManager_; protected EvictionAlgorithm algorithm; public LRUPolicy() { super(); algorithm = new LRUAlgorithm(); } public EvictionAlgorithm getEvictionAlgorithm() { return algorithm; } public Class getEvictionConfigurationClass() { return LRUConfiguration.class; } @Override public void setCache(CacheSPI cache) { super.setCache(cache); regionManager_ = cache_.getRegionManager(); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/eviction/MRUAlgorithm.java0000644000175000017500000000303211004125045027364 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.eviction; import org.jboss.cache.Region; /** * Most Recently Used Algorithm. *

        * This algorithm will evict the most recently used cache entries from cache. *

        * Note: None of the Eviction classes are thread safe. It is assumed that an individual instance of an EvictionPolicy/ * EvictionAlgorithm/EvictionQueue/EvictionConfiguration are only operated on by one thread at any given time. * * @author Daniel Huang (dhuang@jboss.org) * @version $Revision: 5671 $ */ public class MRUAlgorithm extends BaseEvictionAlgorithm { @Override protected EvictionQueue setupEvictionQueue(Region region) throws EvictionException { return new MRUQueue(); } @Override protected boolean shouldEvictNode(NodeEntry ne) { // check the minimum time to live and see if we should not evict the node. This check will // ensure that, if configured, nodes are kept alive for at least a minimum period of time. if (isYoungerThanMinimumTimeToLive(ne)) return false; MRUConfiguration config = (MRUConfiguration) region.getEvictionPolicyConfig(); return evictionQueue.getNumberOfNodes() > config.getMaxNodes(); } @Override protected void processVisitedNodes(EvictedEventNode evictedEventNode) throws EvictionException { super.processVisitedNodes(evictedEventNode); ((MRUQueue) evictionQueue).moveToTopOfStack(evictedEventNode.getFqn()); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/eviction/EvictionQueueList.java0000644000175000017500000001637511012441070030507 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.eviction; import java.util.Arrays; import java.util.Comparator; import java.util.ConcurrentModificationException; import java.util.Iterator; import java.util.ListIterator; import java.util.NoSuchElementException; /** * @author Daniel Huang (dhuang@jboss.org) * @version $Revision: 5836 $ */ public class EvictionQueueList { EvictionListEntry head; EvictionListEntry tail; int modCount; private int size; EvictionQueueList() { head = null; tail = null; size = 0; modCount = 0; } void addToTop(EvictionListEntry entry) { EvictionListEntry formerHead = head; head = entry; // if there was no previous head then this list was empty. if (formerHead != null) { formerHead.previous = head; head.next = formerHead; head.previous = null; } else { tail = entry; } size++; modCount++; } void addToBottom(EvictionListEntry entry) { EvictionListEntry formerTail = tail; tail = entry; // if there was no previous head then this list was empty. if (formerTail != null) { tail.previous = formerTail; formerTail.next = tail; tail.next = null; } else { head = entry; } size++; modCount++; } void remove(EvictionListEntry entry) { if (this.isEmpty()) { return; } if (isSingleNode(entry)) { head = null; tail = null; } else if (isTail(entry)) { tail = entry.previous; // unlink the last node. entry.previous.next = null; } else if (isHead(entry)) { head = entry.next; head.previous = null; } else { // node is in between two other nodes. entry.next.previous = entry.previous; entry.previous.next = entry.next; } size--; modCount++; } int size() { return this.size; } void clear() { head = null; tail = null; size = 0; modCount++; } EvictionListEntry getFirst() { if (head == null) { throw new NoSuchElementException("List is empty"); } return head; } EvictionListEntry getLast() { if (tail == null) { throw new NoSuchElementException("List is empty"); } return tail; } Iterator iterator() { return new EvictionListIterator(); } NodeEntry[] toNodeEntryArray() { if (isEmpty()) { return null; } NodeEntry[] ret = new NodeEntry[size]; int i = 0; EvictionListEntry temp = head; do { ret[i] = temp.node; temp = temp.next; i++; } while (temp != null); return ret; } EvictionListEntry[] toArray() { if (isEmpty()) { return null; } EvictionListEntry[] ret = new EvictionListEntry[size]; int i = 0; EvictionListEntry temp = head; do { ret[i] = temp; temp = temp.next; i++; } while (temp != null); return ret; } void fromArray(EvictionListEntry[] evictionListEntries) { for (EvictionListEntry evictionListEntry : evictionListEntries) { this.addToBottom(evictionListEntry); } } private boolean isEmpty() { return head == null && tail == null; } private boolean isSingleNode(EvictionListEntry entry) { return isTail(entry) && isHead(entry); } private boolean isTail(EvictionListEntry entry) { return entry.next == null; } private boolean isHead(EvictionListEntry entry) { return entry.previous == null; } @Override public String toString() { return Arrays.asList(toArray()).toString(); } static class EvictionListComparator implements Comparator { Comparator nodeEntryComparator; EvictionListComparator(Comparator nodeEntryComparator) { this.nodeEntryComparator = nodeEntryComparator; } public int compare(EvictionListEntry e1, EvictionListEntry e2) { return nodeEntryComparator.compare(e1.node, e2.node); } } class EvictionListIterator implements ListIterator { EvictionListEntry next = head; EvictionListEntry previous; EvictionListEntry cursor; int initialModCount = EvictionQueueList.this.modCount; public boolean hasNext() { this.doConcurrentModCheck(); return next != null; } public Object next() { this.doConcurrentModCheck(); this.forwardCursor(); return cursor.node; } public boolean hasPrevious() { this.doConcurrentModCheck(); return previous != null; } public Object previous() { this.doConcurrentModCheck(); this.rewindCursor(); return cursor.node; } public int nextIndex() { throw new UnsupportedOperationException(); } public int previousIndex() { throw new UnsupportedOperationException(); } public void remove() { this.doConcurrentModCheck(); if (cursor == null) { throw new IllegalStateException("Cannot remove from iterator when there is nothing at the current iteration point"); } EvictionQueueList.this.remove(cursor); cursor = null; initialModCount++; } public void set(Object o) { this.doConcurrentModCheck(); cursor.node = (NodeEntry) o; } public void add(Object o) { this.doConcurrentModCheck(); initialModCount++; } private void doConcurrentModCheck() { if (EvictionQueueList.this.modCount != initialModCount) { throw new ConcurrentModificationException(); } } private void forwardCursor() { if (next == null) { throw new NoSuchElementException("No more objects to iterate."); } previous = cursor; cursor = next; next = cursor.next; } private void rewindCursor() { if (previous == null) { throw new NoSuchElementException(); } next = cursor; cursor = previous; previous = cursor.previous; } } } class EvictionListEntry { EvictionListEntry next; EvictionListEntry previous; NodeEntry node; EvictionListEntry() { } EvictionListEntry(NodeEntry node) { this.node = node; } @Override public boolean equals(Object o) { if (!(o instanceof EvictionListEntry)) return false; EvictionListEntry entry = (EvictionListEntry) o; return this.node.getFqn().equals(entry.node.getFqn()); } @Override public int hashCode() { return this.node.getFqn().hashCode(); } @Override public String toString() { return "EntryLE=" + node; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/eviction/BaseSortedEvictionAlgorithm.java0000644000175000017500000000663511032145203032470 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.eviction; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.Region; /** * An abstract SortedEvictionAlgorithm. *

        * This class supports early termination of the eviction queue processing. Because the eviction * queue is sorted by first to evict to last to evict, when iterating the eviction queue, the first time * a node is encountered that does not require eviction will terminate the loop early. This way we don't incur * the full breadth of the O(n) = n operation everytime we need to check for eviction (defined by eviction poll time * interval). * * @author Daniel Huang - dhuang@jboss.org - 10/2005 */ public abstract class BaseSortedEvictionAlgorithm extends BaseEvictionAlgorithm { private static final Log log = LogFactory.getLog(BaseSortedEvictionAlgorithm.class); private static final boolean trace = log.isTraceEnabled(); @Override protected void processQueues(Region region) throws EvictionException { boolean evictionNodesModified = false; EvictedEventNode node; int count = 0; while ((node = region.takeLastEventNode()) != null) { count++; switch (node.getEventType()) { case ADD_NODE_EVENT: this.processAddedNodes(node); evictionNodesModified = true; break; case REMOVE_NODE_EVENT: this.processRemovedNodes(node); break; case VISIT_NODE_EVENT: this.processVisitedNodes(node); evictionNodesModified = true; break; case ADD_ELEMENT_EVENT: this.processAddedElement(node); evictionNodesModified = true; break; case REMOVE_ELEMENT_EVENT: this.processRemovedElement(node); evictionNodesModified = true; break; default: throw new RuntimeException("Illegal Eviction Event type " + node.getEventType()); } } if (trace) { log.trace("Eviction nodes visited or added requires resort of queue " + evictionNodesModified); } this.resortEvictionQueue(evictionNodesModified); if (trace) { log.trace("processed " + count + " node events"); } } /** * This method is called to resort the queue after add or visit events have occurred. *

        * If the parameter is true, the queue needs to be resorted. If it is false, the queue does not * need resorting. * * @param evictionQueueModified True if the queue was added to or visisted during event processing. */ protected void resortEvictionQueue(boolean evictionQueueModified) { if (!evictionQueueModified) { if (log.isDebugEnabled()) { log.debug("Eviction queue not modified. Resort unnecessary."); } return; } long begin = System.currentTimeMillis(); ((SortedEvictionQueue) evictionQueue).resortEvictionQueue(); long end = System.currentTimeMillis(); if (trace) { long diff = end - begin; log.trace("Took " + diff + "ms to sort queue with " + getEvictionQueue().getNumberOfNodes() + " elements"); } } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/eviction/ElementSizeConfiguration.java0000644000175000017500000000544211032145203032034 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.eviction; import org.jboss.cache.config.ConfigurationException; import org.jboss.cache.config.Dynamic; /** * Configuration for {@link ElementSizePolicy}. *

        * If configured via XML, expects the following: *

        *

         * 
         *    100
         *    10000
         * 
         * 
        *

        * Requires a positive "maxElementsPerNode" value otherwise a ConfigurationException is thrown. * * @author Daniel Huang * @author Brian Stansberry * @version $Revision: 6120 $ */ public class ElementSizeConfiguration extends EvictionPolicyConfigBase { /** * The serialVersionUID */ private static final long serialVersionUID = 2510593544656833758L; @Dynamic private int maxElementsPerNode; public ElementSizeConfiguration() { super(); // Force configuration of maxElementsPerNode setMaxElementsPerNode(-1); } @Override protected void setEvictionPolicyClassName() { setEvictionPolicyClass(ElementSizePolicy.class.getName()); } public int getMaxElementsPerNode() { return maxElementsPerNode; } public void setMaxElementsPerNode(int maxElementsPerNode) { testImmutability("maxElementsPerNode"); this.maxElementsPerNode = maxElementsPerNode; } /** * Requires a positive maxElementsPerNode value or ConfigurationException * is thrown. */ @Override public void validate() throws ConfigurationException { if (maxElementsPerNode < 0) { throw new ConfigurationException("maxElementsPerNode must be must be " + "configured to a value greater than or equal to 0"); } } @Override public String toString() { StringBuilder str = new StringBuilder(); str.append("ElementSizeConfiguration: maxElementsPerNode ="); str.append(getMaxElementsPerNode()).append(" maxNodes =").append(getMaxNodes()); return str.toString(); } @Override public boolean equals(Object obj) { if (this == obj) return true; if (super.equals(obj)) { return this.maxElementsPerNode == ((ElementSizeConfiguration) obj).maxElementsPerNode; } return false; } @Override public int hashCode() { int result = super.hashCode(); result = 31 * result + maxElementsPerNode; return result; } @Override public void reset() { super.reset(); setMaxElementsPerNode(-1); } @Override public ElementSizeConfiguration clone() throws CloneNotSupportedException { return (ElementSizeConfiguration) super.clone(); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/eviction/EvictionException.java0000755000175000017500000000064510537345073030542 0ustar twernertwernerpackage org.jboss.cache.eviction; /** * @author Ben Wang, Feb 11, 2004 */ public class EvictionException extends Exception { private static final long serialVersionUID = 4006783737166646935L; public EvictionException() { super(); } public EvictionException(String msg) { super(msg); } public EvictionException(String msg, Throwable cause) { super(msg, cause); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/eviction/ExpirationAlgorithm.java0000755000175000017500000002167011006101071031051 0ustar twernertwernerpackage org.jboss.cache.eviction; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.Fqn; import org.jboss.cache.NodeSPI; import org.jboss.cache.Region; import java.util.Iterator; import java.util.SortedSet; import java.util.TreeSet; /** * Eviction algorithm that uses a key in the Node data that indicates the time * the node should be evicted. The key must be a java.lang.Long object, with * the time to expire as milliseconds past midnight January 1st, 1970 UTC (the * same relative time as provided by {@link * java.lang.System#currentTimeMillis()}). *

        * This algorithm also obeys the configuration key {@link * ExpirationConfiguration#getMaxNodes()}, and will evict the soonest to * expire entires first to reduce the region size. If there are not enough * nodes with expiration keys set, a warning is logged. *

        * If a node in the eviction region does not have an expiration value, then * {@link ExpirationConfiguration#getTimeToLiveSeconds} (if set) will be used. * The expiration is updated when a node is added or updated. *

        * If there is no time-to-live set, and a node in the eviction region does not * have an expiration value, then that node will never be evicted. As * forgetting to indicate an expiration value is likely a mistake, a warning * message is logged by this class. This warning, however, can be disabled * through {@link ExpirationConfiguration#setWarnNoExpirationKey(boolean)}. *

        * A node's expiration time can be changed by setting a new value in the node. *

        * Example usage: *

         * Cache cache;
         * Fqn fqn1 = Fqn.fromString("/node/1");
         * Long future = new Long(System.currentTimeMillis() + 2000);
         * cache.put(fqn1, ExpirationConfiguration.EXPIRATION_KEY, future);
         * cache.put(fqn1, "foo");
         * assertTrue(cache.get(fqn1) != null);
         * 

        * Thread.sleep(5000); // 5 seconds * assertTrue(cache.get(fqn1) == null); *

        *

        */ public class ExpirationAlgorithm extends BaseEvictionAlgorithm { private static final Log log = LogFactory.getLog(ExpirationAlgorithm.class); private static final boolean trace = log.isTraceEnabled(); private ExpirationConfiguration config; private ExpirationPolicy policy; private SortedSet set; /** * Constructs a new algorithm with a policy. */ public ExpirationAlgorithm(ExpirationPolicy policy) { this.policy = policy; this.set = new TreeSet(); } private void addEvictionEntry(EvictedEventNode node) { Fqn fqn = node.getFqn(); addEvictionEntry(fqn); } private void addEvictionEntry(Fqn fqn) { Long l = getExpiration(fqn); if (l == null) { if (config.getWarnNoExpirationKey()) log.warn("No expiration key '" + config.getExpirationKeyName() + "' for Node: " + fqn); else if (log.isDebugEnabled()) log.debug("No expiration key for Node: " + fqn); } else { setExpiration(fqn, l); } } private void setExpiration(Fqn fqn, Long l) { ExpirationEntry ee = new ExpirationEntry(fqn, l); if (trace) log.trace("adding eviction entry: " + ee); set.add(ee); } @SuppressWarnings("unchecked") private Long getExpiration(Fqn fqn) { NodeSPI n = policy.getCache().peek(fqn, false); if (n == null) return null; return n.getDirect(config.getExpirationKeyName()); } @Override protected void processQueues(Region region) throws EvictionException { EvictedEventNode node; int count = 0; while ((node = region.takeLastEventNode()) != null) { count++; switch (node.getEventType()) { case ADD_NODE_EVENT: case ADD_ELEMENT_EVENT: addEvictionEntry(node); break; case REMOVE_ELEMENT_EVENT: case REMOVE_NODE_EVENT: case UNMARK_USE_EVENT: // Removals will be noticed when double-checking expiry time // removeEvictionEntry(node); break; case VISIT_NODE_EVENT: // unused break; case MARK_IN_USE_EVENT: markInUse(node); break; default: throw new RuntimeException("Illegal Eviction Event type " + node.getEventType()); } } if (trace) { log.trace("processed " + count + " node events in region: " + region.getFqn()); } } private void markInUse(EvictedEventNode node) { long expiration = node.getInUseTimeout() + System.currentTimeMillis(); setExpiration(node.getFqn(), expiration); } @Override protected void prune() throws EvictionException { if (set.isEmpty()) return; long now = System.currentTimeMillis(); int max = config.getMaxNodes(); for (Iterator i = set.iterator(); i.hasNext();) { ExpirationEntry ee = i.next(); Long ce = getExpiration(ee.getFqn()); if (ce == null || ce > ee.getExpiration()) { // Expiration now older i.remove(); continue; } if (ee.getExpiration() < now || (max != 0 && set.size() > max)) { i.remove(); evictCacheNode(ee.getFqn()); } else { break; } } if (max != 0 && max > set.size()) log.warn("Unable to remove nodes to reduce region size below " + config.getMaxNodes() + ". " + "Set expiration for nodes in this region"); } @Override public void resetEvictionQueue(Region region) { for (ExpirationEntry ee : set) { addEvictionEntry(ee.getFqn()); } } @Override protected EvictionQueue setupEvictionQueue(Region region) throws EvictionException { this.region = region; this.config = (ExpirationConfiguration) region.getEvictionPolicyConfig(); return new DummyEvictionQueue(); } @Override protected boolean shouldEvictNode(NodeEntry ne) { throw new UnsupportedOperationException(); } /** * Ordered list of FQN, with the expiration taken from the Map at the time * of processing. */ static class ExpirationEntry implements Comparable { private long expiration; private Fqn fqn; public ExpirationEntry(Fqn fqn) { this.fqn = fqn; } public ExpirationEntry(Fqn fqn, long expiration) { this.fqn = fqn; this.expiration = expiration; } /** * Compares expiration, then FQN order. */ public int compareTo(ExpirationEntry ee) { long n = expiration - ee.expiration; if (n < 0) return -1; if (n > 0) return 1; return fqn.compareTo(ee.fqn); } /** * @return the expiration */ public long getExpiration() { return expiration; } /** * @return the fqn */ public Fqn getFqn() { return fqn; } @Override public boolean equals(Object o) { if (!(o instanceof ExpirationEntry)) return false; ExpirationEntry ee = (ExpirationEntry) o; return expiration == ee.expiration && fqn.equals(ee.fqn); } @Override public int hashCode() { return (int) expiration ^ fqn.hashCode(); } @Override public String toString() { long now = System.currentTimeMillis(); long ttl = expiration - now; String sttl; if (ttl > 1000 * 60) sttl = (ttl / (1000 * 60)) + "min"; else if (ttl > 1000) sttl = (ttl / 1000) + "s"; else sttl = ttl + "ms"; return "EE fqn=" + fqn + " ttl=" + sttl; } } class DummyEvictionQueue implements EvictionQueue { public void addNodeEntry(NodeEntry entry) { throw new UnsupportedOperationException(); } public void clear() { set.clear(); } public boolean containsNodeEntry(NodeEntry entry) { return false; } public NodeEntry getFirstNodeEntry() { return null; } public NodeEntry getNodeEntry(Fqn fqn) { return null; } public NodeEntry getNodeEntry(String fqn) { return null; } public int getNumberOfElements() { return set.size(); } public int getNumberOfNodes() { return set.size(); } public Iterator iterate() { return null; } public void modifyElementCount(int difference) { } public void removeNodeEntry(NodeEntry entry) { throw new UnsupportedOperationException(); } } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/eviction/EvictedEventNode.java0000755000175000017500000000405511020007335030255 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.eviction; import org.jboss.cache.Fqn; /** * Value object used in evicted event node queue. * * @author Ben Wang 2-2004 * @author Daniel Huang (dhuang@jboss.org) * @see org.jboss.cache.Region */ public class EvictedEventNode implements Cloneable { private Fqn fqn_; private NodeEventType type; private int elementDifference; private long inUseTimeout; private long creationTimestamp; public EvictedEventNode(Fqn fqn, NodeEventType type, int elementDifference) { this(fqn, type); setElementDifference(elementDifference); } public EvictedEventNode(Fqn fqn, NodeEventType event) { setFqn(fqn); setEventType(event); creationTimestamp = System.currentTimeMillis(); } public long getCreationTimestamp() { return creationTimestamp; } public long getInUseTimeout() { return inUseTimeout; } public void setInUseTimeout(long inUseTimeout) { this.inUseTimeout = inUseTimeout; } public int getElementDifference() { return elementDifference; } public void setElementDifference(int elementDifference_) { this.elementDifference = elementDifference_; } public Fqn getFqn() { return fqn_; } public void setFqn(Fqn fqn) { this.fqn_ = fqn; } public void setEventType(NodeEventType event) { type = event; } public NodeEventType getEventType() { return type; } @Override public String toString() { return "EvictedEN[fqn=" + fqn_ + " event=" + type + " diff=" + elementDifference + "]"; } public EvictedEventNode clone(Fqn cloneFqn) { EvictedEventNode clone = null; try { clone = (EvictedEventNode) super.clone(); clone.setFqn(Fqn.fromRelativeFqn(cloneFqn, fqn_)); } catch (CloneNotSupportedException e) { // should never get here } return clone; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/eviction/NodeEntry.java0000755000175000017500000000732411031017377027004 0ustar twernertwernerpackage org.jboss.cache.eviction; import org.jboss.cache.Fqn; /** * Value object used in queue * * @author Ben Wang 2-2004 * @author Daniel Huang - dhuang@jboss.org */ public class NodeEntry { private long modifiedTimeStamp; private long creationTimeStamp; private int numberOfNodeVisits; private int numberOfElements; private Fqn fqn; private long inUseTimeoutTimestamp; private boolean currentlyInUse = false; EvictionQueue queue; /** * Private constructor that automatically sets the creation time stamp of the node entry. */ private NodeEntry() { this.creationTimeStamp = System.currentTimeMillis(); } public NodeEntry(Fqn fqn) { this(); setFqn(fqn); } public NodeEntry(String fqn) { this(); setFqn(Fqn.fromString(fqn)); } /** * Is the node currently in use. * * @return True/false if the node is currently marked as in use. */ public boolean isCurrentlyInUse() { return currentlyInUse; } public void setCurrentlyInUse(boolean currentlyInUse, long inUseTimeout) { this.currentlyInUse = currentlyInUse; if (inUseTimeout > 0) { this.inUseTimeoutTimestamp = System.currentTimeMillis() + inUseTimeout; } } public long getInUseTimeoutTimestamp() { return this.inUseTimeoutTimestamp; } /** * Get modified time stamp. This stamp is created during the node is * processed so it has some fuzy tolerance in there. * * @return The last modified time stamp */ public long getModifiedTimeStamp() { return modifiedTimeStamp; } public void setModifiedTimeStamp(long modifiedTimeStamp) { // log.error("Being modified to " + modifiedTimeStamp, new Throwable()); this.modifiedTimeStamp = modifiedTimeStamp; } /** * Get the time stamp for when the node entry was created. * * @return The node entry creation time stamp */ public long getCreationTimeStamp() { return creationTimeStamp; } public void setCreationTimeStamp(long creationTimeStamp) { this.creationTimeStamp = creationTimeStamp; } public int getNumberOfNodeVisits() { return numberOfNodeVisits; } public void setNumberOfNodeVisits(int numberOfNodeVisits) { this.numberOfNodeVisits = numberOfNodeVisits; } public int getNumberOfElements() { return numberOfElements; } public void setNumberOfElements(int numberOfElements) { if (queue != null) { int difference = numberOfElements - this.numberOfElements; queue.modifyElementCount(difference); } this.numberOfElements = numberOfElements; } public Fqn getFqn() { return fqn; } void setFqn(Fqn fqn) { this.fqn = fqn; } @Override public int hashCode() { return fqn.hashCode(); } @Override public boolean equals(Object o) { if (!(o instanceof NodeEntry)) return false; NodeEntry ne = (NodeEntry) o; return fqn.equals(ne.getFqn()); } @Override public String toString() { StringBuilder output = new StringBuilder(); output.append("Fqn: "); if (fqn != null) { output.append(fqn); } else { output.append(" null"); } output.append(" CreateTime: ").append(this.getCreationTimeStamp()); output.append(" NodeVisits: ").append(this.getNumberOfNodeVisits()); output.append(" ModifiedTime: ").append(this.getModifiedTimeStamp()); output.append(" NumberOfElements: ").append(this.getNumberOfElements()); output.append(" CurrentlyInUse: ").append(this.isCurrentlyInUse()); return output.toString(); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/eviction/EvictionTimerTask.java0000755000175000017500000000735011012076160030472 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. * Created on March 25 2003 */ package org.jboss.cache.eviction; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.Region; import java.util.Collections; import java.util.HashSet; import java.util.Set; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.atomic.AtomicInteger; /** * Timer threads to do periodic node clean up by running the eviction policy. * * @author Ben Wang 2-2004 * @author Daniel Huang (dhuang@jboss.org) * @version $Revision: 5831 $ */ public class EvictionTimerTask { private Log log = LogFactory.getLog(EvictionTimerTask.class); private final Set processedRegions; private static AtomicInteger tcount = new AtomicInteger(); private int wakeupIntervalSeconds; private Timer evictionThread; public EvictionTimerTask() { // synchronized set because we need to maintain thread safety // for dynamic configuration purposes. processedRegions = Collections.synchronizedSet(new HashSet()); } public void init(int wakeupIntervalSeconds) { if (log.isTraceEnabled()) log.trace("Creating a new eviction listener with wakeupIntervalSeconds set at " + wakeupIntervalSeconds); this.wakeupIntervalSeconds = wakeupIntervalSeconds; start(); } /** * Add a MarshRegion to process by the Eviction Thread. * * @param region MarshRegion to process. */ public void addRegionToProcess(Region region) { processedRegions.add(region); } /** * Remove a MarshRegion to process from the Eviction thread. * * @param region */ public void removeRegionToProcess(Region region) { processedRegions.remove(region); } public boolean isRegionRegisteredForProcessing(Region region) { return processedRegions.contains(region); } public Set getProcessedRegions() { return processedRegions; } public void stop() { log.debug("Stopping eviction timer"); if (evictionThread != null) { evictionThread.cancel(); } evictionThread = null; } private void start() { evictionThread = new Timer("EvictionTimer-" + tcount.getAndIncrement(), true); TimerTask tt = new TimerTask() { /** * Run the eviction thread. *

        * This thread will synchronize the set of regions and iterate through every MarshRegion registered w/ the * Eviction thread. It also synchronizes on each individual region as it is being processed. */ public void run() { processRegions(); } }; evictionThread.schedule(tt, wakeupIntervalSeconds * 1000, wakeupIntervalSeconds * 1000); } private void processRegions() { synchronized (processedRegions) { for (Region region : processedRegions) { handleRegion(region); } } } private void handleRegion(Region region) { synchronized (region) { final EvictionPolicy policy = region.getEvictionPolicy(); final EvictionAlgorithm algo = policy.getEvictionAlgorithm(); if (algo == null) throw new NullPointerException("algorithm null"); try { algo.process(region); } catch (EvictionException e) { log.error("run(): error processing eviction with exception: " + e.toString() + " will reset the eviction queue list."); region.resetEvictionQueues(); log.debug("trace", e); } } } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/eviction/LFUAlgorithm.java0000644000175000017500000000563111006101071027351 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.eviction; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.Region; /** * Least Frequently Used algorithm for cache eviction. * Note that this algorithm is not thread-safe. *

        * This algorithm relies on maxNodes and minNodes to operate correctly. * Eviction takes place using Least Frequently Used algorithm. A node A * that is used less than a node B is evicted sooner. *

        * The minNodes property defines a threshold for eviction. If minNodes = 100, * the LFUAlgorithm will not evict the cache to anything less than 100 elements * still left in cache. The maxNodes property defines the maximum number of nodes * the cache will accept before eviction. maxNodes = 0 means that this region is * unbounded. minNodes = 0 means that the eviction queue will attempt to bring * the cache of this region to 0 elements (evict all elements) whenever it is run. *

        * This algorithm uses a sorted eviction queue. The eviction queue is sorted in * ascending order based on the number of times a node is visited. The more frequently * a node is visited, the less likely it will be evicted. * * @author Daniel Huang - dhuang@jboss.org 10/2005 * @version $Revision: 5772 $ */ public class LFUAlgorithm extends BaseSortedEvictionAlgorithm { private static final Log log = LogFactory.getLog(LFUAlgorithm.class); private static final boolean trace = log.isTraceEnabled(); @Override protected boolean shouldEvictNode(NodeEntry ne) { if (trace) { log.trace("Deciding whether node in queue " + ne.getFqn() + " requires eviction."); } // check the minimum time to live and see if we should not evict the node. This check will // ensure that, if configured, nodes are kept alive for at least a minimum period of time. if (isYoungerThanMinimumTimeToLive(ne)) return false; LFUConfiguration config = (LFUConfiguration) region.getEvictionPolicyConfig(); int size = this.getEvictionQueue().getNumberOfNodes(); if (config.getMaxNodes() != 0 && size > config.getMaxNodes()) { return true; } else if (size > config.getMinNodes()) { return true; } return false; } /** * Will create a LFUQueue to be used as the underlying eviction queue. * * @param region MarshRegion to create the eviction queue for. * @return The created LFUQueue. * @throws EvictionException */ @Override protected EvictionQueue setupEvictionQueue(Region region) throws EvictionException { return new LFUQueue(); } @Override protected void prune() throws EvictionException { super.prune(); // clean up the Queue's eviction removals ((LFUQueue) this.evictionQueue).prune(); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/eviction/FIFOPolicy.java0000755000175000017500000000135311005374170026772 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.eviction; /** * Eviction policy based on the FIFO algorithm where users can specify the max * number of nodes. * * @author Daniel Huang (dhuang@jboss.org) * @author Morten Kvistgaard * @version $Revision: 5730 $ */ public class FIFOPolicy extends BaseEvictionPolicy { private FIFOAlgorithm algorithm; public FIFOPolicy() { super(); algorithm = new FIFOAlgorithm(); } public EvictionAlgorithm getEvictionAlgorithm() { return algorithm; } public Class getEvictionConfigurationClass() { return FIFOConfiguration.class; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/eviction/LFUConfiguration.java0000644000175000017500000000341411032145203030233 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.eviction; import org.jboss.cache.config.Dynamic; /** * Configuration implementation for {@link LFUPolicy}. *

        * If configured via XML, expects the following: *

        *

         * 
         *    10
         *    20
         * 
         * 
        * * @author Daniel Huang (dhuang@jboss.org) * @version $Revision: 6120 $ */ public class LFUConfiguration extends EvictionPolicyConfigBase { /** * The serialVersionUID */ private static final long serialVersionUID = 1865801530398969179L; @Dynamic private int minNodes; @Override protected void setEvictionPolicyClassName() { setEvictionPolicyClass(LFUPolicy.class.getName()); } public int getMinNodes() { return minNodes; } public void setMinNodes(int minNodes) { testImmutability("minNodes"); this.minNodes = minNodes; } @Override public String toString() { StringBuilder ret = new StringBuilder(); ret.append("LFUConfiguration: maxNodes = ").append(getMaxNodes()).append(" minNodes = ").append(getMinNodes()); return ret.toString(); } @Override public boolean equals(Object obj) { if (super.equals(obj)) { return (this.minNodes == ((LFUConfiguration) obj).minNodes); } return false; } @Override public int hashCode() { int result = super.hashCode(); result = 31 * result + minNodes; return result; } @Override public LFUConfiguration clone() throws CloneNotSupportedException { return (LFUConfiguration) super.clone(); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/eviction/ElementSizeAlgorithm.java0000644000175000017500000000242011004125045031145 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.eviction; import org.jboss.cache.Region; /** * @author Daniel Huang * @version $Revision: 5671 $ */ public class ElementSizeAlgorithm extends BaseSortedEvictionAlgorithm { @Override protected EvictionQueue setupEvictionQueue(Region region) throws EvictionException { return new ElementSizeQueue(); } @Override protected boolean shouldEvictNode(NodeEntry ne) { // check the minimum time to live and see if we should not evict the node. This check will // ensure that, if configured, nodes are kept alive for at least a minimum period of time. if (isYoungerThanMinimumTimeToLive(ne)) return false; ElementSizeConfiguration config = (ElementSizeConfiguration) region.getEvictionPolicyConfig(); int size = this.getEvictionQueue().getNumberOfNodes(); return config.getMaxNodes() != 0 && size > config.getMaxNodes() || ne.getNumberOfElements() > config.getMaxElementsPerNode(); } @Override protected void prune() throws EvictionException { super.prune(); // clean up the Queue's eviction removals ((ElementSizeQueue) this.evictionQueue).prune(); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/eviction/LRUAlgorithm.java0000755000175000017500000001160411032145203027371 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. * Created on March 25 2003 */ package org.jboss.cache.eviction; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.Region; import java.util.Iterator; /** * Least recently Used algorithm to purge old data. * Note that this algorithm is not thread-safe. * * @author Ben Wang 02-2004 * @author Daniel Huang - dhuang@jboss.org */ public class LRUAlgorithm extends BaseEvictionAlgorithm { private static final Log log = LogFactory.getLog(LRUAlgorithm.class); private static final boolean trace = log.isTraceEnabled(); @Override protected EvictionQueue setupEvictionQueue(Region region) throws EvictionException { return new LRUQueue(); } @Override protected boolean shouldEvictNode(NodeEntry entry) { // check the minimum time to live and see if we should not evict the node. This check will // ensure that, if configured, nodes are kept alive for at least a minimum period of time. if (isYoungerThanMinimumTimeToLive(entry)) return false; LRUConfiguration config = (LRUConfiguration) region.getEvictionPolicyConfig(); // no idle or max time limit if (config.getTimeToLiveSeconds() == 0 && config.getMaxAgeSeconds() == 0) return false; long currentTime = System.currentTimeMillis(); if (config.getTimeToLiveSeconds() != 0) { long idleTime = currentTime - entry.getModifiedTimeStamp(); if (trace) { log.trace("Node " + entry.getFqn() + " has been idle for " + idleTime + "ms"); } if ((idleTime >= (config.getTimeToLiveSeconds() * 1000))) { if (trace) { log.trace("Node " + entry.getFqn() + " should be evicted because of idle time"); log.trace("Time to live in millies is: " + (config.getTimeToLiveSeconds() * 1000)); log.trace("Config instance is: " + System.identityHashCode(config)); } return true; } } if (config.getMaxAgeSeconds() != 0) { long objectLifeTime = currentTime - entry.getCreationTimeStamp(); if (trace) { log.trace("Node " + entry.getFqn() + " has been alive for " + objectLifeTime + "ms"); } if ((objectLifeTime >= (config.getMaxAgeSeconds() * 1000))) { if (trace) { log.trace("Node " + entry.getFqn() + " should be evicted because of max age"); } return true; } } if (trace) { log.trace("Node " + entry.getFqn() + " should not be evicted"); } return false; } @Override protected void evict(NodeEntry ne) { if (ne != null && !this.evictCacheNode(ne.getFqn())) { try { recycleQueue.put(ne.getFqn()); } catch (InterruptedException e) { log.debug("InterruptedException", e); } } } @Override protected void prune() throws EvictionException { LRUQueue lruQueue = (LRUQueue) evictionQueue; NodeEntry ne; Iterator it = lruQueue.iterateLRUQueue(); while (it.hasNext()) { ne = (NodeEntry) it.next(); if (isNodeInUseAndNotTimedOut(ne)) { continue; } if (this.shouldEvictNode(ne)) { it.remove(); lruQueue.removeNodeEntryFromMaxAge(ne); this.evict(ne); } else { break; } } it = lruQueue.iterateMaxAgeQueue(); while (it.hasNext()) { ne = (NodeEntry) it.next(); if (isNodeInUseAndNotTimedOut(ne)) { continue; } if (this.shouldEvictNode(ne)) { it.remove(); lruQueue.removeNodeEntryFromLRU(ne); this.evict(ne); } else { break; } } int maxNodes = this.getConfiguration().getMaxNodes(); if (maxNodes <= 0) { return; } it = lruQueue.iterateLRUQueue(); while (evictionQueue.getNumberOfNodes() > maxNodes) { ne = (NodeEntry) it.next(); if (trace) { log.trace("Node " + ne.getFqn() + " will be evicted because of exceeding the maxNode limit." + " maxNode: " + maxNodes + " but current queue size is: " + evictionQueue.getNumberOfNodes()); } if (!this.isNodeInUseAndNotTimedOut(ne)) { it.remove(); lruQueue.removeNodeEntryFromMaxAge(ne); this.evict(ne); } } } protected LRUConfiguration getConfiguration() { return (LRUConfiguration) region.getEvictionPolicyConfig(); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/eviction/NullEvictionPolicy.java0000755000175000017500000000316311004116445030661 0ustar twernertwerner/** * */ package org.jboss.cache.eviction; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.config.EvictionPolicyConfig; /** * Eviction policy that does nothing and always tells the eviction * interceptor an event can be ignored, saving the overhead of * constructing and processing event objects. Basically useful * as a default policy for a cache or subtree that is * shared between multiple usages, some of which don't * want eviction. * * @author Brian Stansberry */ public class NullEvictionPolicy implements EvictionPolicy { private static final Log log = LogFactory.getLog(NullEvictionPolicy.class); private CacheSPI cache; /** * Returns true */ public boolean canIgnoreEvent(Fqn fqn, NodeEventType eventType) { return true; } /** * No-op */ public void evict(Fqn fqn) throws Exception { log.debug("evict should not be called on NullEvictionPolicy"); } /** * Returns {@link NullEvictionAlgorithm#INSTANCE}. */ public EvictionAlgorithm getEvictionAlgorithm() { return NullEvictionAlgorithm.INSTANCE; } /** * Returns {@link NullEvictionPolicyConfig}. */ public Class getEvictionConfigurationClass() { return NullEvictionPolicyConfig.class; } public CacheSPI getCache() { return cache; } public void setCache(CacheSPI cache) { this.cache = cache; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/eviction/ElementSizeQueue.java0000644000175000017500000001203411032145203030304 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.eviction; import org.jboss.cache.Fqn; import java.util.Collections; import java.util.Comparator; 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.NoSuchElementException; import java.util.Set; /** * @author Daniel Huang * @version $Revision: 6120 $ */ public class ElementSizeQueue implements SortedEvictionQueue { private Map nodeMap; private LinkedList evictionList; private Comparator comparator; private Set removalQueue; private int numElements = 0; protected ElementSizeQueue() { nodeMap = new HashMap(); evictionList = new LinkedList(); comparator = new MaxElementComparator(); removalQueue = new HashSet(); } public void resortEvictionQueue() { Collections.sort(evictionList, comparator); } public NodeEntry getFirstNodeEntry() { try { NodeEntry ne; while ((ne = evictionList.getFirst()) != null) { if (removalQueue.contains(ne)) { evictionList.removeFirst(); removalQueue.remove(ne); } else { break; } } return ne; } catch (NoSuchElementException e) { return null; } } public NodeEntry getNodeEntry(Fqn fqn) { return nodeMap.get(fqn); } public NodeEntry getNodeEntry(String fqn) { return this.getNodeEntry(Fqn.fromString(fqn)); } public boolean containsNodeEntry(NodeEntry entry) { Fqn fqn = entry.getFqn(); return this.getNodeEntry(fqn) != null; } public void removeNodeEntry(NodeEntry entry) { NodeEntry ne = nodeMap.remove(entry.getFqn()); if (ne != null) { // don't remove directly from the LinkedList otherwise we will incur a O(n) = n // performance penalty for every removal! In the prune method for LFU, we will iterate the LinkedList through ONCE // doing a single O(n) = n operation and removal. This is much preferred over running O(n) = n every single time // remove is called. There is also special logic in the getFirstNodeEntry that will know to check // the removalQueue before returning. this.removalQueue.add(ne); /* if(!evictionList.remove(ne)) { throw new RuntimeException(""); } */ this.numElements -= ne.getNumberOfElements(); } } public void addNodeEntry(NodeEntry entry) { if (!this.containsNodeEntry(entry)) { Fqn fqn = entry.getFqn(); entry.queue = this; nodeMap.put(fqn, entry); evictionList.add(entry); this.numElements += entry.getNumberOfElements(); } } public int getNumberOfNodes() { return nodeMap.size(); } public int getNumberOfElements() { return this.numElements; } public void modifyElementCount(int difference) { this.numElements += difference; } public void clear() { nodeMap.clear(); evictionList.clear(); removalQueue.clear(); this.numElements = 0; } protected final List getEvictionList() { return evictionList; } protected final Set getRemovalQueue() { return removalQueue; } protected final void prune() { Iterator it = evictionList.iterator(); while (it.hasNext() && removalQueue.size() > 0) { if (removalQueue.remove(it.next())) { it.remove(); } } } public Iterator iterate() { return evictionList.iterator(); } /** * Comparator class for Max Elements. *

        * This class will sort the eviction queue in the correct eviction order. * The top of the list should evict before the bottom of the list. *

        * The sort is based on descending order of numElements. *

        * Note: this class has a natural ordering that is inconsistent with equals as defined by the java.lang.Comparator * contract. */ protected static class MaxElementComparator implements Comparator { public int compare(NodeEntry ne1, NodeEntry ne2) { if (ne1.equals(ne2)) { return 0; } int neNumElements = ne1.getNumberOfElements(); int neNumElements2 = ne2.getNumberOfElements(); if (neNumElements > neNumElements2) { return -1; } else if (neNumElements < neNumElements2) { return 1; } else if (neNumElements == neNumElements2) { return 0; } throw new RuntimeException("Should never reach this condition"); } } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/eviction/FIFOConfiguration.java0000644000175000017500000000340711032145203030332 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.eviction; import org.jboss.cache.config.ConfigurationException; /** * Configuration for {@link FIFOPolicy}. *

        * If configured via XML, expects the following: *

        *

         * 
         *   1000
         * 
         * 
        *

        * Requires a "maxNodes" attribute otherwise a ConfigurationException is thrown. * * @author Daniel Huang (dhuang@jboss.org) * @version $Revision: 6120 $ */ public class FIFOConfiguration extends EvictionPolicyConfigBase { /** * The serialVersionUID */ private static final long serialVersionUID = -7229715009546277313L; public FIFOConfiguration() { super(); // We require that maxNodes is set setMaxNodes(-1); } /** * Requires a positive maxNodes value or ConfigurationException * is thrown. */ @Override public void validate() throws ConfigurationException { if (getMaxNodes() < 0) { throw new ConfigurationException("maxNodes must be must be " + "configured to a value greater than or equal to 0"); } } @Override public String toString() { StringBuilder ret = new StringBuilder(); ret.append("LFUConfiguration: maxNodes = ").append(getMaxNodes()); return ret.toString(); } @Override protected void setEvictionPolicyClassName() { setEvictionPolicyClass(FIFOPolicy.class.getName()); } @Override public void reset() { setMaxNodes(-1); } @Override public FIFOConfiguration clone() throws CloneNotSupportedException { return (FIFOConfiguration) super.clone(); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/eviction/EvictionQueue.java0000644000175000017500000000445011005374170027652 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.eviction; import org.jboss.cache.Fqn; import java.util.Iterator; /** * Eviction Queue interface defines a contract for the Eviction Queue implementations used by EvictionPolicies. *

        * Note: None of the Eviction classes are thread safe. It is assumed that an individual instance of an EvictionPolicy/ * EvictionAlgorithm/EvictionQueue/EvictionConfiguration are only operated on by one thread at any given time. * * @author Daniel Huang (dhuang@jboss.org) * @version $Revision: 5730 $ */ public interface EvictionQueue { /** * Get the first entry in the queue. *

        * If there are no entries in queue, this method will return null. *

        * The first node returned is expected to be the first node to evict. * * @return first NodeEntry in queue. */ NodeEntry getFirstNodeEntry(); /** * Retrieve a node entry by Fqn. *

        * This will return null if the entry is not found. * * @param fqn Fqn of the node entry to retrieve. * @return Node Entry object associated with given Fqn param. */ NodeEntry getNodeEntry(Fqn fqn); NodeEntry getNodeEntry(String fqn); /** * Check if queue contains the given NodeEntry. * * @param entry NodeEntry to check for existence in queue. * @return true/false if NodeEntry exists in queue. */ boolean containsNodeEntry(NodeEntry entry); /** * Remove a NodeEntry from queue. *

        * If the NodeEntry does not exist in the queue, this method will return normally. * * @param entry The NodeEntry to remove from queue. */ void removeNodeEntry(NodeEntry entry); /** * Add a NodeEntry to the queue. * * @param entry The NodeEntry to add to queue. */ void addNodeEntry(NodeEntry entry); /** * Get the number of nodes in the queue. * * @return The number of nodes in the queue. */ int getNumberOfNodes(); /** * Get the number of elements in the queue. * * @return The number of elements in the queue. */ int getNumberOfElements(); void modifyElementCount(int difference); Iterator iterate(); /** * Clear the queue. */ void clear(); } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/eviction/MRUQueue.java0000644000175000017500000000673611032145203026537 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.eviction; import org.jboss.cache.Fqn; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.NoSuchElementException; /** * MRU Eviction Queue implementation. *

        * This nodeMap is sorted by MRU. The first entry in the nodeMap * will also be the most recently used entry. The sort is implicit * based on a Stack that we can implicitly sort to the top by moving * a node that is used to the top of the eviction stack. * * @author Daniel Huang (dhuang@jboss.org) * @version $Revision: 6120 $ */ public class MRUQueue implements EvictionQueue { // we use our own Stack/Linked List implementation here because it guarantees O(n) = 1 for add, remove, get and // we can sort it in order of MRU implicitly while still getting O(n) = 1 access time // throughout. Map nodeMap; EvictionQueueList list; private int numElements = 0; protected MRUQueue() { nodeMap = new HashMap(); list = new EvictionQueueList(); } /** * This call moves a NodeEntry to the top of the stack. *

        * When a node is visited this method should be called to guarantee MRU ordering. * * @param fqn Fqn of the nodeEntry to move to the top of the stack. */ protected void moveToTopOfStack(Fqn fqn) { EvictionListEntry le = nodeMap.remove(fqn); if (le != null) { list.remove(le); list.addToTop(le); nodeMap.put(le.node.getFqn(), le); } } /** * Will return the first entry in the nodeMap. *

        * The first entry in this nodeMap will also be the most recently used entry. * * @return The first node entry in nodeMap. */ public NodeEntry getFirstNodeEntry() { try { return list.getFirst().node; } catch (NoSuchElementException e) { return null; } } public NodeEntry getNodeEntry(Fqn fqn) { EvictionListEntry le = nodeMap.get(fqn); if (le != null) return le.node; return null; } public NodeEntry getNodeEntry(String fqn) { return this.getNodeEntry(Fqn.fromString(fqn)); } public boolean containsNodeEntry(NodeEntry entry) { return nodeMap.containsKey(entry.getFqn()); } public void removeNodeEntry(NodeEntry entry) { EvictionListEntry le = nodeMap.remove(entry.getFqn()); if (le != null) { list.remove(le); this.numElements -= le.node.getNumberOfElements(); } } public void addNodeEntry(NodeEntry entry) { if (!this.containsNodeEntry(entry)) { entry.queue = this; EvictionListEntry le = new EvictionListEntry(entry); list.addToBottom(le); nodeMap.put(entry.getFqn(), le); this.numElements += entry.getNumberOfElements(); } } public int getNumberOfNodes() { return list.size(); } public int getNumberOfElements() { return this.numElements; } public void modifyElementCount(int difference) { this.numElements += difference; } public void clear() { nodeMap.clear(); list.clear(); this.numElements = 0; } public Iterator iterate() { return list.iterator(); } @Override public String toString() { return list.toString(); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/CacheManager.java0000755000175000017500000000642410713760467025566 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache; import java.util.Set; import org.jboss.cache.config.Configuration; import org.jgroups.ChannelFactory; /** * Factory and registry for JBoss Cache instances configured using * named configurations. * * @author Brian Stansberry * @version $Revision: 1 $ */ public interface CacheManager { /** * Gets the names of all the configurations of which this object * is aware. * * @return a set of names. Will not be null. */ Set getConfigurationNames(); /** * Gets the name of all caches that are registered, i.e. that can be * by a call to {@link #getCache(String, boolean) getCache(name, false)}. * * @return a set of names. Will not be null. */ Set getCacheNames(); /** * Gets the JGroups ChannelFactory that will be injected * into any {@link Configuration} that has a * {@link Configuration#getMultiplexerStack() multiplexer stack} * configured. * * @return */ ChannelFactory getChannelFactory(); /** * Get a cache configured according to the given configuration name, * optionally instantiating the cache if it hasn't already been * instantiated. *

        * The caller is free to invoke the {@link Cache#create()} and * {@link Cache#start()} lifecycle methods on the returned cache, but * the @link Cache#stop()} and {@link Cache#destroy()} methods should not * be invoked, since it is quite possible other users are still using the * cache. Use {@link #releaseCache(String)} to notify this * registry that the caller is no longer using a cache; let the registry * control stopping and destroying the cache. *

        *

        * If invoking this method leads to the instantiation of the cache, * create() and start() will not be invoked * on the cache before it is returned. *

        * * @param configName the name of the configuration * @param create should the cache be instantiated if it * hasn't already been? * @return the cache, or null if * create is false and the cache hasn't * been created previously. * * @throws IllegalArgumentException if this object is unaware of * configName; i.e. the set * returned from {@link #getConfigurationNames()} * does not include configName * @throws Exception if there is a problem instantiating the cache */ Cache getCache(String configName, boolean create) throws Exception; /** * Notifies the registry that the caller is no longer using the given * cache. The registry may perform cleanup operations, such as * stopping and destroying the cache. * * @param configName */ void releaseCache(String configName); }jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/config/0000755000175000017500000000000011376174026023655 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/config/PluggableConfigurationComponent.java0000644000175000017500000000522211017524574033035 0ustar twernertwernerpackage org.jboss.cache.config; import org.jboss.cache.xml.XmlHelper; import java.io.ByteArrayInputStream; import java.io.IOException; import java.util.Properties; /** * A configuration component where the implementation class can be specified, and comes with its own set of properties. * * @author Manik Surtani (manik@jboss.org) * @since 2.2.0 */ public abstract class PluggableConfigurationComponent extends ConfigurationComponent { protected String className; protected Properties properties; public String getClassName() { return className; } public void setClassName(String className) { if (className == null || className.length() == 0) return; testImmutability("className"); this.className = className; } public Properties getProperties() { return properties; } public void setProperties(Properties properties) { testImmutability("properties"); this.properties = properties; } public void setProperties(String properties) throws IOException { if (properties == null) return; testImmutability("properties"); // JBCACHE-531: escape all backslash characters // replace any "\" that is not preceded by a backslash with "\\" properties = XmlHelper.escapeBackslashes(properties); ByteArrayInputStream is = new ByteArrayInputStream(properties.trim().getBytes("ISO8859_1")); this.properties = new Properties(); this.properties.load(is); is.close(); } public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; PluggableConfigurationComponent that = (PluggableConfigurationComponent) o; if (className != null ? !className.equals(that.className) : that.className != null) return false; if (properties != null ? !properties.equals(that.properties) : that.properties != null) return false; return true; } public int hashCode() { int result; result = (className != null ? className.hashCode() : 0); result = 31 * result + (properties != null ? properties.hashCode() : 0); return result; } @Override public String toString() { return getClass().getSimpleName() + " {className = " + className + ", properties=" + properties + "}"; } @Override public PluggableConfigurationComponent clone() throws CloneNotSupportedException { PluggableConfigurationComponent clone = (PluggableConfigurationComponent) super.clone(); if (properties != null) clone.properties = (Properties) properties.clone(); return clone; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/config/BuddyReplicationConfig.java0000644000175000017500000001445411017273341031107 0ustar twernertwerner/* * JBoss, Home of Professional Open Source. * Copyright 2006, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.cache.config; import org.jboss.cache.buddyreplication.NextMemberBuddyLocator; import org.jboss.cache.util.Util; import java.util.Properties; public class BuddyReplicationConfig extends ConfigurationComponent { private static final long serialVersionUID = -4826380823985089339L; /** * Test whether buddy replication is enabled. */ private boolean enabled; /** * Name of the buddy pool for current instance. May be null if buddy pooling is not used. */ private String buddyPoolName; private boolean autoDataGravitation = true; private boolean dataGravitationRemoveOnFind = true; private boolean dataGravitationSearchBackupTrees = true; @Dynamic private int buddyCommunicationTimeout = 10000; private BuddyLocatorConfig buddyLocatorConfig; public boolean isAutoDataGravitation() { return autoDataGravitation; } public void setAutoDataGravitation(boolean autoDataGravitation) { testImmutability("autoDataGravitation"); this.autoDataGravitation = autoDataGravitation; } public int getBuddyCommunicationTimeout() { return buddyCommunicationTimeout; } public void setBuddyCommunicationTimeout(int buddyCommunicationTimeout) { testImmutability("buddyCommunicationTimeout"); this.buddyCommunicationTimeout = buddyCommunicationTimeout; } public String getBuddyPoolName() { return buddyPoolName; } public void setBuddyPoolName(String buddyPoolName) { testImmutability("buddyPoolName"); this.buddyPoolName = buddyPoolName; } public boolean isDataGravitationRemoveOnFind() { return dataGravitationRemoveOnFind; } public void setDataGravitationRemoveOnFind(boolean dataGravitationRemoveOnFind) { testImmutability("dataGravitationRemoveOnFind"); this.dataGravitationRemoveOnFind = dataGravitationRemoveOnFind; } public boolean isDataGravitationSearchBackupTrees() { return dataGravitationSearchBackupTrees; } public void setDataGravitationSearchBackupTrees(boolean dataGravitationSearchBackupTrees) { testImmutability("dataGravitationSearchBackupTrees"); this.dataGravitationSearchBackupTrees = dataGravitationSearchBackupTrees; } public boolean isEnabled() { return enabled; } public void setEnabled(boolean enabled) { testImmutability("enabled"); this.enabled = enabled; } public BuddyLocatorConfig getBuddyLocatorConfig() { return buddyLocatorConfig; } public void setBuddyLocatorConfig(BuddyLocatorConfig buddyLocatorConfig) { testImmutability("buddyLocatorConfig"); replaceChildConfig(this.buddyLocatorConfig, buddyLocatorConfig); this.buddyLocatorConfig = buddyLocatorConfig; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj instanceof BuddyReplicationConfig) { BuddyReplicationConfig other = (BuddyReplicationConfig) obj; return (this.autoDataGravitation == other.autoDataGravitation) && (this.dataGravitationRemoveOnFind == other.dataGravitationRemoveOnFind) && (this.dataGravitationSearchBackupTrees == other.dataGravitationSearchBackupTrees) && (this.enabled == other.enabled) && (this.buddyCommunicationTimeout == other.buddyCommunicationTimeout) && Util.safeEquals(this.buddyPoolName, other.buddyPoolName) && Util.safeEquals(this.buddyLocatorConfig, other.buddyLocatorConfig); } return false; } @Override public int hashCode() { int result = 11; result = 29 * result + (autoDataGravitation ? 0 : 1); result = 29 * result + (dataGravitationRemoveOnFind ? 0 : 1); result = 29 * result + (dataGravitationSearchBackupTrees ? 0 : 1); result = 29 * result + (enabled ? 0 : 1); result = 29 * result + buddyCommunicationTimeout; result = 29 * result + (buddyPoolName == null ? 0 : buddyPoolName.hashCode()); result = 29 * result + (buddyLocatorConfig == null ? 0 : buddyLocatorConfig.hashCode()); return result; } @Override public BuddyReplicationConfig clone() throws CloneNotSupportedException { BuddyReplicationConfig clone = (BuddyReplicationConfig) super.clone(); if (buddyLocatorConfig != null) clone.setBuddyLocatorConfig(buddyLocatorConfig.clone()); return clone; } public static class BuddyLocatorConfig extends PluggableConfigurationComponent { private static final long serialVersionUID = -8003634097931826091L; public BuddyLocatorConfig() { // default className = NextMemberBuddyLocator.class.getName(); } public String getBuddyLocatorClass() { return className; } public void setBuddyLocatorClass(String buddyLocatorClass) { setClassName(buddyLocatorClass); } public Properties getBuddyLocatorProperties() { return properties; } public void setBuddyLocatorProperties(Properties buddyLocatorProperties) { setProperties(buddyLocatorProperties); } public BuddyLocatorConfig clone() throws CloneNotSupportedException { return (BuddyLocatorConfig) super.clone(); } } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/config/ConfigurationException.java0000644000175000017500000000264110554155015031203 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.config; import org.jboss.cache.CacheException; import java.util.ArrayList; import java.util.List; /** * An exception that represents an error in the configuration. This could be a parsing error or a logical error involving clashing * configuration options or missing mandatory configuration elements. * * @author Manik Surtani (manik@jboss.org) * @see org.jboss.cache.config.Configuration * @see org.jboss.cache.CacheFactory */ public class ConfigurationException extends CacheException { private static final long serialVersionUID = -5576382839360927955L; private List erroneousAttributes = new ArrayList(); public ConfigurationException(Exception e) { super(e); } public ConfigurationException(String string) { super(string); } public ConfigurationException(String string, String erroneousAttribute) { super(string); erroneousAttributes.add(erroneousAttribute); } public ConfigurationException(String string, Throwable throwable) { super(string, throwable); } public List getErroneousAttributes() { return erroneousAttributes; } public void addErroneousAttribute(String s) { erroneousAttributes.add(s); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/config/EvictionRegionConfig.java0000644000175000017500000001257311006024704030565 0ustar twernertwerner/* * JBoss, Home of Professional Open Source. * Copyright 2006, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.cache.config; import org.apache.commons.logging.LogFactory; import org.jboss.cache.Fqn; import org.jboss.cache.util.Util; import java.lang.reflect.Method; public class EvictionRegionConfig extends ConfigurationComponent { /** * The serialVersionUID */ private static final long serialVersionUID = -5482474634995601400L; public static final String NAME = "name"; public static final String REGION = "region"; public static final String REGION_POLICY_CLASS = "policyClass"; public static final String EVENT_QUEUE_SIZE = "eventQueueSize"; private Fqn regionFqn; @Dynamic private Integer eventQueueSize; private EvictionPolicyConfig evictionPolicyConfig; public EvictionRegionConfig() { } public EvictionRegionConfig(Fqn regionFqn, EvictionPolicyConfig evictionPolicyConfig) { this.regionFqn = regionFqn; this.evictionPolicyConfig = evictionPolicyConfig; } public EvictionPolicyConfig getEvictionPolicyConfig() { return evictionPolicyConfig; } public void setEvictionPolicyConfig(EvictionPolicyConfig config) { testImmutability("evictionPolicyConfig"); if (this.evictionPolicyConfig instanceof ConfigurationComponent) { removeChildConfig((ConfigurationComponent) this.evictionPolicyConfig); } if (config instanceof ConfigurationComponent) { addChildConfig((ConfigurationComponent) config); } // don't validate here - instead validate when we start things up. See RegionManager.start() this.evictionPolicyConfig = config; } public Fqn getRegionFqn() { return regionFqn; } public void setRegionFqn(Fqn regionFqn) { testImmutability("regionFqn"); this.regionFqn = regionFqn; } public String getRegionName() { return regionFqn == null ? null : regionFqn.toString(); } public void setRegionName(String name) { setRegionFqn(name == null ? null : Fqn.fromString(name)); } public int getEventQueueSize() { return eventQueueSize == null ? EvictionConfig.EVENT_QUEUE_SIZE_DEFAULT : eventQueueSize; } public void setEventQueueSize(int queueSize) { testImmutability("eventQueueSize"); if (queueSize <= 0) { LogFactory.getLog(EvictionRegionConfig.class).warn("Ignoring invalid queue capacity " + queueSize + " -- using " + EvictionConfig.EVENT_QUEUE_SIZE_DEFAULT); queueSize = EvictionConfig.EVENT_QUEUE_SIZE_DEFAULT; } this.eventQueueSize = queueSize; } public void setDefaultEventQueueSize(int queueSize) { if (eventQueueSize == null) setEventQueueSize(queueSize); } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj instanceof EvictionRegionConfig) { EvictionRegionConfig other = (EvictionRegionConfig) obj; return Util.safeEquals(this.regionFqn, other.regionFqn); } return false; } @Override public int hashCode() { int result = 17; result = 31 * result + (regionFqn == null ? 0 : regionFqn.hashCode()); return result; } @Override public EvictionRegionConfig clone() throws CloneNotSupportedException { EvictionRegionConfig clone = (EvictionRegionConfig) super.clone(); if (evictionPolicyConfig != null) { if (!(evictionPolicyConfig instanceof Cloneable)) { throw new CloneNotSupportedException(evictionPolicyConfig + " is not Cloneable"); } if (evictionPolicyConfig instanceof ConfigurationComponent) { clone.setEvictionPolicyConfig((EvictionPolicyConfig) ((ConfigurationComponent) evictionPolicyConfig).clone()); } else { try { Method cloneMethod = evictionPolicyConfig.getClass().getDeclaredMethod("clone"); EvictionPolicyConfig epc = (EvictionPolicyConfig) cloneMethod.invoke(evictionPolicyConfig); clone.setEvictionPolicyConfig(epc); } catch (Exception e) { CloneNotSupportedException cnse = new CloneNotSupportedException("Cannot invoke clone() on " + evictionPolicyConfig); cnse.initCause(e); throw cnse; } } } return clone; } }jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/config/Configuration.java0000644000175000017500000006711611041076631027332 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.config; import org.jboss.cache.Version; import org.jboss.cache.factories.XmlConfigurationParser; import org.jboss.cache.factories.annotations.NonVolatile; import org.jboss.cache.lock.IsolationLevel; import org.jboss.cache.marshall.Marshaller; import org.w3c.dom.Element; import java.net.URL; import java.util.Locale; /** * Encapsulates the configuration of a Cache. * * @author Manik Surtani (manik@jboss.org) */ @NonVolatile public class Configuration extends ConfigurationComponent { private static final long serialVersionUID = 5553791890144997466L; private Marshaller marshaller; public void setCacheMarshaller(Marshaller instance) { marshaller = instance; } public Marshaller getMarshaller() { return marshaller; } /** * Behavior of the JVM shutdown hook registered by the cache */ public static enum ShutdownHookBehavior { /** * By default a shutdown hook is registered if no MBean server (apart from the JDK default) is detected. */ DEFAULT, /** * Forces the cache to register a shutdown hook even if an MBean server is detected. */ REGISTER, /** * Forces the cache NOT to register a shutdown hook, even if no MBean server is detected. */ DONT_REGISTER } /** * Cache replication mode. */ public static enum CacheMode { /** * Data is not replicated. */ LOCAL, /** * Data replicated synchronously. */ REPL_SYNC, /** * Data replicated asynchronously. */ REPL_ASYNC, /** * Data invalidated synchronously. */ INVALIDATION_SYNC, /** * Data invalidated asynchronously. */ INVALIDATION_ASYNC; /** * Returns true if the mode is invalidation, either sync or async. */ public boolean isInvalidation() { return this == INVALIDATION_SYNC || this == INVALIDATION_ASYNC; } public boolean isSynchronous() { return this == REPL_SYNC || this == INVALIDATION_SYNC || this == LOCAL; } } public static CacheMode legacyModeToCacheMode(int legacyMode) { switch (legacyMode) { case 1: return CacheMode.LOCAL; case 2: return CacheMode.REPL_ASYNC; case 3: return CacheMode.REPL_SYNC; case 4: return CacheMode.INVALIDATION_ASYNC; case 5: return CacheMode.INVALIDATION_SYNC; default: throw new IllegalArgumentException("Unknown legacy cache mode " + legacyMode); } } /** * Cache node locking scheme. */ public static enum NodeLockingScheme { /** * Data is exclusively locked during modification. * * @see http://en.wikipedia.org/wiki/Concurrency_control (pessimistic) */ PESSIMISTIC, /** * Data is unlocked during modification, modifications merged at commit. * * @see http://en.wikipedia.org/wiki/Optimistic_concurrency_control */ OPTIMISTIC } /** * Default replication version, from {@link Version#getVersionShort}. */ public static final short DEFAULT_REPLICATION_VERSION = Version.getVersionShort(); // ------------------------------------------------------------------------------------------------------------ // CONFIGURATION OPTIONS // ------------------------------------------------------------------------------------------------------------ private String clusterName = "JBossCache-Cluster"; private String clusterConfig = null; private boolean useReplQueue = false; @Dynamic private int replQueueMaxElements = 1000; @Dynamic private long replQueueInterval = 5000; private boolean exposeManagementStatistics = true; @Dynamic private boolean fetchInMemoryState = true; private short replicationVersion = DEFAULT_REPLICATION_VERSION; @Dynamic private long lockAcquisitionTimeout = 10000; @Dynamic private long syncReplTimeout = 15000; private CacheMode cacheMode = CacheMode.LOCAL; private boolean inactiveOnStartup = false; @Dynamic private long stateRetrievalTimeout = 10000; private IsolationLevel isolationLevel = IsolationLevel.REPEATABLE_READ; @Dynamic private boolean lockParentForChildInsertRemove = false; @Dynamic private EvictionConfig evictionConfig = null; private boolean useRegionBasedMarshalling = false; private String transactionManagerLookupClass = null; private CacheLoaderConfig cacheLoaderConfig = null; @Dynamic private boolean syncCommitPhase = false; @Dynamic private boolean syncRollbackPhase = false; private BuddyReplicationConfig buddyReplicationConfig; private boolean nodeLockingOptimistic = false; private NodeLockingScheme nodeLockingScheme = NodeLockingScheme.PESSIMISTIC; private String muxStackName = null; private boolean usingMultiplexer = false; private transient RuntimeConfig runtimeConfig; private String marshallerClass; private ShutdownHookBehavior shutdownHookBehavior = ShutdownHookBehavior.DEFAULT; private boolean useLazyDeserialization = false; private int objectInputStreamPoolSize = 50; private int objectOutputStreamPoolSize = 50; // ------------------------------------------------------------------------------------------------------------ // SETTERS - MAKE SURE ALL SETTERS PERFORM testImmutability()!!! // ------------------------------------------------------------------------------------------------------------ /** * Converts a list of elements to a Java Groups property string. */ public void setClusterConfig(Element config) { setClusterConfig(XmlConfigurationParser.parseClusterConfigXml(config)); } public void setClusterName(String clusterName) { testImmutability("clusterName"); this.clusterName = clusterName; } public void setClusterConfig(String clusterConfig) { testImmutability("clusterConfig"); this.clusterConfig = clusterConfig; } public void setReplQueueMaxElements(int replQueueMaxElements) { testImmutability("replQueueMaxElements"); this.replQueueMaxElements = replQueueMaxElements; } public void setReplQueueInterval(long replQueueInterval) { testImmutability("replQueueInterval"); this.replQueueInterval = replQueueInterval; } public void setExposeManagementStatistics(boolean useMbean) { testImmutability("exposeManagementStatistics"); this.exposeManagementStatistics = useMbean; } public void setFetchInMemoryState(boolean fetchInMemoryState) { testImmutability("fetchInMemoryState"); this.fetchInMemoryState = fetchInMemoryState; } public void setReplicationVersion(short replicationVersion) { testImmutability("replicationVersion"); this.replicationVersion = replicationVersion; } public void setReplVersionString(String replVersionString) { setReplicationVersion(replVersionString == null ? 0 : Version.getVersionShort(replVersionString)); } public void setLockAcquisitionTimeout(long lockAcquisitionTimeout) { testImmutability("lockAcquisitionTimeout"); this.lockAcquisitionTimeout = lockAcquisitionTimeout; } public void setSyncReplTimeout(long syncReplTimeout) { testImmutability("syncReplTimeout"); this.syncReplTimeout = syncReplTimeout; } public void setCacheMode(CacheMode cacheModeInt) { testImmutability("cacheMode"); this.cacheMode = cacheModeInt; } public void setCacheMode(String cacheMode) { testImmutability("cacheMode"); if (cacheMode == null) throw new ConfigurationException("Cache mode cannot be null", "CacheMode"); this.cacheMode = CacheMode.valueOf(uc(cacheMode)); if (this.cacheMode == null) { log.warn("Unknown cache mode '" + cacheMode + "', using defaults."); this.cacheMode = CacheMode.LOCAL; } } public String getCacheModeString() { return cacheMode == null ? null : cacheMode.toString(); } public void setCacheModeString(String cacheMode) { setCacheMode(cacheMode); } public void setInactiveOnStartup(boolean inactiveOnStartup) { testImmutability("inactiveOnStartup"); this.inactiveOnStartup = inactiveOnStartup; } public EvictionConfig getEvictionConfig() { return evictionConfig; } public void setEvictionConfig(EvictionConfig config) { testImmutability("evictionConfig"); this.evictionConfig = config; } /** * This is a deprecated configuration option. While it will be supported for the 2.x series for backward compatibility, * expect to see it disappear in 3.x. *

        * With {@link #isUseLazyDeserialization()}, which is enabled by default, custom class loaders are handled implicitly. * See the user guide for details on how this is handled. *

        */ @Deprecated public void setUseRegionBasedMarshalling(boolean useRegionBasedMarshalling) { testImmutability("useRegionBasedMarshalling"); this.useRegionBasedMarshalling = useRegionBasedMarshalling; } public void setTransactionManagerLookupClass(String transactionManagerLookupClass) { testImmutability("transactionManagerLookupClass"); this.transactionManagerLookupClass = transactionManagerLookupClass; } public void setCacheLoaderConfig(CacheLoaderConfig config) { testImmutability("cacheLoaderConfig"); replaceChildConfig(this.cacheLoaderConfig, config); this.cacheLoaderConfig = config; } public void setSyncCommitPhase(boolean syncCommitPhase) { testImmutability("syncCommitPhase"); this.syncCommitPhase = syncCommitPhase; } public void setSyncRollbackPhase(boolean syncRollbackPhase) { testImmutability("syncRollbackPhase"); this.syncRollbackPhase = syncRollbackPhase; } public void setBuddyReplicationConfig(BuddyReplicationConfig config) { testImmutability("buddyReplicationConfig"); replaceChildConfig(this.buddyReplicationConfig, config); this.buddyReplicationConfig = config; } public void setNodeLockingScheme(NodeLockingScheme nodeLockingScheme) { testImmutability("nodeLockingScheme"); testImmutability("nodeLockingOptimistic"); this.nodeLockingScheme = nodeLockingScheme; this.nodeLockingOptimistic = (nodeLockingScheme == NodeLockingScheme.OPTIMISTIC); } public void setUseReplQueue(boolean useReplQueue) { testImmutability("useReplQueue"); this.useReplQueue = useReplQueue; } public void setIsolationLevel(IsolationLevel isolationLevel) { testImmutability("isolationLevel"); this.isolationLevel = isolationLevel; } public void setNodeLockingOptimistic(boolean nodeLockingOptimistic) { testImmutability("nodeLockingOptimistic"); this.nodeLockingOptimistic = nodeLockingOptimistic; } public void setStateRetrievalTimeout(long stateRetrievalTimeout) { testImmutability("stateRetrievalTimeout"); this.stateRetrievalTimeout = stateRetrievalTimeout; } public void setNodeLockingScheme(String nodeLockingScheme) { testImmutability("nodeLockingScheme"); testImmutability("nodeLockingOptimistic"); if (nodeLockingScheme == null) { throw new ConfigurationException("Node locking scheme cannot be null", "NodeLockingScheme"); } this.nodeLockingScheme = NodeLockingScheme.valueOf(uc(nodeLockingScheme)); if (this.nodeLockingScheme == null) { log.warn("Unknown node locking scheme '" + nodeLockingScheme + "', using defaults."); this.nodeLockingScheme = NodeLockingScheme.PESSIMISTIC; } this.nodeLockingOptimistic = (this.nodeLockingScheme == NodeLockingScheme.OPTIMISTIC); } public String getNodeLockingSchemeString() { return nodeLockingScheme == null ? null : nodeLockingScheme.toString(); } public void setNodeLockingSchemeString(String nodeLockingScheme) { setNodeLockingScheme(nodeLockingScheme); } private static String uc(String s) { return s.toUpperCase(Locale.ENGLISH); } public void setIsolationLevel(String isolationLevel) { testImmutability("isolationLevel"); if (isolationLevel == null) throw new ConfigurationException("Isolation level cannot be null", "IsolationLevel"); this.isolationLevel = IsolationLevel.valueOf(uc(isolationLevel)); if (this.isolationLevel == null) { log.warn("Unknown isolation level '" + isolationLevel + "', using defaults."); this.isolationLevel = IsolationLevel.REPEATABLE_READ; } } public String getIsolationLevelString() { return isolationLevel == null ? null : isolationLevel.toString(); } public void setIsolationLevelString(String isolationLevel) { setIsolationLevel(isolationLevel); } /** * Sets whether inserting or removing a node requires a write lock * on the node's parent (when pessimistic locking is used.) *

        * The default value is false */ public void setLockParentForChildInsertRemove(boolean lockParentForChildInsertRemove) { testImmutability("lockParentForChildInsertRemove"); this.lockParentForChildInsertRemove = lockParentForChildInsertRemove; } public void setMultiplexerStack(String stackName) { testImmutability("muxStackName"); this.muxStackName = stackName; } public boolean isUsingMultiplexer() { return usingMultiplexer; } public void setUsingMultiplexer(boolean usingMultiplexer) { testImmutability("usingMultiplexer"); this.usingMultiplexer = usingMultiplexer; } public void setShutdownHookBehavior(ShutdownHookBehavior shutdownHookBehavior) { testImmutability("shutdownHookBehavior"); this.shutdownHookBehavior = shutdownHookBehavior; } public void setShutdownHookBehavior(String shutdownHookBehavior) { testImmutability("shutdownHookBehavior"); if (shutdownHookBehavior == null) throw new ConfigurationException("Shutdown hook behavior cannot be null", "ShutdownHookBehavior"); this.shutdownHookBehavior = ShutdownHookBehavior.valueOf(uc(shutdownHookBehavior)); if (this.shutdownHookBehavior == null) { log.warn("Unknown shutdown hook behavior '" + shutdownHookBehavior + "', using defaults."); this.shutdownHookBehavior = ShutdownHookBehavior.DEFAULT; } } public void setUseLazyDeserialization(boolean useLazyDeserialization) { testImmutability("useLazyDeserialization"); this.useLazyDeserialization = useLazyDeserialization; } /** * Initialises the size of the object input stream pool size, which defaults to 50. * * @param objectInputStreamPoolSize * @since 2.1.0 */ public void setObjectInputStreamPoolSize(int objectInputStreamPoolSize) { testImmutability("objectInputStreamPoolSize"); this.objectInputStreamPoolSize = objectInputStreamPoolSize; } /** * Initialises the size of the object output stream pool size, which defaults to 50. * * @param objectOutputStreamPoolSize * @since 2.1.0 */ public void setObjectOutputStreamPoolSize(int objectOutputStreamPoolSize) { testImmutability("objectOutputStreamPoolSize"); this.objectOutputStreamPoolSize = objectOutputStreamPoolSize; } // ------------------------------------------------------------------------------------------------------------ // GETTERS // ------------------------------------------------------------------------------------------------------------ public ShutdownHookBehavior getShutdownHookBehavior() { return this.shutdownHookBehavior; } public boolean isNodeLockingOptimistic() { return nodeLockingOptimistic; } public boolean isUseReplQueue() { return useReplQueue; } public String getClusterName() { return clusterName; } public String getClusterConfig() { return clusterConfig; } public int getReplQueueMaxElements() { return replQueueMaxElements; } public long getReplQueueInterval() { return replQueueInterval; } public boolean getExposeManagementStatistics() { return exposeManagementStatistics; } public boolean isFetchInMemoryState() { return fetchInMemoryState; } public short getReplicationVersion() { return replicationVersion; } public String getReplVersionString() { return Version.getVersionString(replicationVersion); } public long getLockAcquisitionTimeout() { return lockAcquisitionTimeout; } public long getSyncReplTimeout() { return syncReplTimeout; } public CacheMode getCacheMode() { return cacheMode; } public boolean isInactiveOnStartup() { return inactiveOnStartup; } public IsolationLevel getIsolationLevel() { return isolationLevel; } /** * Gets whether inserting or removing a node requires a write lock * on the node's parent (when pessimistic locking is used.) *

        * The default value is false */ public boolean isLockParentForChildInsertRemove() { return lockParentForChildInsertRemove; } /** * This is a deprecated configuration option. While it will be supported for the 2.x series for backward compatibility, * expect to see it disappear in 3.x. *

        * With {@link #isUseLazyDeserialization()}, which is enabled by default, custom class loaders are handled implicitly. * See the user guide for details on how this is handled. *

        */ @Deprecated public boolean isUseRegionBasedMarshalling() { return useRegionBasedMarshalling; } public String getTransactionManagerLookupClass() { return transactionManagerLookupClass; } public CacheLoaderConfig getCacheLoaderConfig() { return cacheLoaderConfig; } public boolean isSyncCommitPhase() { return syncCommitPhase; } public boolean isSyncRollbackPhase() { return syncRollbackPhase; } public BuddyReplicationConfig getBuddyReplicationConfig() { return buddyReplicationConfig; } public NodeLockingScheme getNodeLockingScheme() { return nodeLockingScheme; } public long getStateRetrievalTimeout() { return stateRetrievalTimeout; } public String getMultiplexerStack() { return muxStackName; } public boolean isUseLazyDeserialization() { return useLazyDeserialization; } public synchronized RuntimeConfig getRuntimeConfig() { if (runtimeConfig == null) { setRuntimeConfig(new RuntimeConfig(), false); } return runtimeConfig; } public void setRuntimeConfig(RuntimeConfig runtimeConfig) { setRuntimeConfig(runtimeConfig, true); } private void setRuntimeConfig(RuntimeConfig runtimeConfig, boolean testImmutability) { if (testImmutability) { testImmutability("runtimeConfig"); } this.runtimeConfig = runtimeConfig; } public String getMarshallerClass() { return marshallerClass; } public void setMarshallerClass(String marshallerClass) { this.marshallerClass = marshallerClass; } /** * @return the size of he object input stream pool * @since 2.1.0 */ public int getObjectInputStreamPoolSize() { return objectInputStreamPoolSize; } /** * @return the size of he object output stream pool * @since 2.1.0 */ public int getObjectOutputStreamPoolSize() { return objectOutputStreamPoolSize; } /** * Returns a {@link java.net.URL} to a default JGroups configuration file. * * @return a default JGroups config file */ public URL getDefaultClusterConfig() { URL url = getClass().getClassLoader().getResource("flush-udp.xml"); if (log.isTraceEnabled()) log.trace("Using default JGroups configuration file " + url); return url; } // ------------------------------------------------------------------------------------------------------------ // HELPERS // ------------------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------------------ // OVERRIDDEN METHODS // ------------------------------------------------------------------------------------------------------------ @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Configuration that = (Configuration) o; if (exposeManagementStatistics != that.exposeManagementStatistics) return false; if (fetchInMemoryState != that.fetchInMemoryState) return false; if (inactiveOnStartup != that.inactiveOnStartup) return false; if (lockAcquisitionTimeout != that.lockAcquisitionTimeout) return false; if (lockParentForChildInsertRemove != that.lockParentForChildInsertRemove) return false; if (nodeLockingOptimistic != that.nodeLockingOptimistic) return false; if (objectInputStreamPoolSize != that.objectInputStreamPoolSize) return false; if (objectOutputStreamPoolSize != that.objectOutputStreamPoolSize) return false; if (replQueueInterval != that.replQueueInterval) return false; if (replQueueMaxElements != that.replQueueMaxElements) return false; if (replicationVersion != that.replicationVersion) return false; if (stateRetrievalTimeout != that.stateRetrievalTimeout) return false; if (syncCommitPhase != that.syncCommitPhase) return false; if (syncReplTimeout != that.syncReplTimeout) return false; if (syncRollbackPhase != that.syncRollbackPhase) return false; if (useLazyDeserialization != that.useLazyDeserialization) return false; if (useRegionBasedMarshalling != that.useRegionBasedMarshalling) return false; if (useReplQueue != that.useReplQueue) return false; if (usingMultiplexer != that.usingMultiplexer) return false; if (buddyReplicationConfig != null ? !buddyReplicationConfig.equals(that.buddyReplicationConfig) : that.buddyReplicationConfig != null) return false; if (cacheLoaderConfig != null ? !cacheLoaderConfig.equals(that.cacheLoaderConfig) : that.cacheLoaderConfig != null) return false; if (cacheMode != that.cacheMode) return false; if (clusterConfig != null ? !clusterConfig.equals(that.clusterConfig) : that.clusterConfig != null) return false; if (clusterName != null ? !clusterName.equals(that.clusterName) : that.clusterName != null) return false; if (evictionConfig != null ? !evictionConfig.equals(that.evictionConfig) : that.evictionConfig != null) return false; if (isolationLevel != that.isolationLevel) return false; if (marshaller != null ? !marshaller.equals(that.marshaller) : that.marshaller != null) return false; if (marshallerClass != null ? !marshallerClass.equals(that.marshallerClass) : that.marshallerClass != null) return false; if (muxStackName != null ? !muxStackName.equals(that.muxStackName) : that.muxStackName != null) return false; if (nodeLockingScheme != that.nodeLockingScheme) return false; if (runtimeConfig != null ? !runtimeConfig.equals(that.runtimeConfig) : that.runtimeConfig != null) return false; if (shutdownHookBehavior != that.shutdownHookBehavior) return false; if (transactionManagerLookupClass != null ? !transactionManagerLookupClass.equals(that.transactionManagerLookupClass) : that.transactionManagerLookupClass != null) return false; return true; } @Override public int hashCode() { int result; result = (marshaller != null ? marshaller.hashCode() : 0); result = 31 * result + (clusterName != null ? clusterName.hashCode() : 0); result = 31 * result + (clusterConfig != null ? clusterConfig.hashCode() : 0); result = 31 * result + (useReplQueue ? 1 : 0); result = 31 * result + replQueueMaxElements; result = 31 * result + (int) (replQueueInterval ^ (replQueueInterval >>> 32)); result = 31 * result + (exposeManagementStatistics ? 1 : 0); result = 31 * result + (fetchInMemoryState ? 1 : 0); result = 31 * result + (int) replicationVersion; result = 31 * result + (int) (lockAcquisitionTimeout ^ (lockAcquisitionTimeout >>> 32)); result = 31 * result + (int) (syncReplTimeout ^ (syncReplTimeout >>> 32)); result = 31 * result + (cacheMode != null ? cacheMode.hashCode() : 0); result = 31 * result + (inactiveOnStartup ? 1 : 0); result = 31 * result + (int) (stateRetrievalTimeout ^ (stateRetrievalTimeout >>> 32)); result = 31 * result + (isolationLevel != null ? isolationLevel.hashCode() : 0); result = 31 * result + (lockParentForChildInsertRemove ? 1 : 0); result = 31 * result + (evictionConfig != null ? evictionConfig.hashCode() : 0); result = 31 * result + (useRegionBasedMarshalling ? 1 : 0); result = 31 * result + (transactionManagerLookupClass != null ? transactionManagerLookupClass.hashCode() : 0); result = 31 * result + (cacheLoaderConfig != null ? cacheLoaderConfig.hashCode() : 0); result = 31 * result + (syncCommitPhase ? 1 : 0); result = 31 * result + (syncRollbackPhase ? 1 : 0); result = 31 * result + (buddyReplicationConfig != null ? buddyReplicationConfig.hashCode() : 0); result = 31 * result + (nodeLockingOptimistic ? 1 : 0); result = 31 * result + (nodeLockingScheme != null ? nodeLockingScheme.hashCode() : 0); result = 31 * result + (muxStackName != null ? muxStackName.hashCode() : 0); result = 31 * result + (usingMultiplexer ? 1 : 0); result = 31 * result + (runtimeConfig != null ? runtimeConfig.hashCode() : 0); result = 31 * result + (marshallerClass != null ? marshallerClass.hashCode() : 0); result = 31 * result + (shutdownHookBehavior != null ? shutdownHookBehavior.hashCode() : 0); result = 31 * result + (useLazyDeserialization ? 1 : 0); result = 31 * result + objectInputStreamPoolSize; result = 31 * result + objectOutputStreamPoolSize; return result; } @Override public Configuration clone() throws CloneNotSupportedException { Configuration c = (Configuration) super.clone(); if (buddyReplicationConfig != null) { c.setBuddyReplicationConfig(buddyReplicationConfig.clone()); } if (evictionConfig != null) { c.setEvictionConfig(evictionConfig.clone()); } if (cacheLoaderConfig != null) { c.setCacheLoaderConfig(cacheLoaderConfig.clone()); } if (runtimeConfig != null) { c.setRuntimeConfig(runtimeConfig.clone()); // always make sure we reset the runtime when cloning. c.getRuntimeConfig().reset(); } return c; } public boolean isUsingCacheLoaders() { return getCacheLoaderConfig() != null && !getCacheLoaderConfig().getIndividualCacheLoaderConfigs().isEmpty(); } public boolean isUsingBuddyReplication() { return getBuddyReplicationConfig() != null && getBuddyReplicationConfig().isEnabled() && getCacheMode() != Configuration.CacheMode.LOCAL; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/config/ConfigurationRegistry.java0000755000175000017500000000407310713760467031073 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.config; import java.util.Set; /** * A registry for {@link Configuration}s. * * @author Brian Stansberry * @version $Revision$ */ public interface ConfigurationRegistry { /** * Gets a {@link Configuration#clone() clone} of the {@link Configuration} * registered under the given name. *

        * The returned object is a clone of the internally held configuration, * so any changes made to it by the caller will not affect the internal * state of this registry. * * @param configName the name of the configuration * @return a Configuration. Will not be null. * @throws IllegalArgumentException if no configuration is registered * under configName */ Configuration getConfiguration(String configName) throws Exception; /** * Register the given configuration under the given name. *

        * The configuration will be cloned before being stored internally, * so the copy under the control of the registry will not be affected * by any external changes. * * @param configName the name of the configuration * @param config the configuration * * @throws CloneNotSupportedException * @throws IllegalStateException if a configuration is already registered * under configName */ void registerConfiguration(String configName, Configuration config) throws CloneNotSupportedException; /** * Unregisters the named configuration. * * @param configName the name of the configuration * @throws IllegalStateException if no configuration is registered * under configName */ void unregisterConfiguration(String configName); /** * Gets the names of all registered configurations. * * @return */ Set getConfigurationNames(); }jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/config/EvictionConfig.java0000644000175000017500000001715711012076160027424 0ustar twernertwerner/* * JBoss, Home of Professional Open Source. * Copyright 2006, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.cache.config; import org.jboss.cache.RegionManager; import org.jboss.cache.Fqn; import org.jboss.cache.eviction.EvictionPolicy; import org.jboss.cache.util.Util; import java.util.ArrayList; import java.util.List; public class EvictionConfig extends ConfigurationComponent { /** * The serialVersionUID */ private static final long serialVersionUID = -7979639000026975201L; public static final String WAKEUP_INTERVAL_SECONDS = "wakeUpIntervalSeconds"; public static final int WAKEUP_DEFAULT = 5; public static final String EVENT_QUEUE_SIZE = "eventQueueSize"; public static final String EVICTION_POLICY_CLASS = "policyClass"; public static final int EVENT_QUEUE_SIZE_DEFAULT = 200000; private String defaultEvictionPolicyClass; private int wakeupIntervalSeconds = WAKEUP_DEFAULT; private int defaultEventQueueSize = EVENT_QUEUE_SIZE_DEFAULT; // Dynamic to support runtime adds/removes of regions @Dynamic private List evictionRegionConfigs; public EvictionConfig() { } public EvictionConfig(String defaultEvictionClass) { setDefaultEvictionPolicyClass(defaultEvictionClass); } public boolean isValidConfig() { return (defaultEvictionPolicyClass != null && defaultEvictionPolicyClass.length() > 0) || (evictionRegionConfigs != null && evictionRegionConfigs.size() > 0); } public String getDefaultEvictionPolicyClass() { return defaultEvictionPolicyClass; } public void setDefaultEvictionPolicyClass(String defaultEvictionPolicyClass) { testImmutability("defaultEvictionPolicyClass"); this.defaultEvictionPolicyClass = defaultEvictionPolicyClass; } /** * Creates an EvictionRegionConfig for the * {@link RegionManager.DEFAULT_REGION "_default_"} region using the * {@link #getDefaultEvictionPolicyClass(String) default eviction policy class}. Throws a * {@link ConfigurationException} if * {@link #setDefaultEvictionPolicyClass(String) a default eviction policy class} * has not been set. * * @return an EvictionRegionConfig whose FQN is {@link RegionManager.DEFAULT_REGION} * and whose EvictionPolicyConfig is the default config for the * default eviction policy class. * @throws ConfigurationException if a * {@link #setDefaultEvictionPolicyClass(String) a default eviction policy class} * has not been set or there is a problem instantiating the configuration. */ public EvictionRegionConfig createDefaultEvictionRegionConfig() { if (defaultEvictionPolicyClass != null) { try { Class cpolicy = Class.forName(defaultEvictionPolicyClass); EvictionPolicy policy = (EvictionPolicy) cpolicy.newInstance(); EvictionRegionConfig erc = new EvictionRegionConfig(); EvictionPolicyConfig epc = policy.getEvictionConfigurationClass().newInstance(); erc.setEvictionPolicyConfig(epc); erc.setRegionFqn(RegionManager.DEFAULT_REGION); return erc; } catch (Exception e) { log.error("Unable to create EvictionRegionConfig for default region", e); throw new ConfigurationException("Unable to create EvictionRegionConfig for default region", e); } } else { throw new ConfigurationException("Cannot create EvictionRegionConfig for default region; no defaultEvictionPolicyClass configured"); } } public List getEvictionRegionConfigs() { if (evictionRegionConfigs == null) { return new ArrayList(1); } return evictionRegionConfigs; } public int getDefaultEventQueueSize() { return defaultEventQueueSize; } public void setDefaultEventQueueSize(int eventQueueSize) { this.defaultEventQueueSize = eventQueueSize; } public void setEvictionRegionConfigs(List evictionRegionConfigs) { testImmutability("evictionRegionConfigs"); // Make sure region configs built by MC have the event queue size if (evictionRegionConfigs != null) { for (EvictionRegionConfig cfg : evictionRegionConfigs) { cfg.setDefaultEventQueueSize(getDefaultEventQueueSize()); } } replaceChildConfigs(this.evictionRegionConfigs, evictionRegionConfigs); this.evictionRegionConfigs = evictionRegionConfigs; } public int getWakeupIntervalSeconds() { return wakeupIntervalSeconds; } public void setWakeupIntervalSeconds(int wakeupIntervalSeconds) { testImmutability("wakeupIntervalSeconds"); this.wakeupIntervalSeconds = wakeupIntervalSeconds; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj instanceof EvictionConfig) { EvictionConfig other = (EvictionConfig) obj; return (this.wakeupIntervalSeconds == other.wakeupIntervalSeconds) && Util.safeEquals(this.defaultEvictionPolicyClass, other.defaultEvictionPolicyClass) && Util.safeEquals(this.evictionRegionConfigs, other.evictionRegionConfigs); } return false; } @Override public int hashCode() { int result = 17; result = 37 * result + wakeupIntervalSeconds; result = 37 * result + (defaultEvictionPolicyClass == null ? 0 : defaultEvictionPolicyClass.hashCode()); result = 37 * result + (evictionRegionConfigs == null ? 0 : evictionRegionConfigs.hashCode()); return result; } @Override public EvictionConfig clone() throws CloneNotSupportedException { EvictionConfig clone = (EvictionConfig) super.clone(); if (evictionRegionConfigs != null) { List ercs = new ArrayList(evictionRegionConfigs.size()); for (EvictionRegionConfig erc : evictionRegionConfigs) { ercs.add(erc.clone()); } clone.setEvictionRegionConfigs(ercs); } return clone; } /** * Returns the EvictionRegionConfig coresponding to given region fqn, or null if no * match is found. */ public EvictionRegionConfig getEvictionRegionConfig(String region) { Fqn fqn = Fqn.fromString(region); for (EvictionRegionConfig evConfig : getEvictionRegionConfigs()) { if (evConfig.getRegionFqn().equals(fqn)) { return evConfig; } } return null; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/config/XmlParsingConfigurationRegistry.java0000755000175000017500000000532511010550166033061 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.config; import org.jboss.cache.factories.CacheConfigsXmlParser; import java.util.HashSet; import java.util.Hashtable; import java.util.Map; import java.util.Set; /** * {@link ConfigurationRegistry} that obtains its initial set of configurations * by parsing an XML document. * * @author Brian Stansberry * @version $Revision: 1 $ */ public class XmlParsingConfigurationRegistry implements ConfigurationRegistry { private final CacheConfigsXmlParser parser; private final String configResource; private final Map configs = new Hashtable(); private boolean started; public XmlParsingConfigurationRegistry(String configResource) { parser = new CacheConfigsXmlParser(); this.configResource = configResource; } public void start() throws Exception { if (!started) { if (configResource != null) configs.putAll(parser.parseConfigs(configResource)); started = true; } } public void stop() { if (started) { synchronized (configs) { configs.clear(); } started = false; } } public String getConfigResource() { return configResource; } public Set getConfigurationNames() { return new HashSet(configs.keySet()); } public void registerConfiguration(String configName, Configuration config) throws CloneNotSupportedException { synchronized (configs) { if (configs.containsKey(configName)) throw new IllegalStateException(configName + " already registered"); configs.put(configName, config.clone()); } } public void unregisterConfiguration(String configName) { synchronized (configs) { if (configs.remove(configName) == null) throw new IllegalStateException(configName + " not registered"); } } public Configuration getConfiguration(String configName) { Configuration config; synchronized (configs) { config = configs.get(configName); } if (config == null) throw new IllegalArgumentException("unknown config " + configName); // Don't hand out a ref to our master copy try { return config.clone(); } catch (CloneNotSupportedException e) { // This should not happen, as we already cloned the config throw new RuntimeException("Could not clone configuration " + configName, e); } } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/config/ConfigurationComponent.java0000644000175000017500000001041311017273341031201 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.config; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.CacheSPI; import org.jboss.cache.CacheStatus; import org.jboss.cache.factories.ComponentRegistry; import org.jboss.cache.factories.annotations.Inject; import org.jboss.cache.factories.annotations.Start; import java.io.Serializable; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Set; /** * Base superclass of Cache configuration classes that expose some properties * that can be changed after the cache is started. * * @author Brian Stansberry * @version $Revision: 5898 $ * @see #testImmutability(String) */ public abstract class ConfigurationComponent implements Serializable, Cloneable { private static final long serialVersionUID = 4879873994727821938L; protected transient Log log = LogFactory.getLog(getClass()); private transient CacheSPI cache; // back-reference to test whether the cache is running. private final Set children = Collections.synchronizedSet(new HashSet()); private transient ComponentRegistry cr; // a workaround to get over immutability checks private boolean accessible; protected ConfigurationComponent() { } public void passCacheToChildConfig(ConfigurationComponent child) { if (child != null) { child.setCache(cache); } } protected void addChildConfig(ConfigurationComponent child) { if (child != null && children.add(child)) child.setCache(cache); } protected void addChildConfigs(Collection toAdd) { if (toAdd != null) { for (ConfigurationComponent child : toAdd) addChildConfig(child); } } protected void removeChildConfig(ConfigurationComponent child) { children.remove(child); } protected void removeChildConfigs(Collection toRemove) { if (toRemove != null) { for (ConfigurationComponent child : toRemove) removeChildConfig(child); } } protected void replaceChildConfig(ConfigurationComponent oldConfig, ConfigurationComponent newConfig) { removeChildConfig(oldConfig); addChildConfig(newConfig); } protected void replaceChildConfigs(Collection oldConfigs, Collection newConfigs) { synchronized (children) { removeChildConfigs(oldConfigs); addChildConfigs(newConfigs); } } /** * Checks field modifications via setters * * @param fieldName */ protected void testImmutability(String fieldName) { try { if (!accessible && cache != null && cache.getCacheStatus() != null && cache.getCacheStatus() == CacheStatus.STARTED && !getClass().getDeclaredField(fieldName).isAnnotationPresent(Dynamic.class)) { throw new ConfigurationException("Attempted to modify a non-Dynamic configuration element [" + fieldName + "] after the cache has started!"); } } catch (NoSuchFieldException e) { log.warn("Field " + fieldName + " not found!!"); } finally { accessible = false; } } /** * Sets a back-reference to the cache associated with this configuration * * @param cache */ public void setCache(CacheSPI cache) { this.cache = cache; synchronized (children) { for (ConfigurationComponent child : children) { child.setCache(cache); } } } @Inject private void injectDependencies(ComponentRegistry cr) { this.cr = cr; } @Start private void start() { setCache(cr.getComponent(CacheSPI.class)); } @Override public ConfigurationComponent clone() throws CloneNotSupportedException { ConfigurationComponent c = (ConfigurationComponent) super.clone(); c.setCache(null); return c; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/config/Dynamic.java0000644000175000017500000000126610502024541026073 0ustar twernertwernerpackage org.jboss.cache.config; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Annotation that marks fields in {@link Configuration} as being modifiable even after the cache has started. * * @author Manik Surtani * @since 2.0.0 */ // ensure this annotation is available at runtime. @Retention(RetentionPolicy.RUNTIME) // ensure that this annotation is documented on fields in Configuration @Documented // only applies to fields. @Target(ElementType.FIELD) public @interface Dynamic { } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/config/Option.java0000644000175000017500000003251011004125045025752 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.config; import org.jboss.cache.optimistic.DataVersion; /** * Used to override characteristics of specific calls to the cache. The javadocs of each of the setters below detail functionality and behaviour. * * @author Manik Surtani (manik@jboss.org) * @since 1.3.0 */ public class Option implements Cloneable { private boolean failSilently; private boolean cacheModeLocal; private DataVersion dataVersion; private boolean suppressLocking; private boolean forceDataGravitation; private boolean skipDataGravitation; private boolean forceWriteLock; private boolean skipCacheStatusCheck; private boolean forceAsynchronous; private boolean forceSynchronous; private long syncReplTimeout = -1; private int groupRequestMode = -1; private int lockAcquisitionTimeout = -1; /** * @since 1.4.0 */ public boolean isSuppressLocking() { return suppressLocking; } /** * Suppresses acquiring locks for the given invocation. Used with pessimistic locking only. Use with extreme care, may lead to a breach in data integrity! * * @since 1.4.0 */ public void setSuppressLocking(boolean suppressLocking) { this.suppressLocking = suppressLocking; } /** * @since 1.3.0 */ public boolean isFailSilently() { return failSilently; } /** * suppress any failures in your cache operation, including version mismatches with optimistic locking, timeouts obtaining locks, transaction rollbacks. If this is option is set, the method invocation will __never fail or throw an exception__, although it may not succeed. With this option enabled the call will not participate in any ongoing transactions even if a transaction is running. * * @since 1.3.0 */ public void setFailSilently(boolean failSilently) { this.failSilently = failSilently; } /** * only applies to put() and remove() methods on the cache. * * @since 1.3.0 */ public boolean isCacheModeLocal() { return cacheModeLocal; } /** * overriding CacheMode from REPL_SYNC, REPL_ASYNC, INVALIDATION_SYNC, INVALIDATION_ASYNC to LOCAL. Only applies to put() and remove() methods on the cache. * * @param cacheModeLocal * @since 1.3.0 */ public void setCacheModeLocal(boolean cacheModeLocal) { this.cacheModeLocal = cacheModeLocal; } /** * @since 1.3.0 */ public DataVersion getDataVersion() { return dataVersion; } /** * Passing in an {@link org.jboss.cache.optimistic.DataVersion} instance when using optimistic locking will override the default behaviour of internally generated version info and allow the caller to handle data versioning. * * @since 1.3.0 */ public void setDataVersion(DataVersion dataVersion) { this.dataVersion = dataVersion; } /** * @since 1.4.0 */ public boolean getForceDataGravitation() { return forceDataGravitation; } /** * Enables data gravitation calls if a cache miss is detected when using Buddy Replication. * Enabled only for a given invocation, and only useful if autoDataGravitation is set to false. * See Buddy Replication documentation for more details. * * @since 1.4.0 */ public void setForceDataGravitation(boolean enableDataGravitation) { this.forceDataGravitation = enableDataGravitation; } /** * @return true if skipDataGravitation is set to true. * @since 1.4.1.SP6 */ public boolean isSkipDataGravitation() { return skipDataGravitation; } /** * Suppresses data gravitation when buddy replication is used. If true, overrides {@link #setForceDataGravitation(boolean)} * being set to true. Typically used to suppress gravitation calls when {@link org.jboss.cache.config.BuddyReplicationConfig#setAutoDataGravitation(boolean)} * is set to true. * * @param skipDataGravitation * @since 1.4.1.SP6 */ public void setSkipDataGravitation(boolean skipDataGravitation) { this.skipDataGravitation = skipDataGravitation; } /** * Gets whether replication or invalidation should be done asynchronously, * even if the cache is configured in a synchronous mode. Has no * effect if the call is occuring within a transactional context. * * @return true if replication/invalidation should be done * asynchronously; false if the default mode * configured for the cache should be used. */ public boolean isForceAsynchronous() { return forceAsynchronous; } /** * Sets whether replication or invalidation should be done asynchronously, * even if the cache is configured in a synchronous mode. Has no * effect if the call is occuring within a transactional context. * * @param forceAsynchronous true if replication/invalidation * should be done asynchronously; false * if the default mode configured for the cache * should be used. */ public void setForceAsynchronous(boolean forceAsynchronous) { this.forceAsynchronous = forceAsynchronous; } /** * Gets whether replication or invalidation should be done synchronously, * even if the cache is configured in an asynchronous mode. Has no * effect if the call is occuring within a transactional context. * * @return true if replication/invalidation should be done * synchronously; false if the default mode * configured for the cache should be used. */ public boolean isForceSynchronous() { return forceSynchronous; } /** * Sets whether replication or invalidation should be done synchronously, * even if the cache is configured in an asynchronous mode. Has no * effect if the call is occuring within a transactional context. * * @param forceSynchronous true if replication/invalidation * should be done synchronously; false * if the default mode configured for the cache * should be used. */ public void setForceSynchronous(boolean forceSynchronous) { this.forceSynchronous = forceSynchronous; } /** * Gets any lock acquisition timeout configured for the call. * * @return the time in ms that lock acquisition attempts should block * before failing with a TimeoutException. A value < 0 indicates * that the cache's default timeout should be used. */ public int getLockAcquisitionTimeout() { return lockAcquisitionTimeout; } /** * Sets any lock acquisition timeout configured for the call. * * @param lockAcquisitionTimeout the time in ms that lock acquisition * attempts should block before failing with a * TimeoutException. A value < 0 indicates * that the cache's default timeout should be used. */ public void setLockAcquisitionTimeout(int lockAcquisitionTimeout) { this.lockAcquisitionTimeout = lockAcquisitionTimeout; } @Override public String toString() { return "Option{" + "failSilently=" + failSilently + ", cacheModeLocal=" + cacheModeLocal + ", dataVersion=" + dataVersion + ", suppressLocking=" + suppressLocking + ", lockAcquisitionTimeout=" + lockAcquisitionTimeout + ", forceDataGravitation=" + forceDataGravitation + ", skipDataGravitation=" + skipDataGravitation + ", forceAsynchronous=" + forceAsynchronous + ", forceSynchronous=" + forceSynchronous + '}'; } @Override public Option clone() throws CloneNotSupportedException { return (Option) super.clone(); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; final Option option = (Option) o; if (skipDataGravitation != option.skipDataGravitation) return false; if (cacheModeLocal != option.cacheModeLocal) return false; if (failSilently != option.failSilently) return false; if (forceDataGravitation != option.forceDataGravitation) return false; if (suppressLocking != option.suppressLocking) return false; if (dataVersion != null ? !dataVersion.equals(option.dataVersion) : option.dataVersion != null) return false; if (forceWriteLock != option.forceWriteLock) return false; if (forceAsynchronous != option.forceAsynchronous) return false; if (forceSynchronous != option.forceSynchronous) return false; if (lockAcquisitionTimeout != option.lockAcquisitionTimeout) return false; return true; } @Override public int hashCode() { int result; result = (failSilently ? 1 : 0); result = 29 * result + (cacheModeLocal ? 1 : 0); result = 29 * result + (dataVersion != null ? dataVersion.hashCode() : 0); result = 29 * result + (suppressLocking ? 1 : 0); result = 29 * result + (forceDataGravitation ? 1 : 0); result = 29 * result + (skipDataGravitation ? 1 : 0); result = 29 * result + (forceWriteLock ? 0 : 1); result = 29 * result + (forceAsynchronous ? 0 : 1); result = 29 * result + (forceSynchronous ? 0 : 1); result = 29 * result + (lockAcquisitionTimeout); return result; } /** * Resets this option to defaults. */ public void reset() { this.skipDataGravitation = false; this.cacheModeLocal = false; this.failSilently = false; this.forceDataGravitation = false; this.suppressLocking = false; this.dataVersion = null; this.forceWriteLock = false; this.forceAsynchronous = false; this.forceSynchronous = false; this.lockAcquisitionTimeout = -1; } /** * Forces a write lock to be acquired on the call, regardless of whether it is a read or write. Only applies to the {@link org.jboss.cache.interceptors.PessimisticLockInterceptor} * * @param forceWriteLock * @since 2.0.0 */ public void setForceWriteLock(boolean forceWriteLock) { this.forceWriteLock = forceWriteLock; } /** * Tests whether a write lock has been forced on the call, regardless of whether it is a read or write. Only applies to the {@link org.jboss.cache.interceptors.PessimisticLockInterceptor} * * @since 2.0.0 */ public boolean isForceWriteLock() { return forceWriteLock; } /** * If set to true, cache lifecycle checks will be skipped. DO NOT USE unless you really know what you're doing. * * @since 2.0.0 */ public void setSkipCacheStatusCheck(boolean skipCacheStatusCheck) { this.skipCacheStatusCheck = skipCacheStatusCheck; } /** * @return true if skipCacheStatusCheck is true * @since 2.0.0 */ public boolean isSkipCacheStatusCheck() { return skipCacheStatusCheck; } /** * @return the value of the sync replication timeout (used when cache mode is either {@link org.jboss.cache.config.Configuration.CacheMode#REPL_SYNC} * or {@link org.jboss.cache.config.Configuration.CacheMode#INVALIDATION_SYNC}) to be used for this specific call, or -1 (default) if the * default value in {@link Configuration#getSyncReplTimeout()} is to be used instead. * @since 2.1.0 */ public long getSyncReplTimeout() { return syncReplTimeout; } /** * Used to override the value in {@link Configuration#getSyncReplTimeout()} (used when cache mode is either {@link org.jboss.cache.config.Configuration.CacheMode#REPL_SYNC} * or {@link org.jboss.cache.config.Configuration.CacheMode#INVALIDATION_SYNC}) for this specific invocation. Defaults to -1, * which means use the default in the configuration. * * @param syncReplTimeout new timeout value for this invocation. * @since 2.1.0 */ public void setSyncReplTimeout(long syncReplTimeout) { this.syncReplTimeout = syncReplTimeout; } /** * @return overridden JGroups {@link org.jgroups.blocks.GroupRequest} mode to use, or -1 if the {@link org.jboss.cache.RPCManager}'s * own logic is to be used to select a group request mode (this is the default). * @since 2.1.0 */ public int getGroupRequestMode() { return groupRequestMode; } /** * By default, the {@link org.jboss.cache.RPCManager} has inbuilt logic when it comes to selecting a group request mode. * This can be overridden by setting the group request mode here, using this method, for a specific invocation. * * @param groupRequestMode a group request mode, found in the {@link org.jgroups.blocks.GroupRequest} class. * @since 2.1.0 */ public void setGroupRequestMode(int groupRequestMode) { this.groupRequestMode = groupRequestMode; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/config/RuntimeConfig.java0000644000175000017500000001362311006024704027261 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.config; import org.jboss.cache.NodeFactory; import org.jboss.cache.RPCManager; import org.jboss.cache.buddyreplication.BuddyGroup; import org.jboss.cache.util.Util; import org.jgroups.Channel; import org.jgroups.ChannelFactory; import javax.transaction.TransactionManager; public class RuntimeConfig extends ConfigurationComponent { /** * The serialVersionUID */ private static final long serialVersionUID = 5626847485703341794L; private transient TransactionManager transactionManager; private transient Channel channel; private transient ChannelFactory muxChannelFactory; private transient NodeFactory nodeFactory; private transient BuddyGroup buddyGroup; private RPCManager rpcManager; /** * Resets the runtime to default values. */ public void reset() { // only reset the node factory and channel for now. nodeFactory = null; channel = null; rpcManager = null; } /** * Gets the factory the cache will use to create a multiplexed channel. * * @return the channel, or null if not set */ public ChannelFactory getMuxChannelFactory() { return muxChannelFactory; } /** * Sets the factory the cache should use to create a multiplexed channel. * Ignored if a Channel is directly configured via * {@link {@link #setChannel(Channel)}. If the channel factory is set, * {@link Configuration#setMultiplexerStack(String)} must also be set, or * a CacheException will be thrown during cache startup. * * @param multiplexerChannelFactory */ public void setMuxChannelFactory(ChannelFactory multiplexerChannelFactory) { testImmutability("muxChannelFactory"); this.muxChannelFactory = multiplexerChannelFactory; } /** * Gets the channel the cache is using. *

        * External callers should use extreme care if they access the channel. * The cache expects it has exclusive access to the channel; external code * trying to send or receive messages via the channel will almost certainly * disrupt the operation of the cache. *

        * * @return the channel. May return null if the channel was * not externally set via {@link #setChannel(Channel)} and the * cache has not yet been started. * @see #setChannel(Channel) */ public Channel getChannel() { return channel; } /** * Sets the channel the cache will use. The channel should not be * connected or closed. *

        * External callers should use extreme care if they access the channel. * The cache expects it has exclusive access to the channel; external code * trying to send or receive messages via the channel will almost certainly * disrupt the operation of the cache. *

        *

        * If an application wishes to send and receive messages using the same * underlying channel as the Cache, a multiplexed channel should * be used. Two separate mux channels should be created from the same * ChannelFactory using the same stack name but different * ids. * See {@link ChannelFactory#createMultiplexerChannel(String,String,boolean,String)}. * These two mux channels will share the same underlying channel. One of the * two mux channels can be injected into the cache; the other can be used by * the application. The cache will not see the application messages and vice versa. *

        *

        * Configuring the cache to use a mux channel can also be done by configuring * {@link #setMuxChannelFactory(ChannelFactory) the channel factory} and the * {@link Configuration#setMultiplexerStack(String) stack name}, in which case * the cache will create and use a mux channel. *

        * * @param channel */ public void setChannel(Channel channel) { this.channel = channel; } public TransactionManager getTransactionManager() { return transactionManager; } public void setTransactionManager(TransactionManager transactionManager) { testImmutability("transactionManager"); this.transactionManager = transactionManager; } public NodeFactory getNodeFactory() { return nodeFactory; } public void setNodeFactory(NodeFactory nodeFactory) { this.nodeFactory = nodeFactory; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj instanceof RuntimeConfig) { RuntimeConfig other = (RuntimeConfig) obj; return Util.safeEquals(transactionManager, other.transactionManager) && Util.safeEquals(muxChannelFactory, other.muxChannelFactory) && Util.safeEquals(rpcManager, other.rpcManager) && Util.safeEquals(channel, other.channel); } return false; } @Override public int hashCode() { int result = 17; result = result * 29 + (transactionManager == null ? 0 : transactionManager.hashCode()); result = result * 29 + (muxChannelFactory == null ? 0 : muxChannelFactory.hashCode()); result = result * 29 + (rpcManager == null ? 0 : rpcManager.hashCode()); result = result * 29 + (channel == null ? 0 : channel.hashCode()); return result; } public void setBuddyGroup(BuddyGroup buddyGroup) { this.buddyGroup = buddyGroup; } public BuddyGroup getBuddyGroup() { return buddyGroup; } public void setRPCManager(RPCManager rpcManager) { this.rpcManager = rpcManager; } public RPCManager getRPCManager() { return rpcManager; } @Override public RuntimeConfig clone() throws CloneNotSupportedException { return (RuntimeConfig) super.clone(); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/config/CacheLoaderConfig.java0000644000175000017500000003176011031017377030000 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.config; import org.jboss.cache.loader.CacheLoader; import org.jboss.cache.loader.SingletonStoreCacheLoader; import org.jboss.cache.util.Util; import java.util.ArrayList; import java.util.List; import java.util.Properties; /** * Holds the configuration of the cache loader chain. ALL cache loaders should be defined using this class, adding * individual cache loaders to the chain by calling {@link CacheLoaderConfig#addIndividualCacheLoaderConfig} * * @author Manik Surtani (manik@jboss.org) * @author Brian Stansberry * @author Galder Zamarreno */ public class CacheLoaderConfig extends ConfigurationComponent { private static final long serialVersionUID = 2210349340378984424L; private boolean passivation; private String preload; private List cacheLoaderConfigs = new ArrayList(); private boolean shared; public String getPreload() { return preload; } public void setPreload(String preload) { testImmutability("preload"); this.preload = preload; } public void setPassivation(boolean passivation) { testImmutability("passivation"); this.passivation = passivation; } public boolean isPassivation() { return passivation; } public void addIndividualCacheLoaderConfig(IndividualCacheLoaderConfig clc) { testImmutability("cacheLoaderConfigs"); cacheLoaderConfigs.add(clc); // Ensure this config gets our back ref to the cache addChildConfig(clc); } public List getIndividualCacheLoaderConfigs() { return cacheLoaderConfigs; } public void setIndividualCacheLoaderConfigs(List configs) { testImmutability("cacheLoaderConfigs"); // Ensure these configs get our back ref to the cache replaceChildConfigs(this.cacheLoaderConfigs, configs); this.cacheLoaderConfigs = configs == null ? new ArrayList() : configs; } public IndividualCacheLoaderConfig getFirstCacheLoaderConfig() { if (cacheLoaderConfigs.size() == 0) return null; return cacheLoaderConfigs.get(0); } public boolean useChainingCacheLoader() { return !isPassivation() && cacheLoaderConfigs.size() > 1; } @Override public String toString() { return new StringBuilder().append("CacheLoaderConfig{").append("shared=").append(shared).append(", passivation=").append(passivation).append(", preload='").append(preload).append('\'').append(", cacheLoaderConfigs.size()=").append(cacheLoaderConfigs.size()).append('}').toString(); } public void setShared(boolean shared) { testImmutability("shared"); this.shared = shared; } public boolean isShared() { return shared; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj instanceof CacheLoaderConfig) { CacheLoaderConfig other = (CacheLoaderConfig) obj; return (this.passivation == other.passivation) && (this.shared == other.shared) && Util.safeEquals(this.preload, other.preload) && Util.safeEquals(this.cacheLoaderConfigs, other.cacheLoaderConfigs); } return false; } @Override public int hashCode() { int result = 19; result = 51 * result + (passivation ? 0 : 1); result = 51 * result + (shared ? 0 : 1); result = 51 * result + (preload == null ? 0 : preload.hashCode()); result = 51 * result + (cacheLoaderConfigs == null ? 0 : cacheLoaderConfigs.hashCode()); return result; } @Override public CacheLoaderConfig clone() throws CloneNotSupportedException { CacheLoaderConfig clone = (CacheLoaderConfig) super.clone(); if (cacheLoaderConfigs != null) { List clcs = new ArrayList(cacheLoaderConfigs.size()); for (IndividualCacheLoaderConfig clc : cacheLoaderConfigs) { clcs.add(clc.clone()); } clone.setIndividualCacheLoaderConfigs(clcs); } return clone; } /** * Loops through all individual cache loader configs and checks if fetchPersistentState is set on any of them */ public boolean isFetchPersistentState() { for (IndividualCacheLoaderConfig iclc : cacheLoaderConfigs) { if (iclc.isFetchPersistentState()) return true; } return false; } /** * Configuration object that holds the confguration of an individual cache loader. * * @author Manik Surtani (manik@jboss.org) * @author Galder Zamarreno */ public static class IndividualCacheLoaderConfig extends PluggableConfigurationComponent { private static final long serialVersionUID = -2282396799100828593L; private boolean async; private boolean ignoreModifications; private boolean fetchPersistentState; private boolean purgeOnStartup; private SingletonStoreConfig singletonStoreConfig; private transient CacheLoader cacheLoader; protected void populateFromBaseConfig(IndividualCacheLoaderConfig base) { if (base != null) { setAsync(base.isAsync()); setIgnoreModifications(base.isIgnoreModifications()); setFetchPersistentState(base.isFetchPersistentState()); setSingletonStoreConfig(base.getSingletonStoreConfig()); setPurgeOnStartup(base.isPurgeOnStartup()); setProperties(base.getProperties()); } } public boolean isPurgeOnStartup() { return purgeOnStartup; } public boolean isFetchPersistentState() { return fetchPersistentState; } public void setFetchPersistentState(boolean fetchPersistentState) { testImmutability("fetchPersistentState"); this.fetchPersistentState = fetchPersistentState; } public void setAsync(boolean async) { testImmutability("async"); this.async = async; } public boolean isAsync() { return async; } public void setIgnoreModifications(boolean ignoreModifications) { testImmutability("ignoreModifications"); this.ignoreModifications = ignoreModifications; } public boolean isIgnoreModifications() { return ignoreModifications; } public void setPurgeOnStartup(boolean purgeOnStartup) { testImmutability("purgeOnStartup"); this.purgeOnStartup = purgeOnStartup; } public SingletonStoreConfig getSingletonStoreConfig() { return singletonStoreConfig; } public void setSingletonStoreConfig(SingletonStoreConfig singletonStoreConfig) { testImmutability("singletonStoreConfig"); replaceChildConfig(this.singletonStoreConfig, singletonStoreConfig); this.singletonStoreConfig = singletonStoreConfig; } /** * Provides the ability to get and set a running cache loader, which, if exists, will be used rather than * constructing a new one. Primarily to facilitate testing with mock objects. * * @return cache loader, if one exists * @since 2.1.0 */ public CacheLoader getCacheLoader() { return cacheLoader; } /** * Provides the ability to get and set a running cache loader, which, if exists, will be used rather than * constructing a new one. Primarily to facilitate testing with mock objects. * * @param cacheLoader cacheLoader to set * @since 2.1.0 */ public void setCacheLoader(CacheLoader cacheLoader) { this.cacheLoader = cacheLoader; } @Override public boolean equals(Object obj) { if (super.equals(obj)) { IndividualCacheLoaderConfig i = (IndividualCacheLoaderConfig) obj; return equalsExcludingProperties(i); } return false; } protected boolean equalsExcludingProperties(Object obj) { if (!(obj instanceof IndividualCacheLoaderConfig)) return false; IndividualCacheLoaderConfig other = (IndividualCacheLoaderConfig) obj; return Util.safeEquals(this.className, other.className) && (this.async == other.async) && (this.ignoreModifications == other.ignoreModifications) && (this.fetchPersistentState == other.fetchPersistentState) && Util.safeEquals(this.singletonStoreConfig, other.singletonStoreConfig); } @Override public int hashCode() { return 31 * hashCodeExcludingProperties() + (properties == null ? 0 : properties.hashCode()); } protected int hashCodeExcludingProperties() { int result = 17; result = 31 * result + (className == null ? 0 : className.hashCode()); result = 31 * result + (async ? 0 : 1); result = 31 * result + (ignoreModifications ? 0 : 1); result = 31 * result + (fetchPersistentState ? 0 : 1); result = 31 * result + (singletonStoreConfig == null ? 0 : singletonStoreConfig.hashCode()); result = 31 * result + (purgeOnStartup ? 0 : 1); return result; } @Override public String toString() { return new StringBuilder().append("IndividualCacheLoaderConfig{").append("className='").append(className).append('\'') .append(", async=").append(async) .append(", ignoreModifications=").append(ignoreModifications) .append(", fetchPersistentState=").append(fetchPersistentState) .append(", properties=").append(properties) .append(", purgeOnStartup=").append(purgeOnStartup).append("},") .append("SingletonStoreConfig{").append(singletonStoreConfig).append('}') .toString(); } @Override public IndividualCacheLoaderConfig clone() throws CloneNotSupportedException { IndividualCacheLoaderConfig clone = (IndividualCacheLoaderConfig) super.clone(); if (singletonStoreConfig != null) clone.setSingletonStoreConfig(singletonStoreConfig.clone()); clone.cacheLoader = cacheLoader; return clone; } /** * Configuration for a SingletonStoreCacheLoader */ public static class SingletonStoreConfig extends PluggableConfigurationComponent { private static final long serialVersionUID = 824251894176131850L; /** * Indicates whether the singleton store functionality is enabled or not. */ private boolean singletonStoreEnabled; public SingletonStoreConfig() { // default value className = SingletonStoreCacheLoader.class.getName(); } public boolean isSingletonStoreEnabled() { return singletonStoreEnabled; } public void setSingletonStoreEnabled(boolean singletonStoreEnabled) { testImmutability("singletonStoreEnabled"); this.singletonStoreEnabled = singletonStoreEnabled; } public String getSingletonStoreClass() { return className; } public void setSingletonStoreClass(String className) { setClassName(className); } public Properties getSingletonStoreproperties() { return properties; } public void setSingletonStoreproperties(Properties properties) { setProperties(properties); } @Override public boolean equals(Object obj) { if (this == obj) return true; if (super.equals(obj)) { SingletonStoreConfig other = (SingletonStoreConfig) obj; return this.singletonStoreEnabled == other.singletonStoreEnabled; } return false; } @Override public int hashCode() { int result = 19; result = 41 * result + super.hashCode(); result = 41 * result + (singletonStoreEnabled ? 0 : 1); return result; } @Override public String toString() { return super.toString() + " enabled=" + singletonStoreEnabled + " class=" + className + " properties=" + properties; } @Override public SingletonStoreConfig clone() throws CloneNotSupportedException { return (SingletonStoreConfig) super.clone(); } } } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/config/MissingPolicyException.java0000644000175000017500000000244110517566664031202 0ustar twernertwerner/* * JBoss, Home of Professional Open Source. * Copyright 2006, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.cache.config; public class MissingPolicyException extends RuntimeException { /** The serialVersionUID */ private static final long serialVersionUID = 6107098975617303157L; public MissingPolicyException(String message) { super(message); } }jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/config/EvictionPolicyConfig.java0000644000175000017500000000251410631150566030604 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.config; import org.jboss.cache.eviction.EvictionPolicy; import org.jboss.cache.eviction.EvictionPolicyConfigBase; /** * This class encapsulates the configuration element for an eviction policy. *

        * In it's most basic form, it is implemented by {@link EvictionPolicyConfigBase}, but * more specific eviction policies may subclass or re-implement this interface * to provide access to more config variables. * * @author Manik Surtani (manik@jboss.org) */ public interface EvictionPolicyConfig { /** * Gets the class name of the {@link EvictionPolicy} implementation * this object will configure. Used by {@link org.jboss.cache.RegionManager} * to instantiate the policy. * * @return fully qualified class name */ String getEvictionPolicyClass(); /** * Validate the configuration. Will be called after any configuration * properties are set. * * @throws ConfigurationException if any values for the configuration * properties are invalid */ void validate() throws ConfigurationException; /** * Resets the values to their defaults. */ void reset(); } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/notifications/0000755000175000017500000000000011376174011025253 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/notifications/NotifierImpl.java0000644000175000017500000006675411015552370030537 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.notifications; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.*; import org.jboss.cache.buddyreplication.BuddyGroup; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.annotations.*; import org.jboss.cache.marshall.MarshalledValueMap; import org.jboss.cache.notifications.annotation.*; import org.jboss.cache.notifications.event.*; import static org.jboss.cache.notifications.event.Event.Type.*; import org.jboss.cache.util.MapCopy; import org.jgroups.View; import javax.transaction.Transaction; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.*; import java.util.concurrent.CopyOnWriteArrayList; /** * Helper class that handles all notifications to registered listeners. * * @author Manik Surtani (manik@jboss.org) */ @NonVolatile public class NotifierImpl implements Notifier { private static final Log log = LogFactory.getLog(NotifierImpl.class); private static final Class emptyMap = Collections.emptyMap().getClass(); private static final Class singletonMap = Collections.singletonMap(null, null).getClass(); private static final Class[] allowedMethodAnnotations = { CacheStarted.class, CacheStopped.class, CacheBlocked.class, CacheUnblocked.class, NodeCreated.class, NodeRemoved.class, NodeVisited.class, NodeModified.class, NodeMoved.class, NodeActivated.class, NodePassivated.class, NodeLoaded.class, NodeEvicted.class, TransactionRegistered.class, TransactionCompleted.class, ViewChanged.class, BuddyGroupChanged.class }; private static final Class[] parameterTypes = { CacheStartedEvent.class, CacheStoppedEvent.class, CacheBlockedEvent.class, CacheUnblockedEvent.class, NodeCreatedEvent.class, NodeRemovedEvent.class, NodeVisitedEvent.class, NodeModifiedEvent.class, NodeMovedEvent.class, NodeActivatedEvent.class, NodePassivatedEvent.class, NodeLoadedEvent.class, NodeEvictedEvent.class, TransactionRegisteredEvent.class, TransactionCompletedEvent.class, ViewChangedEvent.class, BuddyGroupChangedEvent.class }; private final List cacheStartedListeners = new CopyOnWriteArrayList(); private final List cacheStoppedListeners = new CopyOnWriteArrayList(); private final List cacheBlockedListeners = new CopyOnWriteArrayList(); private final List cacheUnblockedListeners = new CopyOnWriteArrayList(); private final List nodeCreatedListeners = new CopyOnWriteArrayList(); private final List nodeRemovedListeners = new CopyOnWriteArrayList(); private final List nodeVisitedListeners = new CopyOnWriteArrayList(); private final List nodeModifiedListeners = new CopyOnWriteArrayList(); private final List nodeMovedListeners = new CopyOnWriteArrayList(); private final List nodeActivatedListeners = new CopyOnWriteArrayList(); private final List nodePassivatedListeners = new CopyOnWriteArrayList(); private final List nodeLoadedListeners = new CopyOnWriteArrayList(); private final List nodeEvictedListeners = new CopyOnWriteArrayList(); private final List transactionRegisteredListeners = new CopyOnWriteArrayList(); private final List transactionCompletedListeners = new CopyOnWriteArrayList(); private final List viewChangedListeners = new CopyOnWriteArrayList(); private final List buddyGroupChangedListeners = new CopyOnWriteArrayList(); // final Map> listenerInvocations = new ConcurrentHashMap>(); private Cache cache; private boolean useMarshalledValueMaps; private Configuration config; public NotifierImpl() { } public NotifierImpl(Cache cache) { this.cache = cache; } @Inject private void injectDependencies(CacheSPI cache, Configuration config) { this.cache = cache; this.config = config; } @Destroy protected void destroy() { removeAllCacheListeners(); } @Start protected void start() { useMarshalledValueMaps = config.isUseLazyDeserialization(); } /** * Loops through all valid methods on the object passed in, and caches the relevant methods as {@link NotifierImpl.ListenerInvocation} * for invocation by reflection. * * @param listener object to be considered as a listener. */ private void validateAndAddListenerInvocation(Object listener) { testListenerClassValidity(listener.getClass()); boolean foundMethods = false; // now try all methods on the listener for anything that we like. Note that only PUBLIC methods are scanned. for (Method m : listener.getClass().getMethods()) { // loop through all valid method annotations for (int i = 0; i < allowedMethodAnnotations.length; i++) { if (m.isAnnotationPresent(allowedMethodAnnotations[i])) { testListenerMethodValidity(m, parameterTypes[i], allowedMethodAnnotations[i].getName()); addListenerInvocation(allowedMethodAnnotations[i], new ListenerInvocation(listener, m)); foundMethods = true; } } } if (!foundMethods && log.isWarnEnabled()) log.warn("Attempted to register listener of class " + listener.getClass() + ", but no valid, public methods annotated with method-level event annotations found! Ignoring listener."); } private static void testListenerClassValidity(Class listenerClass) { if (!listenerClass.isAnnotationPresent(CacheListener.class)) throw new IncorrectCacheListenerException("Cache listener class MUST be annotated with org.jboss.cache.notifications.annotation.CacheListener"); if (!Modifier.isPublic(listenerClass.getModifiers())) throw new IncorrectCacheListenerException("Cache listener class MUST be public!"); } private static void testListenerMethodValidity(Method m, Class allowedParameter, String annotationName) { if (m.getParameterTypes().length != 1 || !m.getParameterTypes()[0].isAssignableFrom(allowedParameter)) throw new IncorrectCacheListenerException("Methods annotated with " + annotationName + " must accept exactly one parameter, of assignable from type " + allowedParameter.getName()); if (!m.getReturnType().equals(void.class)) throw new IncorrectCacheListenerException("Methods annotated with " + annotationName + " should have a return type of void."); } private void addListenerInvocation(Class annotation, ListenerInvocation li) { List result = getListenersForAnnotation(annotation); result.add(li); } /** * Adds a cache listener to the list of cache listeners registered. * * @param listener */ public void addCacheListener(Object listener) { validateAndAddListenerInvocation(listener); } /** * Removes a cache listener from the list of cache listeners registered. * * @param listener */ public void removeCacheListener(Object listener) { for (Class annotation : allowedMethodAnnotations) removeListenerInvocation(annotation, listener); } private void removeListenerInvocation(Class annotation, Object listener) { if (listener == null) return; List l = getListenersForAnnotation(annotation); Set markedForRemoval = new HashSet(); for (ListenerInvocation li : l) { if (listener.equals(li.target)) markedForRemoval.add(li); } l.removeAll(markedForRemoval); } /** * Removes all listeners from the notifier, including the evictionPolicyListener. */ @Stop(priority = 99) public void removeAllCacheListeners() { cacheStartedListeners.clear(); cacheStoppedListeners.clear(); cacheBlockedListeners.clear(); cacheUnblockedListeners.clear(); nodeCreatedListeners.clear(); nodeRemovedListeners.clear(); nodeVisitedListeners.clear(); nodeModifiedListeners.clear(); nodeMovedListeners.clear(); nodeActivatedListeners.clear(); nodePassivatedListeners.clear(); nodeLoadedListeners.clear(); nodeEvictedListeners.clear(); transactionRegisteredListeners.clear(); transactionCompletedListeners.clear(); viewChangedListeners.clear(); buddyGroupChangedListeners.clear(); } /** * @return Retrieves an (unmodifiable) set of cache listeners registered. */ public Set getCacheListeners() { Set result = new HashSet(); result.addAll(getListeningObjects(cacheStartedListeners)); result.addAll(getListeningObjects(cacheStoppedListeners)); result.addAll(getListeningObjects(cacheBlockedListeners)); result.addAll(getListeningObjects(cacheUnblockedListeners)); result.addAll(getListeningObjects(nodeCreatedListeners)); result.addAll(getListeningObjects(nodeRemovedListeners)); result.addAll(getListeningObjects(nodeVisitedListeners)); result.addAll(getListeningObjects(nodeModifiedListeners)); result.addAll(getListeningObjects(nodeMovedListeners)); result.addAll(getListeningObjects(nodeActivatedListeners)); result.addAll(getListeningObjects(nodePassivatedListeners)); result.addAll(getListeningObjects(nodeLoadedListeners)); result.addAll(getListeningObjects(nodeEvictedListeners)); result.addAll(getListeningObjects(transactionRegisteredListeners)); result.addAll(getListeningObjects(transactionCompletedListeners)); result.addAll(getListeningObjects(viewChangedListeners)); result.addAll(getListeningObjects(buddyGroupChangedListeners)); return Collections.unmodifiableSet(result); } public void notifyNodeCreated(Fqn fqn, boolean pre, InvocationContext ctx) { if (!nodeCreatedListeners.isEmpty()) { boolean originLocal = ctx.isOriginLocal(); Transaction tx = ctx.getTransaction(); InvocationContext backup = resetInvocationContext(ctx); EventImpl e = new EventImpl(); e.setCache(cache); e.setOriginLocal(originLocal); e.setPre(pre); e.setFqn(fqn); e.setTransaction(tx); e.setType(NODE_CREATED); for (ListenerInvocation listener : nodeCreatedListeners) listener.invoke(e); restoreInvocationContext(backup); } } public void notifyNodeModified(Fqn fqn, boolean pre, NodeModifiedEvent.ModificationType modificationType, Map data, InvocationContext ctx) { if (!nodeModifiedListeners.isEmpty()) { boolean originLocal = ctx.isOriginLocal(); Map dataCopy = copy(data, useMarshalledValueMaps); Transaction tx = ctx.getTransaction(); InvocationContext backup = resetInvocationContext(ctx); EventImpl e = new EventImpl(); e.setCache(cache); e.setOriginLocal(originLocal); e.setPre(pre); e.setFqn(fqn); e.setTransaction(tx); e.setModificationType(modificationType); e.setData(dataCopy); e.setType(NODE_MODIFIED); for (ListenerInvocation listener : nodeModifiedListeners) listener.invoke(e); restoreInvocationContext(backup); } } public boolean shouldNotifyOnNodeModified() { return !nodeModifiedListeners.isEmpty(); } public void notifyNodeRemoved(Fqn fqn, boolean pre, Map data, InvocationContext ctx) { if (!nodeRemovedListeners.isEmpty()) { boolean originLocal = ctx.isOriginLocal(); Map dataCopy = copy(data, useMarshalledValueMaps); Transaction tx = ctx.getTransaction(); InvocationContext backup = resetInvocationContext(ctx); EventImpl e = new EventImpl(); e.setCache(cache); e.setOriginLocal(originLocal); e.setPre(pre); e.setFqn(fqn); e.setTransaction(tx); e.setData(dataCopy); e.setType(NODE_REMOVED); for (ListenerInvocation listener : nodeRemovedListeners) listener.invoke(e); restoreInvocationContext(backup); } } public void notifyNodeVisited(Fqn fqn, boolean pre, InvocationContext ctx) { if (!nodeVisitedListeners.isEmpty()) { Transaction tx = ctx.getTransaction(); InvocationContext backup = resetInvocationContext(ctx); EventImpl e = new EventImpl(); e.setCache(cache); e.setPre(pre); e.setFqn(fqn); e.setTransaction(tx); e.setType(NODE_VISITED); for (ListenerInvocation listener : nodeVisitedListeners) listener.invoke(e); restoreInvocationContext(backup); } } public void notifyNodeMoved(Fqn originalFqn, Fqn newFqn, boolean pre, InvocationContext ctx) { if (!nodeMovedListeners.isEmpty()) { boolean originLocal = ctx.isOriginLocal(); Transaction tx = ctx.getTransaction(); InvocationContext backup = resetInvocationContext(ctx); EventImpl e = new EventImpl(); e.setCache(cache); e.setOriginLocal(originLocal); e.setPre(pre); e.setFqn(originalFqn); e.setTargetFqn(newFqn); e.setTransaction(tx); e.setType(NODE_MOVED); for (ListenerInvocation listener : nodeMovedListeners) listener.invoke(e); restoreInvocationContext(backup); } } public void notifyNodeEvicted(final Fqn fqn, final boolean pre, InvocationContext ctx) { if (!nodeEvictedListeners.isEmpty()) { final boolean originLocal = ctx.isOriginLocal(); Transaction tx = ctx.getTransaction(); InvocationContext backup = resetInvocationContext(ctx); EventImpl e = new EventImpl(); e.setCache(cache); e.setOriginLocal(originLocal); e.setPre(pre); e.setFqn(fqn); e.setTransaction(tx); e.setType(NODE_EVICTED); for (ListenerInvocation listener : nodeEvictedListeners) listener.invoke(e); restoreInvocationContext(backup); } } public void notifyNodeLoaded(Fqn fqn, boolean pre, Map data, InvocationContext ctx) { if (!nodeLoadedListeners.isEmpty()) { boolean originLocal = ctx.isOriginLocal(); Map dataCopy = copy(data, useMarshalledValueMaps); Transaction tx = ctx.getTransaction(); InvocationContext backup = resetInvocationContext(ctx); EventImpl e = new EventImpl(); e.setCache(cache); e.setOriginLocal(originLocal); e.setPre(pre); e.setFqn(fqn); e.setTransaction(tx); e.setData(dataCopy); e.setType(NODE_LOADED); for (ListenerInvocation listener : nodeLoadedListeners) listener.invoke(e); restoreInvocationContext(backup); } } public void notifyNodeActivated(Fqn fqn, boolean pre, Map data, InvocationContext ctx) { if (!nodeActivatedListeners.isEmpty()) { boolean originLocal = ctx.isOriginLocal(); Map dataCopy = copy(data, useMarshalledValueMaps); Transaction tx = ctx.getTransaction(); InvocationContext backup = resetInvocationContext(ctx); EventImpl e = new EventImpl(); e.setCache(cache); e.setOriginLocal(originLocal); e.setPre(pre); e.setFqn(fqn); e.setTransaction(tx); e.setData(dataCopy); e.setType(NODE_ACTIVATED); for (ListenerInvocation listener : nodeActivatedListeners) listener.invoke(e); restoreInvocationContext(backup); } } public void notifyNodePassivated(Fqn fqn, boolean pre, Map data, InvocationContext ctx) { if (!nodePassivatedListeners.isEmpty()) { Map dataCopy = copy(data, useMarshalledValueMaps); Transaction tx = ctx.getTransaction(); InvocationContext backup = resetInvocationContext(ctx); EventImpl e = new EventImpl(); e.setCache(cache); e.setPre(pre); e.setFqn(fqn); e.setTransaction(tx); e.setData(dataCopy); e.setType(NODE_PASSIVATED); for (ListenerInvocation listener : nodePassivatedListeners) listener.invoke(e); restoreInvocationContext(backup); } } /** * Notifies all registered listeners of a cacheStarted event. */ @Start(priority = 99) public void notifyCacheStarted() { if (!cacheStartedListeners.isEmpty()) { EventImpl e = new EventImpl(); e.setCache(cache); e.setType(CACHE_STARTED); for (ListenerInvocation listener : cacheStartedListeners) listener.invoke(e); } } /** * Notifies all registered listeners of a cacheStopped event. */ @Stop(priority = 98) public void notifyCacheStopped() { if (!cacheStoppedListeners.isEmpty()) { EventImpl e = new EventImpl(); e.setCache(cache); e.setType(CACHE_STOPPED); for (ListenerInvocation listener : cacheStoppedListeners) listener.invoke(e); } } public void notifyViewChange(final View newView, InvocationContext ctx) { if (!viewChangedListeners.isEmpty()) { InvocationContext backup = resetInvocationContext(ctx); EventImpl e = new EventImpl(); e.setCache(cache); e.setNewView(newView); e.setType(VIEW_CHANGED); for (ListenerInvocation listener : viewChangedListeners) listener.invoke(e); restoreInvocationContext(backup); } } public void notifyBuddyGroupChange(final BuddyGroup buddyGroup, boolean pre) { if (!buddyGroupChangedListeners.isEmpty()) { EventImpl e = new EventImpl(); e.setCache(cache); e.setBuddyGroup(buddyGroup); e.setPre(pre); e.setType(BUDDY_GROUP_CHANGED); for (ListenerInvocation listener : buddyGroupChangedListeners) listener.invoke(e); } } public void notifyTransactionCompleted(Transaction transaction, boolean successful, InvocationContext ctx) { if (!transactionCompletedListeners.isEmpty()) { boolean isOriginLocal = ctx.isOriginLocal(); InvocationContext backup = resetInvocationContext(ctx); EventImpl e = new EventImpl(); e.setCache(cache); e.setOriginLocal(isOriginLocal); e.setTransaction(transaction); e.setSuccessful(successful); e.setType(TRANSACTION_COMPLETED); for (ListenerInvocation listener : transactionCompletedListeners) listener.invoke(e); restoreInvocationContext(backup); } } public void notifyTransactionRegistered(Transaction transaction, InvocationContext ctx) { if (!transactionRegisteredListeners.isEmpty()) { boolean isOriginLocal = ctx.isOriginLocal(); InvocationContext backup = resetInvocationContext(ctx); EventImpl e = new EventImpl(); e.setCache(cache); e.setOriginLocal(isOriginLocal); e.setTransaction(transaction); e.setType(TRANSACTION_REGISTERED); for (ListenerInvocation listener : transactionRegisteredListeners) listener.invoke(e); restoreInvocationContext(backup); } } public void notifyCacheBlocked(boolean pre) { if (!cacheBlockedListeners.isEmpty()) { EventImpl e = new EventImpl(); e.setCache(this.cache); e.setPre(pre); e.setType(CACHE_BLOCKED); for (ListenerInvocation listener : cacheBlockedListeners) listener.invoke(e); } } public void notifyCacheUnblocked(boolean pre) { if (!cacheUnblockedListeners.isEmpty()) { EventImpl e = new EventImpl(); e.setCache(this.cache); e.setPre(pre); e.setType(CACHE_UNBLOCKED); for (ListenerInvocation listener : cacheUnblockedListeners) listener.invoke(e); } } private static Map copy(Map data, boolean useMarshalledValueMaps) { if (data == null) return null; if (data.isEmpty()) return Collections.emptyMap(); if (safe(data)) return useMarshalledValueMaps ? new MarshalledValueMap(data) : data; Map defensivelyCopiedData = new MapCopy(data); return useMarshalledValueMaps ? new MarshalledValueMap(defensivelyCopiedData) : defensivelyCopiedData; } private void restoreInvocationContext(InvocationContext backup) { cache.setInvocationContext(backup); } /** * Resets the current (passed-in) invocation, and returns a temp InvocationContext containing its state so it can * be restored later using {@link #restoreInvocationContext(org.jboss.cache.InvocationContext)} * * @param ctx the current context to be reset * @return a clone of ctx, before it was reset */ private InvocationContext resetInvocationContext(InvocationContext ctx) { // wipe current context. cache.setInvocationContext(null); return ctx; } /** * A map is deemed 'safe' to be passed as-is to a listener, if either of the following are true: *
          *
        • It is null
        • *
        • It is an instance of {@link org.jboss.cache.util.MapCopy}, which is immutable
        • *
        • It is an instance of {@link java.util.Collections#emptyMap()}, which is also immutable
        • *
        • It is an instance of {@link java.util.Collections#singletonMap(Object,Object)}, which is also immutable
        • *
        * * @param map * @return */ private static boolean safe(Map map) { return map == null || map instanceof MapCopy || map.getClass().equals(emptyMap) || map.getClass().equals(singletonMap); } /** * Class that encapsulates a valid invocation for a given registered listener - containing a reference to the * method to be invoked as well as the target object. */ class ListenerInvocation { private final Object target; private final Method method; public ListenerInvocation(Object target, Method method) { this.target = target; this.method = method; } public void invoke(Event e) { try { method.invoke(target, e); } catch (InvocationTargetException e1) { Throwable cause = e1.getCause(); if (cause != null) throw new CacheException("Caught exception invoking method " + method + " on listener instance " + target, cause); else throw new CacheException("Caught exception invoking method " + method + " on listener instance " + target, e1); } catch (IllegalAccessException e1) { log.warn("Unable to invoke method " + method + " on Object instance " + target + " - removing this target object from list of listeners!", e1); removeCacheListener(this.target); } } } private List getListenersForAnnotation(Class annotation) { if (annotation == CacheStarted.class) { return cacheStartedListeners; } else if (annotation == CacheStopped.class) { return cacheStoppedListeners; } else if (annotation == CacheBlocked.class) { return cacheBlockedListeners; } else if (annotation == CacheUnblocked.class) { return cacheUnblockedListeners; } else if (annotation == NodeCreated.class) { return nodeCreatedListeners; } else if (annotation == NodeRemoved.class) { return nodeRemovedListeners; } else if (annotation == NodeVisited.class) { return nodeVisitedListeners; } else if (annotation == NodeModified.class) { return nodeModifiedListeners; } else if (annotation == NodeMoved.class) { return nodeMovedListeners; } else if (annotation == NodeActivated.class) { return nodeActivatedListeners; } else if (annotation == NodePassivated.class) { return nodePassivatedListeners; } else if (annotation == NodeLoaded.class) { return nodeLoadedListeners; } else if (annotation == NodeEvicted.class) { return nodeEvictedListeners; } else if (annotation == TransactionRegistered.class) { return transactionRegisteredListeners; } else if (annotation == TransactionCompleted.class) { return transactionCompletedListeners; } else if (annotation == ViewChanged.class) { return viewChangedListeners; } else if (annotation == BuddyGroupChanged.class) { return buddyGroupChangedListeners; } else { throw new RuntimeException("Unknown listener class: " + annotation); } } private Collection getListeningObjects(List cacheStartedListeners) { Set result = new HashSet(); for (ListenerInvocation li : cacheStartedListeners) { result.add(li.target); } return result; } public List getCacheStartedListeners() { return cacheStartedListeners; } public List getCacheStoppedListeners() { return cacheStoppedListeners; } public List getCacheBlockedListeners() { return cacheBlockedListeners; } public List getCacheUnblockedListeners() { return cacheUnblockedListeners; } public List getNodeCreatedListeners() { return nodeCreatedListeners; } public List getNodeRemovedListeners() { return nodeRemovedListeners; } public List getNodeVisitedListeners() { return nodeVisitedListeners; } public List getNodeModifiedListeners() { return nodeModifiedListeners; } public List getNodeMovedListeners() { return nodeMovedListeners; } public List getNodeActivatedListeners() { return nodeActivatedListeners; } public List getNodePassivatedListeners() { return nodePassivatedListeners; } public List getNodeLoadedListeners() { return nodeLoadedListeners; } public List getNodeEvictedListeners() { return nodeEvictedListeners; } public List getTransactionRegisteredListeners() { return transactionRegisteredListeners; } public List getTransactionCompletedListeners() { return transactionCompletedListeners; } public List getViewChangedListeners() { return viewChangedListeners; } public List getBuddyGroupChangedListeners() { return buddyGroupChangedListeners; } } ././@LongLink0000000000000000000000000000015200000000000011563 Lustar rootrootjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/notifications/IncorrectCacheListenerException.javajbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/notifications/IncorrectCacheListenerException0000644000175000017500000000103010660643050033430 0ustar twernertwernerpackage org.jboss.cache.notifications; import org.jboss.cache.CacheException; /** * Thrown when an incorrectly annotated class is added as a cache listener using the {@link org.jboss.cache.Cache#addCacheListener(Object)} API. * * @author Manik Surtani * @since 2.0.0 */ public class IncorrectCacheListenerException extends CacheException { private static final long serialVersionUID = 3847404572671886703L; public IncorrectCacheListenerException(String s) { super(s); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/notifications/Notifier.java0000644000175000017500000001035211026046072027673 0ustar twernertwernerpackage org.jboss.cache.notifications; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.buddyreplication.BuddyGroup; import org.jboss.cache.notifications.event.NodeModifiedEvent; import org.jgroups.View; import javax.transaction.Transaction; import java.util.Map; import java.util.Set; /** * Public interface with all allowed notifications. * * @author Mircea.Markus@jboss.com * @since 2.2 */ public interface Notifier { /** * Notifies all registered listeners of a nodeCreated event. */ void notifyNodeCreated(Fqn fqn, boolean pre, InvocationContext ctx); /** * Notifies all registered listeners of a nodeModified event. */ void notifyNodeModified(Fqn fqn, boolean pre, NodeModifiedEvent.ModificationType modificationType, Map data, InvocationContext ctx); /** * When notifying about node modifications, in many scenarios there is a need of building a new Map object. If no * listeners are registered for notification then it is pointless building this object - so guard the notification * with this call. */ public boolean shouldNotifyOnNodeModified(); /** * Notifies all registered listeners of a nodeRemoved event. */ void notifyNodeRemoved(Fqn fqn, boolean pre, Map data, InvocationContext ctx); /** * Notifies all registered listeners of a nodeVisited event. */ void notifyNodeVisited(Fqn fqn, boolean pre, InvocationContext ctx); /** * Notifies all registered listeners of a nodeMoved event. */ void notifyNodeMoved(Fqn originalFqn, Fqn newFqn, boolean pre, InvocationContext ctx); /** * Notifies all registered listeners of a nodeEvicted event. */ void notifyNodeEvicted(Fqn fqn, boolean pre, InvocationContext ctx); /** * Notifies all registered listeners of a nodeLoaded event. */ void notifyNodeLoaded(Fqn fqn, boolean pre, Map data, InvocationContext ctx); /** * Notifies all registered listeners of a nodeActivated event. */ void notifyNodeActivated(Fqn fqn, boolean pre, Map data, InvocationContext ctx); /** * Notifies all registered listeners of a nodePassivated event. */ void notifyNodePassivated(Fqn fqn, boolean pre, Map data, InvocationContext ctx); /** * Notifies all registered listeners of a viewChange event. Note that viewChange notifications are ALWAYS sent * immediately. */ void notifyViewChange(View new_view, InvocationContext ctx); /** * Notifies all registered listeners of a buddy group change event. Note that buddy group change notifications are ALWAYS sent * immediately. * * @param buddyGroup buddy group to set * @param pre if true, this has occured before the buddy group message is broadcast to the cluster */ void notifyBuddyGroupChange(BuddyGroup buddyGroup, boolean pre); /** * Notifies all registered listeners of a transaction completion event. * * @param transaction the transaction that has just completed * @param successful if true, the transaction committed. If false, this is a rollback event */ void notifyTransactionCompleted(Transaction transaction, boolean successful, InvocationContext ctx); /** * Notifies all registered listeners of a transaction registration event. * * @param transaction the transaction that has just completed */ void notifyTransactionRegistered(Transaction transaction, InvocationContext ctx); /** * Notify that a cache has been blocked * * @param pre */ void notifyCacheBlocked(boolean pre); /** * Notify that a cache has been unblocked * * @param pre */ void notifyCacheUnblocked(boolean pre); /** * Adds a cache listener to the list of cache listeners registered. * * @param listener */ void addCacheListener(Object listener); /** * Removes a cache listener from the list of cache listeners registered. * * @param listener */ void removeCacheListener(Object listener); /** * @return Retrieves an (unmodifiable) set of cache listeners registered. */ Set getCacheListeners(); } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/notifications/event/0000755000175000017500000000000011376174011026374 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/notifications/event/NodeCreatedEvent.java0000644000175000017500000000046510640763624032432 0ustar twernertwernerpackage org.jboss.cache.notifications.event; /** * This event is passed in to any method annotated with {@link org.jboss.cache.notifications.annotation.NodeCreated}. * * @author Manik Surtani * @since 2.0.0 */ public interface NodeCreatedEvent extends NodeEvent { } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/notifications/event/NodeMovedEvent.java0000644000175000017500000000064710640763624032137 0ustar twernertwernerpackage org.jboss.cache.notifications.event; import org.jboss.cache.Fqn; /** * This event is passed in to any method annotated with {@link org.jboss.cache.notifications.annotation.NodeMoved}. * * @author Manik Surtani * @since 2.0.0 */ public interface NodeMovedEvent extends NodeEvent { /** * @return the new, resultant Fqn after the move */ Fqn getTargetFqn(); } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/notifications/event/NodeRemovedEvent.java0000644000175000017500000000113710640763624032461 0ustar twernertwernerpackage org.jboss.cache.notifications.event; import java.util.Map; /** * This event is passed in to any method annotated with {@link org.jboss.cache.notifications.annotation.NodeRemoved}. * * @author Manik Surtani * @since 2.0.0 */ public interface NodeRemovedEvent extends NodeEvent { /** * @return an unmodifiable {@link Map} of data. When isPre() == true, this is the initial state of the {@link org.jboss.cache.Node} * before removal. When called with isPre() == false, this is null. */ Map getData(); } ././@LongLink0000000000000000000000000000015300000000000011564 Lustar rootrootjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/notifications/event/TransactionRegisteredEvent.javajbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/notifications/event/TransactionRegisteredEven0000644000175000017500000000100310640763624033441 0ustar twernertwernerpackage org.jboss.cache.notifications.event; /** * This event is passed in to any method annotated with {@link org.jboss.cache.notifications.annotation.TransactionRegistered}. *

        * Note that this event is only delivered after the fact, i.e., you will never see an instance of this event * with {@link #isPre()} being set to true. * * @author Manik Surtani * @since 2.0.0 */ public interface TransactionRegisteredEvent extends TransactionalEvent { } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/notifications/event/CacheBlockedEvent.java0000644000175000017500000000046310640763624032542 0ustar twernertwernerpackage org.jboss.cache.notifications.event; /** * This event is passed in to any method annotated with {@link org.jboss.cache.notifications.annotation.CacheBlocked}. * * @author Manik Surtani * @since 2.0.0 */ public interface CacheBlockedEvent extends Event { } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/notifications/event/NodeEvictedEvent.java0000644000175000017500000000046510640763624032446 0ustar twernertwernerpackage org.jboss.cache.notifications.event; /** * This event is passed in to any method annotated with {@link org.jboss.cache.notifications.annotation.NodeEvicted}. * * @author Manik Surtani * @since 2.0.0 */ public interface NodeEvictedEvent extends NodeEvent { } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/notifications/event/CacheStoppedEvent.java0000644000175000017500000000046310640763624032615 0ustar twernertwernerpackage org.jboss.cache.notifications.event; /** * This event is passed in to any method annotated with {@link org.jboss.cache.notifications.annotation.CacheStopped}. * * @author Manik Surtani * @since 2.0.0 */ public interface CacheStoppedEvent extends Event { } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/notifications/event/NodeVisitedEvent.java0000644000175000017500000000046510640763624032472 0ustar twernertwernerpackage org.jboss.cache.notifications.event; /** * This event is passed in to any method annotated with {@link org.jboss.cache.notifications.annotation.NodeVisited}. * * @author Manik Surtani * @since 2.0.0 */ public interface NodeVisitedEvent extends NodeEvent { } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/notifications/event/TransactionalEvent.java0000644000175000017500000000133010640763624033047 0ustar twernertwernerpackage org.jboss.cache.notifications.event; import javax.transaction.Transaction; /** * An event type that includes a transaction context - if one exists - as well as a boolean as to whether the call * originated locally or remotely. * * @author Manik Surtani * @since 2.0.0 */ public interface TransactionalEvent extends Event { /** * @return the Transaction associated with the current call. May be null if the current call is outside the * scope of a transaction. */ Transaction getTransaction(); /** * @return true if the call originated on the local cache instance; false if originated from a remote one. */ boolean isOriginLocal(); } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/notifications/event/CacheStartedEvent.java0000644000175000017500000000046310640763624032605 0ustar twernertwernerpackage org.jboss.cache.notifications.event; /** * This event is passed in to any method annotated with {@link org.jboss.cache.notifications.annotation.CacheStarted}. * * @author Manik Surtani * @since 2.0.0 */ public interface CacheStartedEvent extends Event { } ././@LongLink0000000000000000000000000000015200000000000011563 Lustar rootrootjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/notifications/event/TransactionCompletedEvent.javajbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/notifications/event/TransactionCompletedEvent0000644000175000017500000000131510640763624033452 0ustar twernertwernerpackage org.jboss.cache.notifications.event; /** * This event is passed in to any method annotated with {@link org.jboss.cache.notifications.annotation.TransactionCompleted}. *

        * Note that this event is only delivered after the fact, i.e., you will never see an instance of this event * with {@link #isPre()} being set to true. * * @author Manik Surtani * @since 2.0.0 */ public interface TransactionCompletedEvent extends TransactionalEvent { /** * @return if true, the transaction completed by committing successfully. If false, the transaction * completed with a rollback. */ boolean isSuccessful(); } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/notifications/event/NodeModifiedEvent.java0000644000175000017500000000370011041076631032565 0ustar twernertwernerpackage org.jboss.cache.notifications.event; import java.util.Map; /** * This event is passed in to any method annotated with {@link org.jboss.cache.notifications.annotation.NodeModified} * * @author Manik Surtani * @since 2.0.0 */ public interface NodeModifiedEvent extends NodeEvent { /** * Different cache modification types. */ static enum ModificationType { PUT_DATA, REMOVE_DATA, PUT_MAP } /** * @return an instance of the {@link org.jboss.cache.notifications.event.NodeModifiedEvent.ModificationType} enumeration. */ ModificationType getModificationType(); /** * When called with isPre() == true, this is the initial state of the {@link org.jboss.cache.Node} * before modification. *

        * When called with isPre() == false, this depends on the value of getModificationType(): *

          *
        • {@link ModificationType#PUT_DATA}: Map contains the single key/value pair that was added or modified.
        • *
        • {@link ModificationType#REMOVE_DATA}: Map contains the key/value pairs that were removed.
        • *
        • {@link ModificationType#PUT_MAP}: Map contains the new state of the {@link org.jboss.cache.Node} following modification. This map includes modified key/value * pairs as well as any that were not affected.
        • *
        *

        * Implementations interested in seeing the difference in the node data in the {@link ModificationType#PUT_MAP} case * can cache the value of getData() map passed when isPre() == true, and then when the * isPre() == false callback is received, pass the cached map and the new result of getData() to * {@link org.jboss.cache.util.Util#diffNodeData(java.util.Map,java.util.Map)} * * @return Unmodifiable {@link java.util.Map}; will not be null. See description above. */ Map getData(); } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/notifications/event/NodeLoadedEvent.java0000644000175000017500000000122210640763624032243 0ustar twernertwernerpackage org.jboss.cache.notifications.event; import java.util.Map; /** * This event is passed in to any method annotated with {@link org.jboss.cache.notifications.annotation.NodeLoaded}. * * @author Manik Surtani * @since 2.0.0 */ public interface NodeLoadedEvent extends NodeEvent { /** * @return an unmodifiable {@link Map} of data loaded from a cache loader and into the cache. If the * {@link org.jboss.cache.Node} is loaded but the data isn't (for example when calling {@link org.jboss.cache.Node#getChildren()}) * this method returns null. */ Map getData(); } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/notifications/event/EventImpl.java0000644000175000017500000001435511004125106031137 0ustar twernertwernerpackage org.jboss.cache.notifications.event; import org.jboss.cache.Cache; import org.jboss.cache.Fqn; import org.jboss.cache.buddyreplication.BuddyGroup; import org.jgroups.View; import javax.transaction.Transaction; import java.util.Map; /** * Basic implementation of an event that covers all event types. * * @author Manik Surtani * @since 2.0.0 */ public class EventImpl implements CacheBlockedEvent, CacheUnblockedEvent, CacheStartedEvent, CacheStoppedEvent, NodeActivatedEvent, NodeCreatedEvent, NodeEvictedEvent, NodeLoadedEvent, NodeModifiedEvent, NodeMovedEvent, NodePassivatedEvent, NodeRemovedEvent, NodeVisitedEvent, TransactionCompletedEvent, TransactionRegisteredEvent, ViewChangedEvent, BuddyGroupChangedEvent { private boolean pre = false; // by default events are after the fact private Cache cache; private ModificationType modificationType; private Map data; private Fqn fqn; private Transaction transaction; private boolean originLocal = true; // by default events all originate locally private Fqn targetFqn; private boolean successful; private View newView; private Type type; private BuddyGroup buddyGroup; public EventImpl(boolean pre, Cache cache, ModificationType modificationType, Map data, Fqn fqn, Transaction transaction, boolean originLocal, Fqn targetFqn, boolean successful, View newView, Type type) { this.pre = pre; this.cache = cache; this.modificationType = modificationType; this.data = data; this.fqn = fqn; this.transaction = transaction; this.originLocal = originLocal; this.targetFqn = targetFqn; this.successful = successful; this.newView = newView; this.type = type; } public EventImpl() { } public Type getType() { return type; } public boolean isPre() { return pre; } public Cache getCache() { return cache; } public ModificationType getModificationType() { return modificationType; } public Map getData() { return data; } public Fqn getFqn() { return fqn; } public Transaction getTransaction() { return transaction; } public boolean isOriginLocal() { return originLocal; } public Fqn getTargetFqn() { return targetFqn; } public boolean isSuccessful() { return successful; } public View getNewView() { return newView; } // ------------------------------ setters ----------------------------- public void setPre(boolean pre) { this.pre = pre; } public void setCache(Cache cache) { this.cache = cache; } public void setModificationType(ModificationType modificationType) { this.modificationType = modificationType; } public void setData(Map data) { this.data = data; } public void setFqn(Fqn fqn) { this.fqn = fqn; } public void setTransaction(Transaction transaction) { this.transaction = transaction; } public void setOriginLocal(boolean originLocal) { this.originLocal = originLocal; } public void setTargetFqn(Fqn targetFqn) { this.targetFqn = targetFqn; } public void setSuccessful(boolean successful) { this.successful = successful; } public void setNewView(View newView) { this.newView = newView; } public void setType(Type type) { this.type = type; } public void setBuddyGroup(BuddyGroup buddyGroup) { this.buddyGroup = buddyGroup; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; EventImpl event = (EventImpl) o; if (originLocal != event.originLocal) return false; if (pre != event.pre) return false; if (successful != event.successful) return false; if (cache != null ? !cache.equals(event.cache) : event.cache != null) return false; if (data != null ? !data.equals(event.data) : event.data != null) return false; if (fqn != null ? !fqn.equals(event.fqn) : event.fqn != null) return false; if (modificationType != event.modificationType) return false; if (targetFqn != null ? !targetFqn.equals(event.targetFqn) : event.targetFqn != null) return false; if (transaction != null ? !transaction.equals(event.transaction) : event.transaction != null) return false; if (newView != null ? !newView.equals(event.newView) : event.newView != null) return false; if (buddyGroup != null ? !buddyGroup.equals(event.buddyGroup) : event.buddyGroup != null) return false; if (type != null ? !type.equals(event.type) : event.type != null) return false; return true; } @Override public int hashCode() { int result; result = (pre ? 1 : 0); result = 31 * result + (cache != null ? cache.hashCode() : 0); result = 31 * result + (modificationType != null ? modificationType.hashCode() : 0); result = 31 * result + (data != null ? data.hashCode() : 0); result = 31 * result + (fqn != null ? fqn.hashCode() : 0); result = 31 * result + (transaction != null ? transaction.hashCode() : 0); result = 31 * result + (originLocal ? 1 : 0); result = 31 * result + (targetFqn != null ? targetFqn.hashCode() : 0); result = 31 * result + (successful ? 1 : 0); result = 31 * result + (newView != null ? newView.hashCode() : 0); result = 31 * result + (buddyGroup != null ? buddyGroup.hashCode() : 0); result = 31 * result + (type != null ? type.hashCode() : 0); return result; } @Override public String toString() { return "EventImpl{" + "type=" + type + ",pre=" + pre + ", cache=" + cache + ", modificationType=" + modificationType + ", data=" + data + ", fqn=" + fqn + ", transaction=" + transaction + ", originLocal=" + originLocal + ", targetFqn=" + targetFqn + ", successful=" + successful + ", newView=" + newView + ", buddyGroup=" + buddyGroup + '}'; } public BuddyGroup getBuddyGroup() { return buddyGroup; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/notifications/event/Event.java0000644000175000017500000000164311041076631030322 0ustar twernertwernerpackage org.jboss.cache.notifications.event; import org.jboss.cache.Cache; /** * An interface that defines common characteristics of events * * @author Manik Surtani * @since 2.0.0 */ public interface Event { static enum Type { CACHE_STARTED, CACHE_STOPPED, CACHE_BLOCKED, CACHE_UNBLOCKED, NODE_ACTIVATED, NODE_PASSIVATED, NODE_LOADED, NODE_EVICTED, NODE_CREATED, NODE_REMOVED, NODE_MODIFIED, NODE_MOVED, NODE_VISITED, TRANSACTION_COMPLETED, TRANSACTION_REGISTERED, VIEW_CHANGED, BUDDY_GROUP_CHANGED } /** * @return the type of event represented by this instance. */ Type getType(); /** * @return true if the notification is before the event has occured, false if after the event has occured. */ boolean isPre(); /** * @return a handle to the cache instance that generated this notification. */ Cache getCache(); } ././@LongLink0000000000000000000000000000014700000000000011567 Lustar rootrootjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/notifications/event/BuddyGroupChangedEvent.javajbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/notifications/event/BuddyGroupChangedEvent.ja0000644000175000017500000000072310740243612033250 0ustar twernertwernerpackage org.jboss.cache.notifications.event; import org.jboss.cache.buddyreplication.BuddyGroup; /** * This event is passed in to any method annotated with {@link org.jboss.cache.notifications.annotation.BuddyGroupChanged}. * * @author Manik Surtani * @since 2.1.0 */ public interface BuddyGroupChangedEvent extends Event { /** * @return the new buddy group * @since 2.1.0 */ BuddyGroup getBuddyGroup(); }jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/notifications/event/NodePassivatedEvent.java0000644000175000017500000000076410640763624033170 0ustar twernertwernerpackage org.jboss.cache.notifications.event; import java.util.Map; /** * This event is passed in to any method annotated with {@link org.jboss.cache.notifications.annotation.NodePassivated}. * * @author Manik Surtani * @since 2.0.0 */ public interface NodePassivatedEvent extends NodeEvent { /** * @return an unmodifiable {@link java.util.Map} of data being passivated. Empty map when {@link #isPre()} is false. */ Map getData(); } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/notifications/event/ViewChangedEvent.java0000644000175000017500000000065410640763624032441 0ustar twernertwernerpackage org.jboss.cache.notifications.event; import org.jgroups.View; /** * This event is passed in to any method annotated with {@link org.jboss.cache.notifications.annotation.ViewChanged}. * * @author Manik Surtani * @since 2.0.0 */ public interface ViewChangedEvent extends Event { /** * @return the new view associated with this view change. */ View getNewView(); } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/notifications/event/NodeEvent.java0000644000175000017500000000063510640763624031141 0ustar twernertwernerpackage org.jboss.cache.notifications.event; import org.jboss.cache.Fqn; /** * Transactional events that additionally expose an Fqn as such events pertain to a specific node. * * @author Manik Surtani * @since 2.0.0 */ public interface NodeEvent extends TransactionalEvent { /** * @return the Fqn pointing to the node that is affected. */ Fqn getFqn(); } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/notifications/event/NodeActivatedEvent.java0000644000175000017500000000076510640763624032772 0ustar twernertwernerpackage org.jboss.cache.notifications.event; import java.util.Map; /** * This event is passed in to any method annotated with {@link org.jboss.cache.notifications.annotation.NodeActivated}. * * @author Manik Surtani * @since 2.0.0 */ public interface NodeActivatedEvent extends NodeEvent { /** * @return an unmodifiable {@link java.util.Map} of data being activated. Empty map when {@link #isPre()} returns true. */ Map getData(); } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/notifications/event/CacheUnblockedEvent.java0000644000175000017500000000046710640763624033111 0ustar twernertwernerpackage org.jboss.cache.notifications.event; /** * This event is passed in to any method annotated with {@link org.jboss.cache.notifications.annotation.CacheUnblocked}. * * @author Manik Surtani * @since 2.0.0 */ public interface CacheUnblockedEvent extends Event { } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/notifications/annotation/0000755000175000017500000000000011376174012027426 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/notifications/annotation/NodePassivated.java0000644000175000017500000000162010640763624033207 0ustar twernertwernerpackage org.jboss.cache.notifications.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * This annotation should be used on methods that need to be notified when a node is passivated. *

        * Methods annotated with this annotation should accept a single * parameter, a {@link org.jboss.cache.notifications.event.NodePassivatedEvent} otherwise a {@link org.jboss.cache.notifications.IncorrectCacheListenerException} * will be thrown when registering your listener. * * @author Manik Surtani * @see CacheListener * @since 2.0.0 */ // ensure this annotation is available at runtime. @Retention(RetentionPolicy.RUNTIME) // ensure that this annotation is applied to classes. @Target(ElementType.METHOD) public @interface NodePassivated { } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/notifications/annotation/CacheBlocked.java0000644000175000017500000000157710640763624032600 0ustar twernertwernerpackage org.jboss.cache.notifications.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * This annotation should be used on methods that need to be notified when a JGroups BLOCK event occurs. *

        * Methods annotated with this annotation should be public and take in a single parameter, a {@link org.jboss.cache.notifications.event.CacheBlockedEvent} * otherwise an {@link org.jboss.cache.notifications.IncorrectCacheListenerException} will be thrown when registering * your cache listener. * * @author Manik Surtani * @see CacheListener * @see org.jboss.cache.notifications.event.CacheBlockedEvent * @since 2.0.0 */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface CacheBlocked { } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/notifications/annotation/NodeMoved.java0000644000175000017500000000173610640763624032166 0ustar twernertwernerpackage org.jboss.cache.notifications.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * This annotation should be used on methods that need to be notified when a node is moved using the {@link org.jboss.cache.Cache#move(org.jboss.cache.Fqn,org.jboss.cache.Fqn)} * API. *

        * Methods annotated with this annotation should accept a single * parameter, a {@link org.jboss.cache.notifications.event.NodeMovedEvent} otherwise a {@link org.jboss.cache.notifications.IncorrectCacheListenerException} * will be thrown when registering your listener. * * @author Manik Surtani * @see CacheListener * @since 2.0.0 */ // ensure this annotation is available at runtime. @Retention(RetentionPolicy.RUNTIME) // ensure that this annotation is applied to classes. @Target(ElementType.METHOD) public @interface NodeMoved { } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/notifications/annotation/NodeRemoved.java0000644000175000017500000000164010640763624032507 0ustar twernertwernerpackage org.jboss.cache.notifications.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * This annotation should be used on methods that need to be notified when a node is removed from the cache. *

        * Methods annotated with this annotation should accept a single * parameter, a {@link org.jboss.cache.notifications.event.TransactionRegisteredEvent} otherwise a {@link org.jboss.cache.notifications.IncorrectCacheListenerException} * will be thrown when registering your listener. * * @author Manik Surtani * @see CacheListener * @since 2.0.0 */ // ensure this annotation is available at runtime. @Retention(RetentionPolicy.RUNTIME) // ensure that this annotation is applied to classes. @Target(ElementType.METHOD) public @interface NodeRemoved { } ././@LongLink0000000000000000000000000000015200000000000011563 Lustar rootrootjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/notifications/annotation/TransactionCompleted.javajbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/notifications/annotation/TransactionCompleted0000644000175000017500000000235010640763624033501 0ustar twernertwernerpackage org.jboss.cache.notifications.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * This annotation should be used on methods that need to be notified when the cache is called to participate in a transaction and * the transaction completes, either with a commit or a rollback. *

        * Methods annotated with this annotation should accept a single * parameter, a {@link org.jboss.cache.notifications.event.TransactionCompletedEvent} otherwise a {@link org.jboss.cache.notifications.IncorrectCacheListenerException} * will be thrown when registering your listener. *

        * Note that methods marked with this annotation will only be fired after the fact, i.e., your method will never be * called with {@link org.jboss.cache.notifications.event.Event#isPre()} being set to true. * * @author Manik Surtani * @see CacheListener * @since 2.0.0 */ // ensure this annotation is available at runtime. @Retention(RetentionPolicy.RUNTIME) // ensure that this annotation is applied to classes. @Target(ElementType.METHOD) public @interface TransactionCompleted { } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/notifications/annotation/NodeActivated.java0000644000175000017500000000157210640763624033016 0ustar twernertwernerpackage org.jboss.cache.notifications.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * This annotation should be used on methods that need to be notified when a node is activated. *

        * Methods annotated with this annotation should be public and take in a single parameter, a {@link org.jboss.cache.notifications.event.NodeActivatedEvent} * otherwise an {@link org.jboss.cache.notifications.IncorrectCacheListenerException} will be thrown when registering * your cache listener. * * @author Manik Surtani * @see CacheListener * @see org.jboss.cache.notifications.annotation.NodePassivated * @since 2.0.0 */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface NodeActivated { } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/notifications/annotation/NodeLoaded.java0000644000175000017500000000154410640763624032301 0ustar twernertwernerpackage org.jboss.cache.notifications.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * This annotation should be used on methods that need to be notified when a node is loaded from a {@link org.jboss.cache.loader.CacheLoader}. *

        * Methods annotated with this annotation should be public and take in a single parameter, a {@link org.jboss.cache.notifications.event.NodeEvictedEvent} * otherwise an {@link org.jboss.cache.notifications.IncorrectCacheListenerException} will be thrown when registering * your cache listener. * * @author Manik Surtani * @see CacheListener * @since 2.0.0 */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface NodeLoaded { } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/notifications/annotation/ViewChanged.java0000644000175000017500000000174510640763624032472 0ustar twernertwernerpackage org.jboss.cache.notifications.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * This annotation should be used on methods that need to be notified when the cache is used in a cluster and the cluster topology * changes (i.e., a member joins or leaves the cluster). *

        * Methods annotated with this annotation should accept a single parameter, * a {@link org.jboss.cache.notifications.event.ViewChangedEvent} otherwise a {@link org.jboss.cache.notifications.IncorrectCacheListenerException} * will be thrown when registering your listener. * * @author Manik Surtani * @see CacheListener * @since 2.0.0 */ // ensure this annotation is available at runtime. @Retention(RetentionPolicy.RUNTIME) // ensure that this annotation is applied to classes. @Target(ElementType.METHOD) public @interface ViewChanged { } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/notifications/annotation/NodeEvicted.java0000644000175000017500000000156010640763624032472 0ustar twernertwernerpackage org.jboss.cache.notifications.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * This annotation should be used on methods that need to be notified when a node is evicted. *

        * Methods annotated with this annotation should be public and take in a single parameter, a {@link org.jboss.cache.notifications.event.NodeEvictedEvent} * otherwise an {@link org.jboss.cache.notifications.IncorrectCacheListenerException} will be thrown when registering * your cache listener. * * @author Manik Surtani * @see CacheListener * @see org.jboss.cache.notifications.annotation.NodeLoaded * @since 2.0.0 */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface NodeEvicted { } ././@LongLink0000000000000000000000000000015300000000000011564 Lustar rootrootjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/notifications/annotation/TransactionRegistered.javajbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/notifications/annotation/TransactionRegistere0000644000175000017500000000244110640763624033517 0ustar twernertwernerpackage org.jboss.cache.notifications.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * This annotation should be used on methods that need to be notified when the cache is called to participate in a transaction and * registers a {@link javax.transaction.Synchronization} with a registered {@link javax.transaction.TransactionManager}. *

        * Methods annotated with this annotation should accept a single * parameter, a {@link org.jboss.cache.notifications.event.TransactionRegisteredEvent} otherwise a {@link org.jboss.cache.notifications.IncorrectCacheListenerException} * will be thrown when registering your listener. *

        * Note that methods marked with this annotation will only be fired after the fact, i.e., your method will never be * called with {@link org.jboss.cache.notifications.event.Event#isPre()} being set to true. * * @author Manik Surtani * @see CacheListener * @since 2.0.0 */ // ensure this annotation is available at runtime. @Retention(RetentionPolicy.RUNTIME) // ensure that this annotation is applied to classes. @Target(ElementType.METHOD) public @interface TransactionRegistered { } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/notifications/annotation/NodeCreated.java0000644000175000017500000000146410640763624032461 0ustar twernertwernerpackage org.jboss.cache.notifications.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * This annotation should be used on methods that need to be notified when a node is created. *

        * Methods annotated with this annotation should be public and take in a single parameter, a {@link org.jboss.cache.notifications.event.NodeCreatedEvent} * otherwise an {@link org.jboss.cache.notifications.IncorrectCacheListenerException} will be thrown when registering * your cache listener. * * @author Manik Surtani * @see CacheListener * @since 2.0.0 */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface NodeCreated { } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/notifications/annotation/CacheUnblocked.java0000644000175000017500000000150710640763624033134 0ustar twernertwernerpackage org.jboss.cache.notifications.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * This annotation should be used on methods that need to be notified when a JGroups UNBLOCK event occurs. *

        * Methods annotated with this annotation should be public and take in a single parameter, a {@link org.jboss.cache.notifications.event.CacheUnblockedEvent} * otherwise an {@link org.jboss.cache.notifications.IncorrectCacheListenerException} will be thrown when registering * your cache listener. * * @author Manik Surtani * @see CacheListener * @since 2.0.0 */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface CacheUnblocked { } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/notifications/annotation/CacheStarted.java0000644000175000017500000000144110640763624032631 0ustar twernertwernerpackage org.jboss.cache.notifications.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * This annotation should be used on methods that need to be notified when a cache is started. *

        * Methods annotated with this annotation should accept a single * parameter, a {@link org.jboss.cache.notifications.event.CacheStartedEvent} otherwise a {@link org.jboss.cache.notifications.IncorrectCacheListenerException} * will be thrown when registering your listener. * * @author Manik Surtani * @see CacheListener * @since 2.0.0 */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface CacheStarted { } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/notifications/annotation/CacheStopped.java0000644000175000017500000000144110640763624032641 0ustar twernertwernerpackage org.jboss.cache.notifications.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * This annotation should be used on methods that need to be notified when a cache is stopped. *

        * Methods annotated with this annotation should accept a single * parameter, a {@link org.jboss.cache.notifications.event.CacheStoppedEvent} otherwise a {@link org.jboss.cache.notifications.IncorrectCacheListenerException} * will be thrown when registering your listener. * * @author Manik Surtani * @see CacheListener * @since 2.0.0 */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface CacheStopped { } ././@LongLink0000000000000000000000000000014700000000000011567 Lustar rootrootjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/notifications/annotation/BuddyGroupChanged.javajbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/notifications/annotation/BuddyGroupChanged.ja0000644000175000017500000000207010740243612033274 0ustar twernertwernerpackage org.jboss.cache.notifications.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * This annotation should be used on methods that need to be notified when a buddy group changes. *

        * Methods annotated with this annotation should be public and take in a single parameter, a {@link org.jboss.cache.notifications.event.BuddyGroupChangedEvent} * otherwise an {@link org.jboss.cache.notifications.IncorrectCacheListenerException} will be thrown when registering * your cache listener. *

        * This call back only occurs when a buddy group structure is changed. In a cache setup where buddy replication is not * enabled, this call back would never occur. *

        * * @author Manik Surtani * @see CacheListener * @see org.jboss.cache.notifications.event.CacheBlockedEvent * @since 2.1.0 */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface BuddyGroupChanged { } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/notifications/annotation/NodeVisited.java0000644000175000017500000000160710640763624032520 0ustar twernertwernerpackage org.jboss.cache.notifications.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * This annotation should be used on methods that need to be notified when a node is visited. *

        * Methods annotated with this annotation should accept a single * parameter, a {@link org.jboss.cache.notifications.event.NodeVisitedEvent} otherwise a {@link org.jboss.cache.notifications.IncorrectCacheListenerException} * will be thrown when registering your listener. * * @author Manik Surtani * @see CacheListener * @since 2.0.0 */ // ensure this annotation is available at runtime. @Retention(RetentionPolicy.RUNTIME) // ensure that this annotation is applied to classes. @Target(ElementType.METHOD) public @interface NodeVisited { } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/notifications/annotation/CacheListener.java0000644000175000017500000002635311017515454033014 0ustar twernertwernerpackage org.jboss.cache.notifications.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Class-level annotation used to annotate an object as being a valid cache listener. Used with the * {@link org.jboss.cache.Cache#addCacheListener(Object)} and related APIs. *

        * Note that even if a class is annotated with this annotation, it still needs method-level annotation (such as * {@link org.jboss.cache.notifications.annotation.CacheStarted}) to actually receive notifications. *

        * Objects annotated with this annotation - listeners - can be attached to a running {@link org.jboss.cache.Cache} so * users can be notified of {@link org.jboss.cache.Cache} events. *

        *

        * There can be multiple methods that are annotated to receive the same event, * and a method may receive multiple events by using a super type. *

        *

        *

        Delivery Semantics

        *

        * An event is delivered immediately after the * respective operation, but before the underlying cache call returns. For this * reason it is important to keep listener processing logic short-lived. If a * long running task needs to be performed, it's recommended to use another * thread. *

        *

        *

        Transactional Semantics

        *

        * Since the event is delivered during the actual cache call, the transactional * outcome is not yet known. For this reason, events are always delivered, even * if the changes they represent are discarded by their containing transaction. * For applications that must only process events that represent changes in a * completed transaction, {@link org.jboss.cache.notifications.event.TransactionalEvent#getTransaction()} can be used, * along with {@link org.jboss.cache.notifications.event.TransactionCompletedEvent#isSuccessful()} to record events and * later process them once the transaction has been successfully committed. * Example 4 demonstrates this. *

        *

        *

        Threading Semantics

        *

        * A listener implementation must be capable of handling concurrent invocations. Local * notifications reuse the calling thread; remote notifications reuse the network thread. *

        *

        * Since notifications reuse the calling or network thread, it is important to realise that * if your listener implementation blocks or performs a long-running task, the original caller which * triggered the cache event may block until the listener callback completes. It is therefore a good idea to use * the listener to be notified of an event but to perform any * long running tasks in a separate thread so as not to block the original caller. *

        *

        * In addition, any locks acquired for the operation being performed will still be held for the callback. This needs to be kept in mind * as locks may be held longer than necessary or intended to and may cause deadlocking in certain situations. See above paragraph * on long-running tasks that should be run in a separate thread. *

        *

        *

        * Summary of Notification Annotations *

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

*

AnnotationEventDescription
{@link CacheStarted}{@link org.jboss.cache.notifications.event.CacheStartedEvent}A cache was started
{@link CacheStopped}{@link org.jboss.cache.notifications.event.CacheStoppedEvent}A cache was stopped
{@link NodeModified}{@link org.jboss.cache.notifications.event.NodeModifiedEvent}A node was modified
{@link NodeMoved}{@link org.jboss.cache.notifications.event.NodeMovedEvent}A node was moved
{@link NodeCreated}{@link org.jboss.cache.notifications.event.NodeCreatedEvent}A node was created
{@link NodeRemoved}{@link org.jboss.cache.notifications.event.NodeRemovedEvent}A node was removed
{@link NodeVisited}{@link org.jboss.cache.notifications.event.NodeVisitedEvent}A node was visited
{@link NodeLoaded}{@link org.jboss.cache.notifications.event.NodeLoadedEvent}A node was loaded
{@link org.jboss.cache.notifications.annotation.NodeEvicted}{@link org.jboss.cache.notifications.event.NodeEvictedEvent}A node was evicted
{@link org.jboss.cache.notifications.annotation.NodeActivated}{@link org.jboss.cache.notifications.event.NodeActivatedEvent}A node was activated
{@link org.jboss.cache.notifications.annotation.NodePassivated}{@link org.jboss.cache.notifications.event.NodePassivatedEvent}A node was passivated
{@link org.jboss.cache.notifications.annotation.ViewChanged}{@link org.jboss.cache.notifications.event.ViewChangedEvent}A view change event was detected
{@link org.jboss.cache.notifications.annotation.CacheBlocked}{@link org.jboss.cache.notifications.event.CacheBlockedEvent}A cache block event was detected
{@link CacheUnblocked}{@link org.jboss.cache.notifications.event.CacheUnblockedEvent}A cache unblock event was detected
{@link TransactionRegistered}{@link org.jboss.cache.notifications.event.TransactionRegisteredEvent}The cache has started to participate in a transaction
{@link TransactionCompleted}{@link org.jboss.cache.notifications.event.TransactionCompletedEvent}The cache has completed it's participation in a transaction
{@link BuddyGroupChanged}{@link org.jboss.cache.notifications.event.BuddyGroupChangedEvent}Buddy replication is enabled and one of the buddy groups that the instance is a member of has changed it's membership.
*

*

Example 1 - Method receiving a single event

*
 *    @CacheListener
 *    public class SingleEventListener
 *    {
 *       @CacheStarted
 *       public void doSomething(Event event)
 *       {
 *          System.out.println("Cache started.  Details = " + event);
 *       }
 *    }
 * 
*

*

Example 2 - Method receiving multiple events

*
 *    @CacheListener
 *    public class MultipleEventListener
 *    {
 *       @CacheStarted
 *       @CacheStopped
 *       public void doSomething(Event event)
 *       {
 *          if (event.getType() == Event.Type.CACHE_STARTED)
 *             System.out.println("Cache started.  Details = " + event);
 *          else if (event.getType() == Event.Type.CACHE_STOPPED)
 *             System.out.println("Cache stopped.  Details = " + event);
 *       }
 *    }
 * 
*

*

Example 3 - Multiple methods receiving the same event

*
 *    @CAcheListener
 *    public class SingleEventListener
 *    {
 *       @CacheStarted
 *       public void handleStart(Event event)
 *       {
 *          System.out.println("Cache started");
 *       }
 * 

* @CacheStarted * @CacheStopped * @CacheBlocked * @CacheUnblocked * @ViewChanged * public void logEvent(Event event) * { * logSystem.logEvent(event.getType()); * } * } *

*

*

* Example 4 - Processing only events with a committed transaction. *

*

 *    @CacheListener
 *    public class TxGuaranteedListener
 *    {
 *       private class TxEventQueue
 *       {
 *          private ConcurrentMap<Transaction, Queue<Event>> map = new ConcurrentHashMap<Transaction, Queue<Event>>();
 * 

* public void offer(Event event) * { * Queue<Event> queue = getQueue(event.getContext().getTransaction()); * queue.offer(event); * } *

* private Queue<Event> getQueue(Transaction transaction) * { * Queue<Event> queue = map.get(transaction); * if (queue == null) * { * queue = new ConcurrentLinkedQueue<Event>(); * map.putIfAbsent(transaction, queue); * } *

* return queue; * } *

* public Queue<Event> takeAll(Transaction transaction) * { * return map.remove(transaction); * } * } *

* private TxEventQueue events = new TxEventQueue(); *

* @NodeModified * @NodeMoved * @NodeCreated * @NodeRemoved * public void handle(Event event) * { * events.offer(event); * } *

* @TransactionCompleted * public void handleTx(TransactionCompletedEvent event) * { * Queue<Event> completed = events.takeAll(event.getTransaction()); * if (completed != null && event.isSuccessful()) * System.out.println("Comitted events = " + completed); * } * } *

* * @author Manik Surtani * @author Jason T. Greene * @see CacheStarted * @see CacheStopped * @see NodeModified * @see NodeMoved * @see NodeCreated * @see NodeRemoved * @see NodeVisited * @see NodeLoaded * @see NodeEvicted * @see NodeActivated * @see NodePassivated * @see ViewChanged * @see CacheBlocked * @see CacheUnblocked * @see TransactionCompleted * @see TransactionRegistered * @see BuddyGroupChanged * @see org.jboss.cache.Cache#addCacheListener(Object) * @see org.jboss.cache.Cache#addCacheListener(org.jboss.cache.Fqn,Object) * @see org.jboss.cache.Cache#removeCacheListener(Object) * @see org.jboss.cache.Cache#removeCacheListener(org.jboss.cache.Fqn,Object) * @see org.jboss.cache.Cache#getCacheListeners() * @see org.jboss.cache.Cache#getCacheListeners(org.jboss.cache.Fqn) * @since 2.0.0 */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface CacheListener { } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/notifications/annotation/NodeModified.java0000644000175000017500000000150510640763624032626 0ustar twernertwernerpackage org.jboss.cache.notifications.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * This annotation should be used on methods that need to be notified when a node has been modified. *

* Methods annotated with this annotation should be public and take in a single parameter, a {@link org.jboss.cache.notifications.event.NodeModifiedEvent} * otherwise an {@link org.jboss.cache.notifications.IncorrectCacheListenerException} will be thrown when registering * your cache listener. *

* * @author Manik Surtani * @see CacheListener * @since 2.0.0 */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface NodeModified { } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/SuspectException.java0000644000175000017500000000106110775146760026564 0ustar twernertwernerpackage org.jboss.cache; /** * Thrown when a member is suspected during remote method invocation * * @author Bela Ban * @version $Id: SuspectException.java 5498 2008-04-03 12:30:40Z manik.surtani@jboss.com $ */ public class SuspectException extends CacheException { private static final long serialVersionUID = -2965599037371850141L; public SuspectException() { super(); } public SuspectException(String msg) { super(msg); } public SuspectException(String msg, Throwable cause) { super(msg, cause); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/interceptors/0000755000175000017500000000000011376174015025127 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/interceptors/TxInterceptorMBean.java0000644000175000017500000000321510365750360031510 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * Copyright 2005, JBoss Inc., and individual contributors as indicated * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.cache.interceptors; /** * Interface capturing transaction statistics * @author Jerry Gauthier * @version $Id: TxInterceptorMBean.java 1071 2006-01-25 19:26:40Z jerrygauth $ */ public interface TxInterceptorMBean extends InterceptorMBean { /** * Returns the number of transaction prepares * * @return the number of prepares */ long getPrepares(); /** * Returns the number of transaction commits * * @return the number of commits */ long getCommits(); /** * Returns the number of transaction rollbacks * * @return the number of rollbacks */ long getRollbacks(); } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/interceptors/Interceptor.java0000644000175000017500000001077411032145203030263 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * Copyright 2005, JBoss Inc., and individual contributors as indicated * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.cache.interceptors; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.NodeSPI; import org.jboss.cache.commands.VisitableCommand; import org.jboss.cache.factories.annotations.Inject; import org.jboss.cache.factories.annotations.Start; import org.jboss.cache.interceptors.base.CommandInterceptor; /** * Class representing an interceptor. * * @author Bela Ban * @version $Id: Interceptor.java 6120 2008-06-30 11:58:59Z manik.surtani@jboss.com $ * @deprecated this will be removed in a 3.x release. Please use {@link org.jboss.cache.interceptors.base.CommandInterceptor} instead, since it provides strongly typed callbacks which are more efficient. */ @Deprecated public abstract class Interceptor extends CommandInterceptor { protected CacheSPI cache; protected boolean trace; public void setCache(CacheSPI cache) { this.cache = cache; } @Start private void start() { // for backward compatibility, this must only be done when the cache starts. setCache(cache); } @Inject private void injectDependencies(CacheSPI cache) { this.cache = cache; } /** * Using this method call for forwarding a call in the chain is not redable and error prone in the case of interceptors * extending other interceptors. This metod rather refers to interceptor doing its business operations rather than * delegating to the nextInterceptor interceptor in chain. For delegation please use {@link #nextInterceptor(org.jboss.cache.InvocationContext)} */ public Object invoke(InvocationContext ctx) throws Throwable { return handleDefault(ctx, null); } /** * Forwards the call to the nextInterceptor interceptor in the chain. * This is here for backward compatibility. */ public Object nextInterceptor(InvocationContext ctx) throws Throwable { return invokeNextInterceptor(ctx, null); } @Override public String toString() { return getClass().getName() + "{next: " + (getNext() == null ? null : getNext().getClass()) + "}"; } /** * First checks the invocation context for previously obtained reference to a node, if this doesn't exist, performs * a cache.peek() and holds on to the node reference. * * @param ctx invocation context * @param f fqn to find * @param forceRefresh forces calling cache.peek() even if we hold a reference to the relevant node. * @param includeDeletedNodes includes nodes marked for deletion if this is true * @param includeInvalidNodes includes nodes marked as invalid if this is true * @return a node, or null if one cannot be found. * @since 2.1.0 */ public NodeSPI peekNode(InvocationContext ctx, Fqn f, boolean forceRefresh, boolean includeDeletedNodes, boolean includeInvalidNodes) { return cache.peek(f, includeDeletedNodes, includeInvalidNodes); } @Override @SuppressWarnings("deprecation") public Object handleDefault(InvocationContext ctx, VisitableCommand command) throws Throwable { if (command != null) //call originated from a command's accept() method. { return invoke(ctx); } //this means that another Interceptor called this method, we have to dispatch the call to the appropriate method. Probably called directly using super.invoke(). command = ctx.getCommand(); return command.acceptVisitor(ctx, getNext()); } } ././@LongLink0000000000000000000000000000016000000000000011562 Lustar rootrootjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/interceptors/OptimisticCreateIfNotExistsInterceptor.javajbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/interceptors/OptimisticCreateIfNotExistsInter0000755000175000017500000002560511032145203033461 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.interceptors; import org.jboss.cache.CacheException; import org.jboss.cache.CacheSPI; import org.jboss.cache.DataContainer; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.NodeFactory; import org.jboss.cache.NodeSPI; import org.jboss.cache.commands.write.MoveCommand; import org.jboss.cache.commands.write.PutDataMapCommand; import org.jboss.cache.commands.write.PutForExternalReadCommand; import org.jboss.cache.commands.write.PutKeyValueCommand; import org.jboss.cache.factories.annotations.Inject; import org.jboss.cache.factories.annotations.Start; import org.jboss.cache.notifications.Notifier; import org.jboss.cache.optimistic.DataVersion; import org.jboss.cache.optimistic.DefaultDataVersion; import org.jboss.cache.optimistic.TransactionWorkspace; import org.jboss.cache.optimistic.WorkspaceNode; import org.jboss.cache.transaction.GlobalTransaction; import java.util.ArrayList; import java.util.List; /** * Used to create new {@link NodeSPI} instances in the main data structure and then copy it into the * {@link TransactionWorkspace} as {@link WorkspaceNode}s as needed. This is only invoked if nodes needed do not exist * in the underlying structure, they are added and the corresponding {@link org.jboss.cache.optimistic.WorkspaceNode#isCreated()} * would return true to denote that this node has been freshly created in the underlying structure. * * @author Manik Surtani (manik@jboss.org) * @author Steve Woodcock (stevew@jofti.com) */ public class OptimisticCreateIfNotExistsInterceptor extends OptimisticInterceptor { /** * A reference to the node factory registered with the cache instance, used to create both WorkspaceNodes as well as * NodeSPI objects in the underlying data structure. */ private NodeFactory nodeFactory; private DataContainer dataContainer; private CacheSPI cache; private long lockAcquisitionTimeout; @Inject private void injectDependencies(NodeFactory nodeFactory, DataContainer dataContainer, CacheSPI cacheSPI) { this.nodeFactory = nodeFactory; this.dataContainer = dataContainer; this.cache = cacheSPI; } @Start private void init() { lockAcquisitionTimeout = configuration.getLockAcquisitionTimeout(); } @Override public Object visitPutDataMapCommand(InvocationContext ctx, PutDataMapCommand command) throws Throwable { createNode(ctx, command.getFqn(), false); return invokeNextInterceptor(ctx, command); } @Override public Object visitPutForExternalReadCommand(InvocationContext ctx, PutForExternalReadCommand command) throws Throwable { createNode(ctx, command.getFqn(), false); return invokeNextInterceptor(ctx, command); } @Override public Object visitPutKeyValueCommand(InvocationContext ctx, PutKeyValueCommand command) throws Throwable { createNode(ctx, command.getFqn(), false); return invokeNextInterceptor(ctx, command); } @Override public Object visitMoveCommand(InvocationContext ctx, MoveCommand command) throws Throwable { List fqns = new ArrayList(); fqns.add(command.getTo()); // peek into Node and get a hold of all child fqns as these need to be in the workspace. NodeSPI node = dataContainer.peek(command.getFqn(), true, true); greedyGetFqns(fqns, node, command.getTo()); if (trace) log.trace("Adding Fqns " + fqns + " for a move() operation."); for (Fqn f : fqns) { createNode(ctx, f, true); } return invokeNextInterceptor(ctx, command); } /** * The only method that should be creating nodes. * * @param targetFqn * @throws CacheException */ private void createNode(InvocationContext ctx, Fqn targetFqn, boolean suppressNotification) throws CacheException { if (dataContainer.peek(targetFqn, false, false) != null) return; // we do nothing if targetFqn is null if (targetFqn == null) return; boolean debug = log.isDebugEnabled(); GlobalTransaction gtx = getGlobalTransaction(ctx); TransactionWorkspace workspace = getTransactionWorkspace(ctx); WorkspaceNode workspaceNode; List nodesCreated = new ArrayList(); DataVersion version = null; if (ctx.getOptionOverrides() != null && ctx.getOptionOverrides().getDataVersion() != null) { version = ctx.getOptionOverrides().getDataVersion(); workspace.setVersioningImplicit(false); } // start with the ROOT node and then work our way down to the node necessary, creating nodes along the way. workspaceNode = workspace.getNode(Fqn.ROOT); if (debug) log.debug("GlobalTransaction: " + gtx + "; Root: " + workspaceNode); // we do not have the root in the workspace! Put it into thr workspace now. if (workspaceNode == null) { NodeSPI node = dataContainer.getRoot(); workspaceNode = lockAndCreateWorkspaceNode(nodeFactory, node, workspace, gtx, lockAcquisitionTimeout); workspace.addNode(workspaceNode); log.debug("Created root node in workspace."); } else { log.debug("Found root node in workspace."); } // iterate through the target Fqn's elements. int targetFqnSize = targetFqn.size(), currentDepth = 1; for (Object childName : targetFqn.peekElements()) { boolean isTargetFqn = (currentDepth == targetFqnSize); currentDepth++; // current workspace node canot be null. // try and get the child of current node if (debug) log.debug("Attempting to get child " + childName); NodeSPI currentNode = workspaceNode.getNode().getChildDirect(childName); if (currentNode == null) { // first test that it exists in the workspace and has been created in thix tx! WorkspaceNode peekInWorkspace = workspace.getNode(Fqn.fromRelativeElements(workspaceNode.getFqn(), childName)); if (peekInWorkspace != null && peekInWorkspace.isCreated()) { // exists in workspace and has just been created. currentNode = peekInWorkspace.getNode(); if (peekInWorkspace.isDeleted()) { peekInWorkspace.markAsDeleted(false); // add in parent again workspaceNode.addChild(peekInWorkspace); } } } if (currentNode == null) { // no child exists with this name; create it in the underlying data structure and then add it to the workspace. if (trace) log.trace("Creating new child, since it doesn't exist in the cache."); // we put the parent node into the workspace as we are changing it's children. // at this point "workspaceNode" refers to the parent of the current node. It should never be null if // you got this far! if (workspaceNode.isDeleted()) { //add a new one or overwrite an existing one that has been deleted if (trace) log.trace("Parent node doesn't exist in workspace or has been deleted. Adding to workspace."); workspace.addNode(workspaceNode); if (!(workspaceNode.getVersion() instanceof DefaultDataVersion)) workspaceNode.setVersioningImplicit(false); } else { if (trace) log.trace("Parent node exists: " + workspaceNode); } // get the version passed in, if we need to use explicit versioning. DataVersion versionToPassIn = null; if (isTargetFqn && !workspace.isVersioningImplicit()) versionToPassIn = version; NodeSPI newUnderlyingChildNode = workspaceNode.createChild(childName, workspaceNode.getNode(), cache, versionToPassIn); // now assign "workspaceNode" to the new child created. workspaceNode = lockAndCreateWorkspaceNode(nodeFactory, newUnderlyingChildNode, workspace, gtx, lockAcquisitionTimeout); workspaceNode.setVersioningImplicit(versionToPassIn == null || !isTargetFqn); if (trace) log.trace("setting versioning of " + workspaceNode.getFqn() + " to be " + (workspaceNode.isVersioningImplicit() ? "implicit" : "explicit")); // now add the wrapped child node into the transaction space workspace.addNode(workspaceNode); workspaceNode.markAsCreated(); // save in list so we can broadcast our created nodes outside // the synch block nodesCreated.add(workspaceNode.getFqn()); } else { // node does exist but might not be in the workspace workspaceNode = workspace.getNode(currentNode.getFqn()); // wrap it up so we can put it in later if we need to if (workspaceNode == null) { if (trace) log.trace("Child node " + currentNode.getFqn() + " doesn't exist in workspace or has been deleted. Adding to workspace in gtx " + gtx); workspaceNode = lockAndCreateWorkspaceNode(nodeFactory, currentNode, workspace, gtx, lockAcquisitionTimeout); // if the underlying node is a tombstone then mark the workspace node as newly created if (!currentNode.isValid()) workspaceNode.markAsCreated(); if (isTargetFqn && !workspace.isVersioningImplicit()) { workspaceNode.setVersion(version); workspaceNode.setVersioningImplicit(false); } else { workspaceNode.setVersioningImplicit(true); } if (trace) log.trace("setting versioning of " + workspaceNode.getFqn() + " to be " + (workspaceNode.isVersioningImplicit() ? "implicit" : "explicit")); workspace.addNode(workspaceNode); } else if (workspaceNode.isDeleted()) { if (trace) log.trace("Found node but it is deleted in this workspace. Needs resurrecting."); undeleteWorkspaceNode(workspaceNode, workspace); } else { if (trace) log.trace("Found child node in the workspace: " + currentNode); } } } if (!suppressNotification && nodesCreated.size() > 0) { Notifier n = cache.getNotifier(); for (Fqn temp : nodesCreated) { n.notifyNodeCreated(temp, true, ctx); n.notifyNodeCreated(temp, false, ctx); if (trace) log.trace("Notifying cache of node created in workspace " + temp); } } } } ././@LongLink0000000000000000000000000000015300000000000011564 Lustar rootrootjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/interceptors/InvocationContextInterceptorMBean.javajbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/interceptors/InvocationContextInterceptorMBea0000644000175000017500000000036510477336565033513 0ustar twernertwernerpackage org.jboss.cache.interceptors; /** * MBean to the {@link InvocationContextInterceptor} * * @author Manik Surtani */ public interface InvocationContextInterceptorMBean extends InterceptorMBean { } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/interceptors/TxInterceptor.java0000644000175000017500000011676111075447240030617 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.interceptors; import org.jboss.cache.CacheException; import org.jboss.cache.InvocationContext; import org.jboss.cache.RPCManager; import org.jboss.cache.ReplicationException; import org.jboss.cache.commands.AbstractVisitor; import org.jboss.cache.commands.ReplicableCommand; import org.jboss.cache.commands.ReversibleCommand; import org.jboss.cache.commands.VisitableCommand; import org.jboss.cache.commands.tx.CommitCommand; import org.jboss.cache.commands.tx.OptimisticPrepareCommand; import org.jboss.cache.commands.tx.PrepareCommand; import org.jboss.cache.commands.tx.RollbackCommand; import org.jboss.cache.commands.write.ClearDataCommand; import org.jboss.cache.commands.write.CreateNodeCommand; import org.jboss.cache.commands.write.InvalidateCommand; import org.jboss.cache.commands.write.PutDataMapCommand; import org.jboss.cache.commands.write.PutKeyValueCommand; import org.jboss.cache.commands.write.RemoveKeyCommand; import org.jboss.cache.commands.write.RemoveNodeCommand; import org.jboss.cache.config.Option; import org.jboss.cache.factories.CommandsFactory; import org.jboss.cache.factories.ComponentRegistry; import org.jboss.cache.factories.annotations.Inject; import org.jboss.cache.invocation.InvocationContextContainer; import org.jboss.cache.lock.LockManager; import org.jboss.cache.notifications.Notifier; import org.jboss.cache.transaction.GlobalTransaction; import org.jboss.cache.transaction.TransactionEntry; import org.jboss.cache.transaction.TransactionTable; import org.jboss.cache.util.concurrent.ConcurrentHashSet; import javax.transaction.InvalidTransactionException; import javax.transaction.Status; import javax.transaction.Synchronization; import javax.transaction.SystemException; import javax.transaction.Transaction; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; /** * This interceptor is the new default at the head of all interceptor chains, * and makes transactional attributes available to all interceptors in the chain. * This interceptor is also responsible for registering for synchronisation on * transaction completion. * * @author Manik Surtani (manik@jboss.org) * @author Steve Woodcock (stevew@jofti.com) */ public class TxInterceptor extends BaseTransactionalContextInterceptor implements TxInterceptorMBean { protected CommandsFactory commandsFactory; protected RPCManager rpcManager; private Notifier notifier; private InvocationContextContainer invocationContextContainer; private ComponentRegistry componentRegistry; /** * List that we have registered for */ private final Set transactions = new ConcurrentHashSet(); private final Map rollbackTransactions = new ConcurrentHashMap(16); private long prepares = 0; private long commits = 0; private long rollbacks = 0; protected boolean optimistic = false; private LockManager lockManager; @Inject public void intialize(RPCManager rpcManager, Notifier notifier, InvocationContextContainer icc, CommandsFactory factory, ComponentRegistry componentRegistry, LockManager lockManager) { this.commandsFactory = factory; this.rpcManager = rpcManager; this.notifier = notifier; this.invocationContextContainer = icc; this.componentRegistry = componentRegistry; this.lockManager = lockManager; } @Override public Object visitPrepareCommand(InvocationContext ctx, PrepareCommand command) throws Throwable { Object result = null; // this is a prepare, commit, or rollback. if (trace) log.trace("Got gtx from invocation context " + ctx.getGlobalTransaction()); try { if (ctx.getGlobalTransaction().isRemote()) { result = handleRemotePrepare(ctx, command); if (getStatisticsEnabled()) prepares++; } else { if (trace) log.trace("received my own message (discarding it)"); result = null; } } catch (Throwable e) { ctx.throwIfNeeded(e); } return result; } @Override @SuppressWarnings("unchecked") public Object visitCommitCommand(InvocationContext ctx, CommitCommand command) throws Throwable { if (!ctx.getGlobalTransaction().isRemote()) { if (trace) log.trace("received my own message (discarding it)"); return null; } try { if (trace) log.trace("(" + rpcManager.getLocalAddress() + ") call on command [" + command + "]"); GlobalTransaction gtx = ctx.getGlobalTransaction(); Transaction ltx = txTable.getLocalTransaction(gtx, true); // disconnect if we have a current tx associated Transaction currentTx = txManager.getTransaction(); boolean resumeCurrentTxOnCompletion = false; try { if (!ltx.equals(currentTx)) { currentTx = txManager.suspend(); resumeCurrentTxOnCompletion = true; txManager.resume(ltx); // make sure we set this in the ctx ctx.setTransaction(ltx); } if (trace) log.trace(" executing commit() with local TX " + ltx + " under global tx " + gtx); txManager.commit(); if (getStatisticsEnabled()) commits++; } finally { //resume the old transaction if we suspended it if (resumeCurrentTxOnCompletion) { resumeTransactionOnCompletion(ctx, currentTx); } // remove from local lists. transactions.remove(ltx); // this tx has completed. Clean up in the tx table. txTable.remove(gtx, ltx); } if (trace) log.trace("Finished remote rollback method for " + gtx); } catch (Throwable throwable) { ctx.throwIfNeeded(throwable); } return null; } @Override public Object visitRollbackCommand(InvocationContext ctx, RollbackCommand command) throws Throwable { if (!ctx.getGlobalTransaction().isRemote()) { if (trace) log.trace("received my own message (discarding it)"); return null; } try { if (trace) log.trace("(" + rpcManager.getLocalAddress() + ") call on command [" + command + "]"); GlobalTransaction gtx = ctx.getGlobalTransaction(); Transaction ltx = txTable.getLocalTransaction(gtx); if (ltx == null) { log.warn("No local transaction for this remotely originating rollback. Possibly rolling back before a prepare call was broadcast?"); txTable.remove(gtx); return null; } // disconnect if we have a current tx associated Transaction currentTx = txManager.getTransaction(); boolean resumeCurrentTxOnCompletion = false; try { if (!ltx.equals(currentTx)) { currentTx = txManager.suspend(); resumeCurrentTxOnCompletion = true; txManager.resume(ltx); // make sure we set this in the ctx ctx.setTransaction(ltx); } if (trace) log.trace("executing with local TX " + ltx + " under global tx " + gtx); txManager.rollback(); if (getStatisticsEnabled()) rollbacks++; } finally { //resume the old transaction if we suspended it if (resumeCurrentTxOnCompletion) { resumeTransactionOnCompletion(ctx, currentTx); } // remove from local lists. transactions.remove(ltx); // this tx has completed. Clean up in the tx table. txTable.remove(gtx, ltx); } if (trace) log.trace("Finished remote commit/rollback method for " + gtx); } catch (Throwable throwable) { ctx.throwIfNeeded(throwable); } return null; } @Override public Object visitInvalidateCommand(InvocationContext ctx, InvalidateCommand command) throws Throwable { return invokeNextInterceptor(ctx, command); } /** * Tests if we already have a tx running. If so, register a sync handler for this method invocation. * if not, create a local tx if we're using opt locking. * * @return * @throws Throwable */ @Override public Object handleDefault(InvocationContext ctx, VisitableCommand command) throws Throwable { try { return attachGtxAndPassUpChain(ctx, command); } catch (Throwable throwable) { ctx.throwIfNeeded(throwable); return null; } } protected Object attachGtxAndPassUpChain(InvocationContext ctx, VisitableCommand command) throws Throwable { Transaction tx = ctx.getTransaction(); if (tx != null) attachGlobalTransaction(ctx, tx, command); return invokeNextInterceptor(ctx, command); } // ------------------------------------------------------------------------ // JMX statistics // ------------------------------------------------------------------------ public long getPrepares() { return prepares; } public long getCommits() { return commits; } public long getRollbacks() { return rollbacks; } @Override public void resetStatistics() { prepares = 0; commits = 0; rollbacks = 0; } @Override public Map dumpStatistics() { Map retval = new HashMap(3); retval.put("Prepares", prepares); retval.put("Commits", commits); retval.put("Rollbacks", rollbacks); return retval; } // -------------------------------------------------------------- /** * Handles a remotely originating prepare call, by creating a local transaction for the remote global transaction * and replaying modifications in this new local transaction. * * @param ctx invocation context * @param command prepare command * @return result of the prepare, typically a null. * @throws Throwable in the event of problems. */ private Object handleRemotePrepare(InvocationContext ctx, PrepareCommand command) throws Throwable { // the InvocationContextInterceptor would have set this for us GlobalTransaction gtx = ctx.getGlobalTransaction(); // Is there a local transaction associated with GTX? (not the current tx associated with the thread, which may be // in the invocation context Transaction ltx = txTable.getLocalTransaction(gtx); Transaction currentTx = txManager.getTransaction(); Object retval = null; boolean success = false; try { if (ltx == null) { if (currentTx != null) txManager.suspend(); // create a new local transaction ltx = createLocalTx(); // associate this with a global tx txTable.put(ltx, gtx); if (trace) log.trace("Created new tx for gtx " + gtx); if (trace) log.trace("Started new local tx as result of remote prepare: local tx=" + ltx + " (status=" + ltx.getStatus() + "), gtx=" + gtx); } else { //this should be valid if (!TransactionTable.isValid(ltx)) throw new CacheException("Transaction " + ltx + " not in correct state to be prepared"); //associate this thread with the local transaction associated with the global transaction, IF the localTx is NOT the current tx. if (currentTx == null || !ltx.equals(currentTx)) { if (trace) log.trace("Suspending current tx " + currentTx); txManager.suspend(); txManager.resume(ltx); } } if (trace) log.trace("Resuming existing tx " + ltx + ", global tx=" + gtx); // at this point we have a non-null ltx // Asssociate the local TX with the global TX. Create new // entry for TX in txTable, the modifications // below will need this entry to add their modifications // under the GlobalTx key TransactionEntry entry = txTable.get(gtx); if (entry == null) { // create a new transaction entry if (trace) log.trace("creating new tx entry"); entry = createNewTransactionEntry(ltx); txTable.put(gtx, entry); } setTransactionalContext(ltx, gtx, entry, ctx); // register a sync handler for this tx. registerHandler(ltx, new RemoteSynchronizationHandler(gtx, ltx, entry), ctx); success = false; // replay modifications replayModifications(ctx, ltx, command); success = true; // no exceptions were thrown above!! // now pass the prepare command up the chain as well. if (command.isOnePhaseCommit()) { if (trace) log.trace("Using one-phase prepare. Not propagating the prepare call up the stack until called to do so by the sync handler."); } else { // now pass up the prepare method itself. invokeNextInterceptor(ctx, command); } // JBCACHE-361 Confirm that the transaction is ACTIVE assertTxIsStillValid(ltx); } finally { // if we are running a one-phase commit, perform a commit or rollback now. if (trace) log.trace("Are we running a 1-phase commit? " + command.isOnePhaseCommit()); if (command.isOnePhaseCommit()) { try { if (success) { ltx.commit(); } else { ltx.rollback(); } } catch (Throwable t) { log.error("Commit/rollback failed.", t); if (success) { // try another rollback... try { log.info("Attempting anotehr rollback"); //invokeOnePhaseCommitMethod(globalTransaction, modifications.size() > 0, false); ltx.rollback(); } catch (Throwable t2) { log.error("Unable to rollback", t2); } } } finally { transactions.remove(ltx);// JBAS-298 } } txManager.suspend();// suspends ltx - could be null // resume whatever else we had going. if (currentTx != null) txManager.resume(currentTx); if (trace) log.trace("Finished remote prepare " + gtx); } return retval; } protected TransactionEntry createNewTransactionEntry(Transaction tx) throws Exception { return new TransactionEntry(tx); } private ReplicableCommand attachGlobalTransaction(InvocationContext ctx, Transaction tx, VisitableCommand command) throws Throwable { if (trace) { log.trace(" local transaction exists - registering global tx if not present for " + Thread.currentThread()); } if (trace) { GlobalTransaction tempGtx = txTable.get(tx); log.trace("Associated gtx in txTable is " + tempGtx); } // register a sync handler for this tx - only if the globalTransaction is not remotely initiated. GlobalTransaction gtx = registerTransaction(tx, ctx); if (gtx != null) { command = replaceGtx(command, gtx); } else { // get the current globalTransaction from the txTable. gtx = txTable.get(tx); } // make sure we attach this globalTransaction to the invocation context. ctx.setGlobalTransaction(gtx); return command; } /** * Replays modifications * * @param ctx * @param ltx * @param command * @return * @throws Exception */ protected void replayModifications(InvocationContext ctx, Transaction ltx, PrepareCommand command) throws Throwable { try { // replay modifications for (ReversibleCommand modification : command.getModifications()) { invokeNextInterceptor(ctx, modification); assertTxIsStillValid(ltx); } } catch (Throwable th) { log.error("prepare failed!", th); throw th; } } private void resumeTransactionOnCompletion(InvocationContext ctx, Transaction currentTx) throws SystemException, InvalidTransactionException { if (trace) log.trace("Resuming suspended transaction " + currentTx); txManager.suspend(); if (currentTx != null) { txManager.resume(currentTx); ctx.setTransaction(currentTx); } } /** * Handles a commit or a rollback. Called by the synch handler. Simply tests that we are in the correct tx and * passes the meth call up the interceptor chain. * * @return * @throws Throwable */ @SuppressWarnings("deprecation") private Object handleCommitRollback(InvocationContext ctx, VisitableCommand command) throws Throwable { GlobalTransaction gtx = ctx.getGlobalTransaction(); Object result; VisitableCommand originalCommand = ctx.getCommand(); ctx.setCommand(command); try { result = invokeNextInterceptor(ctx, command); } finally { ctx.setCommand(originalCommand); ctx.setMethodCall(null); } if (trace) log.trace("Finished local commit/rollback method for " + gtx); return result; } // -------------------------------------------------------------- // Transaction phase runners // -------------------------------------------------------------- protected PrepareCommand buildPrepareCommand(GlobalTransaction gtx, List modifications, boolean onePhaseCommit) { return commandsFactory.buildPrepareCommand(gtx, modifications, rpcManager.getLocalAddress(), onePhaseCommit); } /** * creates a commit() */ protected void runCommitPhase(InvocationContext ctx, GlobalTransaction gtx, List modifications, boolean onePhaseCommit) { // set the hasMods flag in the invocation ctx. This should not be replicated, just used locally by the interceptors. ctx.setTxHasMods(modifications != null && modifications.size() > 0); try { VisitableCommand commitCommand = onePhaseCommit ? buildPrepareCommand(gtx, modifications, true) : commandsFactory.buildCommitCommand(gtx); if (trace) log.trace("Running commit for " + gtx); handleCommitRollback(ctx, commitCommand); } catch (Throwable e) { log.warn("Commit failed. Clearing stale locks."); try { cleanupStaleLocks(ctx); } catch (RuntimeException re) { log.error("Unable to clear stale locks", re); throw re; } catch (Throwable e2) { log.error("Unable to clear stale locks", e2); throw new RuntimeException(e2); } if (e instanceof RuntimeException) throw (RuntimeException) e; else throw new RuntimeException("Commit failed.", e); } } protected void cleanupStaleLocks(InvocationContext ctx) throws Throwable { TransactionEntry entry = ctx.getTransactionEntry(); if (entry != null) lockManager.unlock(ctx); } /** * creates a rollback() */ protected void runRollbackPhase(InvocationContext ctx, GlobalTransaction gtx, Transaction tx, List modifications) { try { ctx.setTxHasMods(modifications != null && modifications.size() > 0); // JBCACHE-457 VisitableCommand rollbackCommand = commandsFactory.buildRollbackCommand(gtx); if (trace) log.trace(" running rollback for " + gtx); //JBCACHE-359 Store a lookup for the globalTransaction so a listener // callback can find it rollbackTransactions.put(tx, gtx); handleCommitRollback(ctx, rollbackCommand); } catch (Throwable e) { log.warn("Rollback had a problem", e); } finally { rollbackTransactions.remove(tx); } } /** * An optimisation of commands to be broadcast in a prepare call, which involves removing of unnecessary commands * as well as the "aggregation" of commands that can be aggregated. * * @param mods list of modifications * @return compacted list of modifications */ private List compact(List mods) { // TODO: 3.0.0: Make this more sophisticated, so it aggregates multiple puts on the same node, puts followed by a remove, etc. // for now this just removes the redundant CreateNodeCommands from the list. List newList = new LinkedList(); for (ReversibleCommand cmd : mods) { if (!(cmd instanceof CreateNodeCommand)) newList.add(cmd); } return newList; } private boolean isOnePhaseCommit() { if (!configuration.getCacheMode().isSynchronous() && !optimistic) { // this is a REPL_ASYNC call - do 1-phase commit. break! if (trace) log.trace("This is a REPL_ASYNC call (1 phase commit) - do nothing for beforeCompletion()"); return true; } return false; } /** * Handles a local prepare - invoked by the sync handler. Tests if the current tx matches the gtx passed in to the * method call and passes the prepare() call up the chain. */ @SuppressWarnings("deprecation") public Object runPreparePhase(InvocationContext ctx, GlobalTransaction gtx, List modifications) throws Throwable { // running a 2-phase commit. VisitableCommand prepareCommand = buildPrepareCommand(gtx, modifications, false); Object result; // Is there a local transaction associated with GTX ? Transaction ltx = ctx.getTransaction(); //if ltx is not null and it is already running Transaction currentTransaction = txManager.getTransaction(); if (currentTransaction != null && ltx != null && currentTransaction.equals(ltx)) { VisitableCommand originalCommand = ctx.getCommand(); ctx.setCommand(prepareCommand); // set the hasMods flag in the invocation ctx. This should not be replicated, just used locally by the interceptors. ctx.setTxHasMods(modifications != null && modifications.size() > 0); try { result = invokeNextInterceptor(ctx, prepareCommand); } finally { ctx.setCommand(originalCommand); ctx.setMethodCall(null); } } else { log.warn("Local transaction does not exist or does not match expected transaction " + gtx); throw new CacheException(" local transaction " + ltx + " does not exist or does not match expected transaction " + gtx); } return result; } // -------------------------------------------------------------- // Private helper methods // -------------------------------------------------------------- protected void assertTxIsStillValid(Transaction tx) { if (!TransactionTable.isActive(tx)) { try { throw new ReplicationException("prepare() failed -- local transaction status is not STATUS_ACTIVE; is " + tx.getStatus()); } catch (SystemException e) { throw new ReplicationException("prepare() failed -- local transaction status is not STATUS_ACTIVE; Unable to retrieve transaction status."); } } } /** * Creates a gtx (if one doesnt exist), a sync handler, and registers the tx. * * @param tx * @return * @throws Exception */ private GlobalTransaction registerTransaction(Transaction tx, InvocationContext ctx) throws Exception { GlobalTransaction gtx; if (TransactionTable.isValid(tx) && transactions.add(tx)) { gtx = txTable.getCurrentTransaction(tx, true); TransactionEntry entry; if (ctx.getGlobalTransaction() == null) { ctx.setGlobalTransaction(gtx); entry = txTable.get(gtx); ctx.setTransactionEntry(entry); } else { entry = ctx.getTransactionEntry(); } if (gtx.isRemote()) { // should be no need to register a handler since this a remotely initiated globalTransaction if (trace) log.trace("is a remotely initiated gtx so no need to register a tx for it"); } else { if (trace) log.trace("Registering sync handler for tx " + tx + ", gtx " + gtx); // see the comment in the LocalSyncHandler for the last isOriginLocal param. LocalSynchronizationHandler myHandler = new LocalSynchronizationHandler(gtx, tx, entry, !ctx.isOriginLocal()); registerHandler(tx, myHandler, ctx); } } else if ((gtx = rollbackTransactions.get(tx)) != null) { if (trace) log.trace("Transaction " + tx + " is already registered and is rolling back."); } else { if (trace) log.trace("Transaction " + tx + " is already registered."); } return gtx; } /** * Registers a sync hander against a tx. * * @param tx * @param handler * @throws Exception */ private void registerHandler(Transaction tx, Synchronization handler, InvocationContext ctx) throws Exception { OrderedSynchronizationHandler orderedHandler = ctx.getTransactionEntry().getOrderedSynchronizationHandler(); //OrderedSynchronizationHandler.getInstance(tx); if (trace) log.trace("registering for TX completion: SynchronizationHandler(" + handler + ")"); orderedHandler.registerAtHead(handler);// needs to be invoked first on TX commit notifier.notifyTransactionRegistered(tx, ctx); } /** * Replaces the global transaction in a VisitableCommand with a new global transaction passed in. */ private VisitableCommand replaceGtx(VisitableCommand command, final GlobalTransaction gtx) throws Throwable { command.acceptVisitor(null, new AbstractVisitor() { @Override public Object visitPutDataMapCommand(InvocationContext ctx, PutDataMapCommand command) throws Throwable { command.setGlobalTransaction(gtx); return null; } @Override public Object visitPutKeyValueCommand(InvocationContext ctx, PutKeyValueCommand command) throws Throwable { command.setGlobalTransaction(gtx); return null; } @Override public Object visitClearDataCommand(InvocationContext ctx, ClearDataCommand command) throws Throwable { command.setGlobalTransaction(gtx); return null; } @Override public Object visitRemoveNodeCommand(InvocationContext ctx, RemoveNodeCommand command) throws Throwable { command.setGlobalTransaction(gtx); return null; } @Override public Object visitRemoveKeyCommand(InvocationContext ctx, RemoveKeyCommand command) throws Throwable { command.setGlobalTransaction(gtx); return null; } @Override public Object visitCommitCommand(InvocationContext ctx, CommitCommand command) throws Throwable { command.setGlobalTransaction(gtx); return null; } @Override public Object visitPrepareCommand(InvocationContext ctx, PrepareCommand command) throws Throwable { command.setGlobalTransaction(gtx); return null; } @Override public Object visitRollbackCommand(InvocationContext ctx, RollbackCommand command) throws Throwable { command.setGlobalTransaction(gtx); return null; } @Override public Object visitOptimisticPrepareCommand(InvocationContext ctx, OptimisticPrepareCommand command) throws Throwable { command.setGlobalTransaction(gtx); return null; } }); return command; } /** * Creates and starts a local tx * * @return * @throws Exception */ protected Transaction createLocalTx() throws Exception { if (trace) { log.trace("Creating transaction for thread " + Thread.currentThread()); } Transaction localTx; if (txManager == null) throw new Exception("Failed to create local transaction; TransactionManager is null"); txManager.begin(); localTx = txManager.getTransaction(); return localTx; } // ------------------------------------------------------------------------ // Synchronization classes // ------------------------------------------------------------------------ // this controls the whole transaction private class RemoteSynchronizationHandler implements Synchronization { Transaction tx = null; GlobalTransaction gtx = null; List modifications = null; TransactionEntry entry = null; protected InvocationContext ctx; // the context for this call. RemoteSynchronizationHandler(GlobalTransaction gtx, Transaction tx, TransactionEntry entry) { this.gtx = gtx; this.tx = tx; this.entry = entry; } public void beforeCompletion() { if (trace) log.trace("Running beforeCompletion on gtx " + gtx); if (entry == null) { log.error("Transaction has a null transaction entry - beforeCompletion() will fail."); throw new IllegalStateException("cannot find transaction entry for " + gtx); } modifications = entry.getModifications(); ctx = invocationContextContainer.get(); setTransactionalContext(tx, gtx, entry, ctx); if (ctx.isOptionsUninitialised() && entry.getOption() != null) ctx.setOptionOverrides(entry.getOption()); assertCanContinue(); ctx.setOriginLocal(false); } // this should really not be done here - // it is supposed to be post commit not actually run the commit public void afterCompletion(int status) { // could happen if a rollback is called and beforeCompletion() doesn't get called. if (ctx == null) { ctx = invocationContextContainer.get(); setTransactionalContext(tx, gtx, entry, ctx); if (ctx.isOptionsUninitialised() && entry != null && entry.getOption() != null) { // use the options from the transaction entry instead ctx.setOptionOverrides(entry.getOption()); } } try { assertCanContinue(); try { if (txManager.getTransaction() != null && !txManager.getTransaction().equals(tx)) txManager.resume(tx); } catch (Exception e) { log.error("afterCompletion error: " + status, e); } if (trace) log.trace("calling aftercompletion for " + gtx); // set any transaction wide options as current for this thread. if (entry != null) { // this should ideally be set in beforeCompletion(), after compacting the list. if (modifications == null) modifications = entry.getModifications(); ctx.setOptionOverrides(entry.getOption()); } if (tx != null) transactions.remove(tx); switch (status) { case Status.STATUS_COMMITTED: boolean onePhaseCommit = isOnePhaseCommit(); if (trace) log.trace("Running commit phase. One phase? " + onePhaseCommit); runCommitPhase(ctx, gtx, modifications, onePhaseCommit); log.trace("Finished commit phase"); break; case Status.STATUS_UNKNOWN: log.warn("Received JTA STATUS_UNKNOWN in afterCompletion()! XA resources may not be in sync. The app should manually clean up resources at this point."); case Status.STATUS_MARKED_ROLLBACK: case Status.STATUS_ROLLEDBACK: log.trace("Running rollback phase"); runRollbackPhase(ctx, gtx, tx, modifications); log.trace("Finished rollback phase"); break; default: throw new IllegalStateException("illegal status: " + status); } } catch (Exception th) { log.trace("Caught exception ", th); } finally { // clean up the tx table txTable.remove(gtx); txTable.remove(tx); setTransactionalContext(null, null, null, ctx); cleanupInternalState(); } } private void assertCanContinue() { if (!componentRegistry.invocationsAllowed(true) && (ctx.getOptionOverrides() == null || !ctx.getOptionOverrides().isSkipCacheStatusCheck())) throw new IllegalStateException("Cache not in STARTED state!"); } /** * Cleans out (nullifies) member variables held by the sync object for easier gc. Could be (falsely) seen as a mem * leak if the TM implementation hangs on to the synchronizations for an unnecessarily long time even after the tx * completes. See JBCACHE-1007. */ private void cleanupInternalState() { tx = null; gtx = null; modifications = null; if (entry != null) entry.reset(); entry = null; } @Override public String toString() { return "TxInterceptor.RemoteSynchronizationHandler(gtx=" + gtx + ", tx=" + getTxAsString() + ")"; } protected String getTxAsString() { // JBCACHE-1114 -- don't call toString() on tx or it can lead to stack overflow if (tx == null) return null; return tx.getClass().getName() + "@" + System.identityHashCode(tx); } } private class LocalSynchronizationHandler extends RemoteSynchronizationHandler { private boolean localRollbackOnly = true; // a VERY strange situation where a tx has remote origins, but since certain buddy group org methods perform local // cleanups even when remotely triggered, and optimistic locking is used, you end up with an implicit local tx. // This is STILL remotely originating though and this needs to be made explicit here. // this can be checked by inspecting the InvocationContext.isOriginLocal() at the time of registering the sync. private boolean remoteLocal = false; private Option originalOptions, transactionalOptions; /** * A Synchronization for locally originating txs. *

* a VERY strange situation where a tx has remote origins, but since certain buddy group org methods perform local * cleanups even when remotely triggered, and optimistic locking is used, you end up with an implicit local tx. * This is STILL remotely originating though and this needs to be made explicit here. * this can be checked by inspecting the InvocationContext.isOriginLocal() at the time of registering the sync. * * @param gtx * @param tx * @param remoteLocal */ LocalSynchronizationHandler(GlobalTransaction gtx, Transaction tx, TransactionEntry entry, boolean remoteLocal) { super(gtx, tx, entry); this.remoteLocal = remoteLocal; } @Override public void beforeCompletion() { super.beforeCompletion(); ctx.setOriginLocal(!remoteLocal); // this is the LOCAL sync handler after all! // fetch the modifications before the transaction is committed // (and thus removed from the txTable) setTransactionalContext(tx, gtx, entry, ctx); if (!entry.hasModifications()) { if (trace) log.trace("No modifications in this tx. Skipping beforeCompletion()"); modifications = Collections.emptyList(); return; } // set any transaction wide options as current for this thread, caching original options that would then be reset originalOptions = ctx.getOptionOverrides(); transactionalOptions = entry.getOption(); ctx.setOptionOverrides(transactionalOptions); try { switch (tx.getStatus()) { // if we are active or preparing then we can go ahead case Status.STATUS_ACTIVE: case Status.STATUS_PREPARING: // run a prepare call. modifications = compact(modifications); Object result = isOnePhaseCommit() ? null : runPreparePhase(ctx, gtx, modifications); if (result instanceof Throwable) { if (log.isDebugEnabled()) log.debug("Transaction needs to be rolled back - the cache returned an instance of Throwable for this prepare call (tx=" + tx + " and gtx=" + gtx + ")", (Throwable) result); tx.setRollbackOnly(); throw (Throwable) result; } break; default: throw new CacheException("transaction " + tx + " in status " + tx.getStatus() + " unable to start transaction"); } } catch (Throwable t) { if (log.isWarnEnabled()) log.warn("Caught exception, will now set transaction to roll back", t); try { tx.setRollbackOnly(); } catch (SystemException se) { throw new RuntimeException("setting tx rollback failed ", se); } if (t instanceof RuntimeException) throw (RuntimeException) t; else throw new RuntimeException("", t); } finally { localRollbackOnly = false; setTransactionalContext(null, null, null, ctx); ctx.setOptionOverrides(originalOptions); } } @Override public void afterCompletion(int status) { // could happen if a rollback is called and beforeCompletion() doesn't get called. if (ctx == null) ctx = invocationContextContainer.get(); ctx.setLocalRollbackOnly(localRollbackOnly); setTransactionalContext(tx, gtx, entry, ctx); ctx.setOptionOverrides(transactionalOptions); try { super.afterCompletion(status); } finally { ctx.setOptionOverrides(originalOptions); } } @Override public String toString() { return "TxInterceptor.LocalSynchronizationHandler(gtx=" + gtx + ", tx=" + getTxAsString() + ")"; } } }jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/interceptors/CacheMgmtInterceptorMBean.java0000644000175000017500000000613110625013553032740 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * Copyright 2005, JBoss Inc., and individual contributors as indicated * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.cache.interceptors; /** * Interface capturing basic cache management statistics * * @author Jerry Gauthier * @version $Id: CacheMgmtInterceptorMBean.java 3892 2007-05-23 10:28:59Z msurtani $ */ public interface CacheMgmtInterceptorMBean extends InterceptorMBean { /** * Returns the number of cache attribute hits * * @return the number of cache hits */ long getHits(); /** * Returns the number of cache attribute misses * * @return the number of cache misses */ long getMisses(); /** * Returns the number of cache attribute put operations * * @return the number of cache put operations */ long getStores(); /** * Returns the number of cache eviction operations * * @return the number of cache eviction operations */ long getEvictions(); int getNumberOfAttributes(); int getNumberOfNodes(); /** * Returns the hit/miss ratio for the cache * This ratio is defined as hits/(hits + misses) * * @return the hit/miss ratio for the cache */ double getHitMissRatio(); /** * Returns the read/write ratio for the cache * This ratio is defined as (hits + misses)/stores * * @return the read/writes ratio for the cache */ double getReadWriteRatio(); /** * Returns average milliseconds for an attribute read operation * This includes both hits and misses. * * @return the average number of milliseconds for a read operation */ long getAverageReadTime(); /** * Returns average milliseconds for an attribute write operation * * @return the average number of milliseconds for a write operation */ long getAverageWriteTime(); /** * Returns seconds since cache started * * @return the number of seconds since the cache was started */ long getElapsedTime(); /** * Returns seconds since cache statistics reset * If statistics haven't been reset, this will be the same as ElapsedTime * * @return the number of seconds since the cache statistics were last reset */ long getTimeSinceReset(); } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/interceptors/ActivationInterceptor.java0000644000175000017500000002235511147256322032317 0ustar twernertwernerpackage org.jboss.cache.interceptors; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.Modification; import org.jboss.cache.NodeSPI; import org.jboss.cache.commands.read.GetChildrenNamesCommand; import org.jboss.cache.commands.read.GetKeyValueCommand; import org.jboss.cache.commands.read.GetKeysCommand; import org.jboss.cache.commands.read.GetNodeCommand; import org.jboss.cache.commands.tx.OptimisticPrepareCommand; import org.jboss.cache.commands.tx.PrepareCommand; import org.jboss.cache.commands.write.ClearDataCommand; import org.jboss.cache.commands.write.MoveCommand; import org.jboss.cache.commands.write.PutDataMapCommand; import org.jboss.cache.commands.write.PutForExternalReadCommand; import org.jboss.cache.commands.write.PutKeyValueCommand; import org.jboss.cache.commands.write.RemoveKeyCommand; import org.jboss.cache.commands.write.RemoveNodeCommand; import org.jboss.cache.factories.annotations.Inject; import org.jboss.cache.transaction.GlobalTransaction; import org.jboss.cache.transaction.TransactionEntry; import javax.transaction.SystemException; import javax.transaction.TransactionManager; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; /** * Loads nodes that don't exist at the time of the call into memory from the CacheLoader. * If the nodes were evicted earlier then we remove them from the cache loader after * their attributes have been initialized and their children have been loaded in memory. * * @author {Hany Mesha} * @version $Id: ActivationInterceptor.java 7733 2009-02-19 13:21:22Z manik.surtani@jboss.com $ */ public class ActivationInterceptor extends CacheLoaderInterceptor implements ActivationInterceptorMBean { protected TransactionManager txMgr = null; private long activations = 0; /** * List that we have registered for */ protected ConcurrentHashMap transactions = new ConcurrentHashMap(16); protected static final Object NULL = new Object(); public ActivationInterceptor() { isActivation = true; useCacheStore = false; } @Inject public void injectTransactionManager(TransactionManager txMgr) { this.txMgr = txMgr; } @Override public Object visitClearDataCommand(InvocationContext ctx, ClearDataCommand command) throws Throwable { Object returnValue = super.visitClearDataCommand(ctx, command); if (trace) log.trace("This is a remove data operation; removing the data from the loader, no activation processing needed."); loader.removeData(command.getFqn()); return returnValue; } @Override public Object visitRemoveNodeCommand(InvocationContext ctx, RemoveNodeCommand command) throws Throwable { Object returnValue = super.visitRemoveNodeCommand(ctx, command); if (trace) log.trace("This is a remove operation; removing the node from the loader, no activation processing needed."); loader.remove(command.getFqn()); return returnValue; } @Override public Object visitGetChildrenNamesCommand(InvocationContext ctx, GetChildrenNamesCommand command) throws Throwable { Object returnValue = super.visitGetChildrenNamesCommand(ctx, command); removeNodeFromCacheLoader(command.getFqn()); return returnValue; } @Override public Object visitGetKeysCommand(InvocationContext ctx, GetKeysCommand command) throws Throwable { Object returnValue = super.visitGetKeysCommand(ctx, command); removeNodeFromCacheLoader(command.getFqn()); return returnValue; } @Override public Object visitGetNodeCommand(InvocationContext ctx, GetNodeCommand command) throws Throwable { Object returnValue = super.visitGetNodeCommand(ctx, command); removeNodeFromCacheLoader(command.getFqn()); return returnValue; } @Override public Object visitGetKeyValueCommand(InvocationContext ctx, GetKeyValueCommand command) throws Throwable { Object returnValue = super.visitGetKeyValueCommand(ctx, command); removeNodeFromCacheLoader(command.getFqn()); return returnValue; } @Override public Object visitPutForExternalReadCommand(InvocationContext ctx, PutForExternalReadCommand command) throws Throwable { return visitPutKeyValueCommand(ctx, command); } @Override public Object visitPutKeyValueCommand(InvocationContext ctx, PutKeyValueCommand command) throws Throwable { Object returnValue = super.visitPutKeyValueCommand(ctx, command); removeNodeFromCacheLoader(command.getFqn()); return returnValue; } @Override public Object visitPutDataMapCommand(InvocationContext ctx, PutDataMapCommand command) throws Throwable { Object returnValue = super.visitPutDataMapCommand(ctx, command); removeNodeFromCacheLoader(command.getFqn()); return returnValue; } @Override public Object visitRemoveKeyCommand(InvocationContext ctx, RemoveKeyCommand command) throws Throwable { Object returnValue = super.visitRemoveKeyCommand(ctx, command); removeNodeFromCacheLoader(command.getFqn()); return returnValue; } @Override public Object visitMoveCommand(InvocationContext ctx, MoveCommand command) throws Throwable { Object returnValue = super.visitMoveCommand(ctx, command); if (trace) log.trace("This is a move operation; removing the FROM node from the loader, no activation processing needed."); loader.remove(command.getFqn()); removeNodeFromCacheLoader(command.getFqn().getParent()); removeNodeFromCacheLoader(command.getTo()); return returnValue; } /** * Remove the node from the cache loader if it exists in memory, * its attributes have been initialized, its children have been loaded, * AND it was found in the cache loader (nodeLoaded = true). * Then notify the listeners that the node has been activated. */ private void removeNodeFromCacheLoader(Fqn fqn) throws Throwable { NodeSPI n; if (((n = dataContainer.peek(fqn, true, false)) != null) && n.isDataLoaded() && loader.exists(fqn)) { // node not null and attributes have been loaded? if (!n.getChildrenDirect().isEmpty()) { boolean result = childrenLoaded(n); if (result) { log.debug("children all initialized"); remove(fqn); } } else if (loaderNoChildren(fqn)) { if (log.isDebugEnabled()) log.debug("no children " + n); remove(fqn); } } } private static boolean childrenLoaded(NodeSPI node) { if (!node.isChildrenLoaded()) { return false; } for (NodeSPI child : node.getChildrenDirect()) { if (!child.isDataLoaded()) { return false; } } return true; } @Override public Object visitOptimisticPrepareCommand(InvocationContext ctx, OptimisticPrepareCommand command) throws Throwable { Object retval = invokeNextInterceptor(ctx, command); if (inTransaction()) { prepareCacheLoader(ctx); } return retval; } private boolean inTransaction() throws SystemException { return txMgr != null && txMgr.getTransaction() != null; } @Override public Object visitPrepareCommand(InvocationContext ctx, PrepareCommand command) throws Throwable { Object retval = invokeNextInterceptor(ctx, command); if (inTransaction()) { prepareCacheLoader(ctx); } return retval; } private void remove(Fqn fqn) throws Exception { loader.remove(fqn); if (getStatisticsEnabled()) activations++; } /** * Returns true if the loader indicates no children for this node. * Return false on error. */ private boolean loaderNoChildren(Fqn fqn) { try { Set childrenNames = loader.getChildrenNames(fqn); return (childrenNames == null); } catch (Exception e) { log.error("failed getting the children names for " + fqn + " from the cache loader", e); return false; } } public long getActivations() { return activations; } @Override public void resetStatistics() { super.resetStatistics(); activations = 0; } @Override public Map dumpStatistics() { Map retval = super.dumpStatistics(); if (retval == null) { retval = new HashMap(); } retval.put("Activations", activations); return retval; } private void prepareCacheLoader(InvocationContext ctx) throws Throwable { GlobalTransaction gtx = ctx.getGlobalTransaction(); TransactionEntry entry = ctx.getTransactionEntry(); if (entry == null) { throw new Exception("entry for transaction " + gtx + " not found in transaction table"); } List cacheLoaderModifications = new ArrayList(); if (cacheLoaderModifications.size() > 0) { loader.prepare(gtx, cacheLoaderModifications, false); } } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/interceptors/CacheStoreInterceptor.java0000755000175000017500000003645711065737557032266 0ustar twernertwernerpackage org.jboss.cache.interceptors; import org.apache.commons.logging.LogFactory; import org.jboss.cache.DataContainer; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.Modification; import org.jboss.cache.NodeSPI; import org.jboss.cache.commands.AbstractVisitor; import org.jboss.cache.commands.ReversibleCommand; import org.jboss.cache.commands.VisitableCommand; import org.jboss.cache.commands.tx.CommitCommand; import org.jboss.cache.commands.tx.OptimisticPrepareCommand; import org.jboss.cache.commands.tx.PrepareCommand; import org.jboss.cache.commands.tx.RollbackCommand; import org.jboss.cache.commands.write.ClearDataCommand; import org.jboss.cache.commands.write.MoveCommand; import org.jboss.cache.commands.write.PutDataMapCommand; import org.jboss.cache.commands.write.PutForExternalReadCommand; import org.jboss.cache.commands.write.PutKeyValueCommand; import org.jboss.cache.commands.write.RemoveKeyCommand; import org.jboss.cache.commands.write.RemoveNodeCommand; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.factories.annotations.Inject; import org.jboss.cache.factories.annotations.Start; import org.jboss.cache.interceptors.base.SkipCheckChainedInterceptor; import org.jboss.cache.loader.CacheLoader; import org.jboss.cache.loader.CacheLoaderManager; import org.jboss.cache.transaction.GlobalTransaction; import org.jboss.cache.transaction.TransactionEntry; import javax.transaction.SystemException; import javax.transaction.Transaction; import javax.transaction.TransactionManager; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; /** * Writes modifications back to the store on the way out: stores modifications back * through the CacheLoader, either after each method call (no TXs), or at TX commit. * * @author Bela Ban * @version $Id: CacheStoreInterceptor.java 6772 2008-09-22 15:53:19Z manik.surtani@jboss.com $ */ public class CacheStoreInterceptor extends SkipCheckChainedInterceptor implements CacheStoreInterceptorMBean { private CacheLoaderConfig loaderConfig = null; private TransactionManager txMgr = null; private HashMap txStores = new HashMap(); private Map> preparingTxs = new ConcurrentHashMap>(); private long cacheStores = 0; private CacheLoader loader; private DataContainer dataContainer; private CacheLoaderManager loaderManager; public CacheStoreInterceptor() { log = LogFactory.getLog(getClass()); trace = log.isTraceEnabled(); } @Inject protected void init(DataContainer dataContainer, CacheLoaderManager loaderManager, TransactionManager txManager, CacheLoaderConfig clConfig) { // never inject a CacheLoader at this stage - only a CacheLoaderManager, since the CacheLoaderManager only creates a CacheLoader instance when it @Starts. this.loaderManager = loaderManager; this.loaderConfig = clConfig; txMgr = txManager; this.dataContainer = dataContainer; } @Start protected void start() { // this should only happen after the CacheLoaderManager has started, since the CacheLoaderManager only creates the CacheLoader instance in it's @Start method. loader = loaderManager.getCacheLoader(); } /** * if this is a shared cache loader and the call is of remote origin, pass up the chain */ @Override public boolean skipInterception(InvocationContext ctx, VisitableCommand command) { if (!ctx.isOriginLocal() && loaderConfig.isShared()) { if (trace) { log.trace("Passing up method call and bypassing this interceptor since the cache loader is shared and this call originated remotely."); } return true; } return false; } @Override protected Object handleCommitCommand(InvocationContext ctx, CommitCommand command) throws Throwable { if (inTransaction()) { if (ctx.isTxHasMods()) { // this is a commit call. GlobalTransaction gtx = command.getGlobalTransaction(); if (trace) log.trace("Calling loader.commit() for gtx " + gtx); // sync call (a write) on the loader // ignore modified FQNs // List fqnsModified = getFqnsFromModificationList(txTable.get(globalTransaction).getCacheLoaderModifications()); try { loader.commit(gtx); } catch (Throwable t) { preparingTxs.remove(gtx); throw t; } if (getStatisticsEnabled()) { Integer puts = (Integer) txStores.get(gtx); if (puts != null) { cacheStores = cacheStores + puts; } txStores.remove(gtx); } Object returnValue = invokeNextInterceptor(ctx, command); // persist additional internal state, if any, and then clean up internal resources Set affectedFqns = preparingTxs.remove(gtx); if (affectedFqns != null) { storeInternalState(affectedFqns); } return returnValue; } else { if (trace) log.trace("Commit called with no modifications; ignoring."); } } return invokeNextInterceptor(ctx, command); } @Override protected Object handleRollbackCommand(InvocationContext ctx, RollbackCommand command) throws Throwable { if (inTransaction()) { if (trace) log.trace("transactional so don't put stuff in the cloader yet."); if (ctx.isTxHasMods()) { GlobalTransaction gtx = command.getGlobalTransaction(); // this is a rollback method if (preparingTxs.containsKey(gtx)) { preparingTxs.remove(gtx); loader.rollback(gtx); } if (getStatisticsEnabled()) txStores.remove(gtx); } else { if (trace) log.trace("Rollback called with no modifications; ignoring."); } } return invokeNextInterceptor(ctx, command); } @Override protected Object handleOptimisticPrepareCommand(InvocationContext ctx, OptimisticPrepareCommand command) throws Throwable { return handlePrepareCommand(ctx, command); } @Override protected Object handlePrepareCommand(InvocationContext ctx, PrepareCommand command) throws Throwable { if (inTransaction()) { if (trace) log.trace("transactional so don't put stuff in the cloader yet."); prepareCacheLoader(command.getGlobalTransaction(), ctx.getTransactionEntry(), command.isOnePhaseCommit()); } return invokeNextInterceptor(ctx, command); } /** * remove() methods need to be applied to the CacheLoader before passing up the call: a listener might * access an element just removed, causing the CacheLoader to *load* the element before *removing* it. */ @Override protected Object handleRemoveNodeCommand(InvocationContext ctx, RemoveNodeCommand command) throws Throwable { if (!inTransaction()) { loader.remove(command.getFqn()); } return invokeNextInterceptor(ctx, command); } @Override protected Object handleRemoveKeyCommand(InvocationContext ctx, RemoveKeyCommand command) throws Throwable { if (!inTransaction()) { Object returnValue = loader.remove(command.getFqn(), command.getKey()); invokeNextInterceptor(ctx, command); return returnValue; } return invokeNextInterceptor(ctx, command); } @Override protected Object handleRemoveDataCommand(InvocationContext ctx, ClearDataCommand command) throws Throwable { if (!inTransaction()) { loader.removeData(command.getFqn()); // we need to mark this node as data loaded NodeSPI n = dataContainer.peek(command.getFqn(), false, false); if (n != null) { n.setDataLoaded(true); } } return invokeNextInterceptor(ctx, command); } @Override protected Object handleMoveCommand(InvocationContext ctx, MoveCommand command) throws Throwable { Object returnValue = invokeNextInterceptor(ctx, command); if (inTransaction()) { return returnValue; } Fqn newNodeFqn = Fqn.fromRelativeElements(command.getTo(), command.getFqn().getLastElement()); recursiveMove(command.getFqn(), newNodeFqn); loader.remove(command.getFqn()); return returnValue; } @Override protected Object handlePutDataMapCommand(InvocationContext ctx, PutDataMapCommand command) throws Throwable { Object returnValue = invokeNextInterceptor(ctx, command); if (inTransaction()) { return returnValue; } loader.put(command.getFqn(), command.getData()); if (getStatisticsEnabled()) cacheStores++; return returnValue; } @Override protected Object handlePutKeyValueCommand(InvocationContext ctx, PutKeyValueCommand command) throws Throwable { Object returnValue = invokeNextInterceptor(ctx, command); if (inTransaction()) { return returnValue; } returnValue = loader.put(command.getFqn(), command.getKey(), command.getValue()); if (getStatisticsEnabled()) cacheStores++; return returnValue; } @Override protected Object handlePutForExternalReadCommand(InvocationContext ctx, PutForExternalReadCommand command) throws Throwable { return handlePutKeyValueCommand(ctx, command); } private boolean inTransaction() throws SystemException { return txMgr != null && txMgr.getTransaction() != null; } private void storeInternalState(Set affectedFqns) throws Exception { if (configuration.isNodeLockingOptimistic()) { // we need to suspend any txs here since they would be in the tx-committed state and if the loader attempts to // use JTA (E.g., a JDBC CL using connections from a tx aware datasource) it will fail since the tx is in an // illegal state to perform writes. See JBCACHE-1408. Transaction tx = txMgr.suspend(); try { for (Fqn f : affectedFqns) { // NOT going to store tombstones!! NodeSPI n = dataContainer.peek(f, false, false); if (n != null) { Map internalState = n.getInternalState(true); loader.put(f, internalState); } } } finally { txMgr.resume(tx); } } } private void recursiveMove(Fqn fqn, Fqn newFqn) throws Exception { loader.put(newFqn, loader.get(fqn)); //recurse Set childrenNames = loader.getChildrenNames(fqn); if (childrenNames != null) { for (Object child : childrenNames) { recursiveMove(Fqn.fromRelativeElements(fqn, child), Fqn.fromRelativeElements(newFqn, child)); } } } public long getCacheLoaderStores() { return cacheStores; } @Override public void resetStatistics() { cacheStores = 0; } @Override public Map dumpStatistics() { Map retval = new HashMap(); retval.put("CacheLoaderStores", cacheStores); return retval; } private void prepareCacheLoader(GlobalTransaction gtx, TransactionEntry entry, boolean onePhase) throws Throwable { if (entry == null) { throw new Exception("entry for transaction " + gtx + " not found in transaction table"); } List modifications = entry.getModifications(); if (modifications.size() == 0) { if (trace) log.trace("Transaction has not logged any modifications!"); return; } if (trace) log.trace("Cache loader modification list: " + modifications); StoreModificationsBuilder modsBuilder = new StoreModificationsBuilder(getStatisticsEnabled()); for (ReversibleCommand cacheCommand : modifications) { cacheCommand.acceptVisitor(null, modsBuilder); } if (trace) { log.trace("Converted method calls to cache loader modifications. List size: " + modsBuilder.modifications.size()); } if (modsBuilder.modifications.size() > 0) { loader.prepare(gtx, modsBuilder.modifications, onePhase); preparingTxs.put(gtx, modsBuilder.affectedFqns); if (getStatisticsEnabled() && modsBuilder.putCount > 0) { txStores.put(gtx, modsBuilder.putCount); } } } public static class StoreModificationsBuilder extends AbstractVisitor { boolean generateStatistics; int putCount; Set affectedFqns = new HashSet(); List modifications = new ArrayList(); public StoreModificationsBuilder(boolean generateStatistics) { this.generateStatistics = generateStatistics; } @Override public Object visitPutDataMapCommand(InvocationContext ctx, PutDataMapCommand command) throws Throwable { if (generateStatistics) putCount++; modifications.add(new Modification(Modification.ModificationType.PUT_DATA, command.getFqn(), command.getData())); affectedFqns.add(command.getFqn()); return null; } @Override public Object visitPutKeyValueCommand(InvocationContext ctx, PutKeyValueCommand command) throws Throwable { if (generateStatistics) putCount++; modifications.add(new Modification(Modification.ModificationType.PUT_KEY_VALUE, command.getFqn(), command.getKey(), command.getValue())); affectedFqns.add(command.getFqn()); return null; } @Override public Object visitRemoveKeyCommand(InvocationContext ctx, RemoveKeyCommand command) throws Throwable { modifications.add(new Modification(Modification.ModificationType.REMOVE_KEY_VALUE, command.getFqn(), command.getKey())); affectedFqns.add(command.getFqn()); return null; } @Override public Object visitClearDataCommand(InvocationContext ctx, ClearDataCommand command) throws Throwable { modifications.add(new Modification(Modification.ModificationType.REMOVE_DATA, command.getFqn())); affectedFqns.add(command.getFqn()); return null; } @Override public Object visitRemoveNodeCommand(InvocationContext ctx, RemoveNodeCommand command) throws Throwable { modifications.add(new Modification(Modification.ModificationType.REMOVE_NODE, command.getFqn())); affectedFqns.add(command.getFqn()); return null; } @Override public Object visitMoveCommand(InvocationContext ctx, MoveCommand command) throws Throwable { Fqn moveFrom = command.getFqn(); affectedFqns.add(command.getFqn()); affectedFqns.add(moveFrom); Modification mod = new Modification(Modification.ModificationType.MOVE, moveFrom, command.getTo()); modifications.add(mod); return null; } } } ././@LongLink0000000000000000000000000000015500000000000011566 Lustar rootrootjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/interceptors/BaseTransactionalContextInterceptor.javajbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/interceptors/BaseTransactionalContextIntercep0000644000175000017500000000555611017062036033510 0ustar twernertwernerpackage org.jboss.cache.interceptors; import org.jboss.cache.InvocationContext; import org.jboss.cache.config.Option; import org.jboss.cache.factories.annotations.Inject; import org.jboss.cache.interceptors.base.CommandInterceptor; import org.jboss.cache.transaction.GlobalTransaction; import org.jboss.cache.transaction.TransactionEntry; import org.jboss.cache.transaction.TransactionTable; import javax.transaction.Status; import javax.transaction.SystemException; import javax.transaction.Transaction; import javax.transaction.TransactionManager; /** * Class providing some base functionality around manipulating transactions and global transactions withing invocation contexts. * * @author Manik Surtani */ public abstract class BaseTransactionalContextInterceptor extends CommandInterceptor { protected TransactionTable txTable; protected TransactionManager txManager; @Inject private void injectDependencies(TransactionTable txTable, TransactionManager txManager) { this.txManager = txManager; this.txTable = txTable; } protected void copyInvocationScopeOptionsToTxScope(InvocationContext ctx) { // notify the transaction entry that this override is in place. TransactionEntry entry = txTable.get(ctx.getGlobalTransaction()); if (entry != null) { Option txScopeOption = new Option(); txScopeOption.setCacheModeLocal(ctx.getOptionOverrides() != null && ctx.getOptionOverrides().isCacheModeLocal()); txScopeOption.setSkipCacheStatusCheck(ctx.getOptionOverrides() != null && ctx.getOptionOverrides().isSkipCacheStatusCheck()); entry.setOption(txScopeOption); } } protected void setTransactionalContext(Transaction tx, GlobalTransaction gtx, TransactionEntry entry, InvocationContext ctx) { if (trace) { log.trace("Setting up transactional context."); log.trace("Setting tx as " + tx + " and gtx as " + gtx); } ctx.setTransaction(tx); ctx.setGlobalTransaction(gtx); if (entry == null) { if (gtx != null) { ctx.setTransactionEntry(txTable.get(gtx)); } else if (tx == null) { // then nullify the transaction entry as well ctx.setTransactionEntry(null); } } else { ctx.setTransactionEntry(entry); } } /** * Returns true if transaction is rolling back, false otherwise */ protected boolean isRollingBack(Transaction tx) { if (tx == null) return false; int status; try { status = tx.getStatus(); return status == Status.STATUS_ROLLING_BACK || status == Status.STATUS_ROLLEDBACK; } catch (SystemException e) { log.error("failed getting transaction status", e); return false; } } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/interceptors/ActivationInterceptorMBean.java0000644000175000017500000000265610370473147033227 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * Copyright 2005, JBoss Inc., and individual contributors as indicated * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.cache.interceptors; /** * Interface capturing activation statistics * @author Jerry Gauthier * @version $Id: ActivationInterceptorMBean.java 1111 2006-02-02 21:15:19Z jerrygauth $ */ public interface ActivationInterceptorMBean extends CacheLoaderInterceptorMBean { /** * Returns the number of cache node activations * * @return the number of cache node activations */ long getActivations(); } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/interceptors/EvictionInterceptor.java0000644000175000017500000002045711032145203031763 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. * Created on March 25 2003 */ package org.jboss.cache.interceptors; import org.jboss.cache.DataContainer; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.NodeSPI; import org.jboss.cache.Region; import org.jboss.cache.RegionManager; import org.jboss.cache.commands.read.GetDataMapCommand; import org.jboss.cache.commands.read.GetKeyValueCommand; import org.jboss.cache.commands.read.GetNodeCommand; import org.jboss.cache.commands.write.ClearDataCommand; import org.jboss.cache.commands.write.EvictCommand; import org.jboss.cache.commands.write.PutDataMapCommand; import org.jboss.cache.commands.write.PutForExternalReadCommand; import org.jboss.cache.commands.write.PutKeyValueCommand; import org.jboss.cache.commands.write.RemoveKeyCommand; import org.jboss.cache.commands.write.RemoveNodeCommand; import org.jboss.cache.eviction.EvictedEventNode; import org.jboss.cache.eviction.NodeEventType; import org.jboss.cache.factories.annotations.Inject; import org.jboss.cache.interceptors.base.CommandInterceptor; /** * Eviction Interceptor. *

* This interceptor is used to handle eviction events. * * @author Daniel Huang * @author Mircea.Markus@jboss.com * @version $Revision: 6120 $ */ public class EvictionInterceptor extends CommandInterceptor { protected RegionManager regionManager; private DataContainer dataContainer; @Inject public void initialize(DataContainer dataContainer) { this.dataContainer = dataContainer; } /** * this method is for ease of unit testing. thus package access. *

* Not to be attempted to be used anywhere else. */ @Inject void setRegionManager(RegionManager regionManager) { this.regionManager = regionManager; } @Override public Object visitEvictFqnCommand(InvocationContext ctx, EvictCommand command) throws Throwable { Fqn fqn = command.getFqn(); Object retVal = invokeNextInterceptor(ctx, command); // See if the node still exists; i.e. was only data removed // because it still has children. // If yes, put an ADD event in the queue so the node gets revisited boolean complete = (retVal != null && (Boolean) retVal); if (!complete) { Region r; if (fqn != null && (r = getRegion(fqn, NodeEventType.ADD_NODE_EVENT)) != null) { registerEvictionEventToRegionManager(new EvictedEventNode(fqn, NodeEventType.ADD_NODE_EVENT, 0), r); } } return retVal; } @Override public Object visitPutForExternalReadCommand(InvocationContext ctx, PutForExternalReadCommand command) throws Throwable { return visitPutKeyValueCommand(ctx, command); } @Override public Object visitPutKeyValueCommand(InvocationContext ctx, PutKeyValueCommand command) throws Throwable { Object retVal = invokeNextInterceptor(ctx, command); Region r; if (command.getFqn() != null && command.getKey() != null && (r = getRegion(command.getFqn(), NodeEventType.ADD_ELEMENT_EVENT)) != null) { registerEvictionEventToRegionManager(new EvictedEventNode(command.getFqn(), NodeEventType.ADD_ELEMENT_EVENT, 1), r); } return retVal; } @Override public Object visitPutDataMapCommand(InvocationContext ctx, PutDataMapCommand command) throws Throwable { Object retVal = invokeNextInterceptor(ctx, command); Fqn fqn = command.getFqn(); Region r; if (fqn != null && (r = getRegion(fqn, NodeEventType.ADD_NODE_EVENT)) != null) { if (command.getData() == null) { if (trace) { log.trace("Putting null data under fqn " + fqn + "."); } } else { int size; synchronized (command.getData()) { size = command.getData().size(); } EvictedEventNode event = new EvictedEventNode(fqn, NodeEventType.ADD_NODE_EVENT, size); registerEvictionEventToRegionManager(event, r); } } return retVal; } @Override public Object visitRemoveKeyCommand(InvocationContext ctx, RemoveKeyCommand command) throws Throwable { Object retVal = invokeNextInterceptor(ctx, command); if (retVal == null) { if (trace) { log.trace("No event added. Element does not exist"); } } else { Fqn fqn = command.getFqn(); Region r; if (fqn != null && command.getKey() != null && (r = getRegion(fqn, NodeEventType.REMOVE_ELEMENT_EVENT)) != null) { registerEvictionEventToRegionManager(new EvictedEventNode(fqn, NodeEventType.REMOVE_ELEMENT_EVENT, 1), r); } } return retVal; } @Override public Object visitGetNodeCommand(InvocationContext ctx, GetNodeCommand command) throws Throwable { Object retVal = invokeNextInterceptor(ctx, command); return handleGetNodeOrDataCommands(retVal, command.getFqn()); } private Object handleGetNodeOrDataCommands(Object retVal, Fqn fqn) { if (retVal == null) { if (trace) { log.trace("No event added. Node does not exist"); } } else { Region r; if (fqn != null && (r = getRegion(fqn, NodeEventType.VISIT_NODE_EVENT)) != null) { registerEvictionEventToRegionManager(new EvictedEventNode(fqn, NodeEventType.VISIT_NODE_EVENT), r); } } return retVal; } @Override public Object visitGetDataMapCommand(InvocationContext ctx, GetDataMapCommand command) throws Throwable { Object retVal = invokeNextInterceptor(ctx, command); return handleGetNodeOrDataCommands(retVal, command.getFqn()); } @Override public Object visitGetKeyValueCommand(InvocationContext ctx, GetKeyValueCommand command) throws Throwable { Object retVal = invokeNextInterceptor(ctx, command); Fqn fqn = command.getFqn(); Region r; if (retVal == null) { if (trace) { log.trace("No event added. Element does not exist"); } } else if (fqn != null && command.getKey() != null && (r = getRegion(fqn, NodeEventType.VISIT_NODE_EVENT)) != null) { registerEvictionEventToRegionManager(new EvictedEventNode(fqn, NodeEventType.VISIT_NODE_EVENT), r); } return retVal; } @Override public Object visitRemoveNodeCommand(InvocationContext ctx, RemoveNodeCommand command) throws Throwable { Object retVal = invokeNextInterceptor(ctx, command); Region r; if (command.getFqn() != null && (r = getRegion(command.getFqn(), NodeEventType.REMOVE_NODE_EVENT)) != null) { registerEvictionEventToRegionManager(new EvictedEventNode(command.getFqn(), NodeEventType.REMOVE_NODE_EVENT), r); } return retVal; } @Override public Object visitClearDataCommand(InvocationContext ctx, ClearDataCommand command) throws Throwable { Object retVal = invokeNextInterceptor(ctx, command); Region r; if (command.getFqn() != null && (r = getRegion(command.getFqn(), NodeEventType.REMOVE_NODE_EVENT)) != null) { registerEvictionEventToRegionManager(new EvictedEventNode(command.getFqn(), NodeEventType.REMOVE_NODE_EVENT), r); } return retVal; } private void registerEvictionEventToRegionManager(EvictedEventNode event, Region region) { if (event == null) { // no node modifications. return; } NodeSPI nodeSPI = dataContainer.peek(event.getFqn(), false, false); //we do not trigger eviction events for resident nodes if (nodeSPI != null && nodeSPI.isResident()) { return; } region.putNodeEvent(event); if (trace) { log.trace("Adding event " + event + " to region at " + region.getFqn()); } if (trace) { log.trace("Finished updating node"); } } protected Region getRegion(Fqn fqn, NodeEventType type) { Region r = regionManager.getRegion(fqn, Region.Type.EVICTION, false); if (r != null && r.getEvictionPolicy().canIgnoreEvent(fqn, type)) return null; return r; } } ././@LongLink0000000000000000000000000000014600000000000011566 Lustar rootrootjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/interceptors/InvalidationInterceptorMBean.javajbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/interceptors/InvalidationInterceptorMBean.jav0000644000175000017500000000263710365672334033410 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * Copyright 2005, JBoss Inc., and individual contributors as indicated * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.cache.interceptors; /** * Interface capturing invalidation statistics * @author Jerry Gauthier * @version $Id: InvalidationInterceptorMBean.java 1070 2006-01-25 12:53:48Z jerrygauth $ */ public interface InvalidationInterceptorMBean extends InterceptorMBean { /** * Returns the number of cache invalidations * * @return the number of invalidations */ long getInvalidations(); } ././@LongLink0000000000000000000000000000015500000000000011566 Lustar rootrootjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/interceptors/BuddyRegionAwareEvictionInterceptor.javajbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/interceptors/BuddyRegionAwareEvictionIntercep0000644000175000017500000000323611024205072033430 0ustar twernertwernerpackage org.jboss.cache.interceptors; import org.jboss.cache.Fqn; import org.jboss.cache.Region; import org.jboss.cache.factories.annotations.Inject; import org.jboss.cache.buddyreplication.BuddyFqnTransformer; import org.jboss.cache.eviction.NodeEventType; /** * A subclass of EvictionInterceptor that is aware of and able to deal with buddy regions. * * @author Manik Surtani (manik@jboss.org) * @since 2.2.0 */ public class BuddyRegionAwareEvictionInterceptor extends EvictionInterceptor { private BuddyFqnTransformer buddyFqnTransformer; @Inject public void initialize(BuddyFqnTransformer transformer) { this.buddyFqnTransformer = transformer; } @Override protected Region getRegion(Fqn fqn, NodeEventType type) { Region r = super.getRegion(fqn, type); if (r != null) return r; else if (buddyFqnTransformer.isBackupFqn(fqn)) { // try and grab a backup region, creating one if need be. Fqn actualFqn = buddyFqnTransformer.getActualFqn(fqn); Fqn backupRoot = buddyFqnTransformer.getBackupRootFromFqn(fqn); // the actual region could be a few levels higher than actualFqn Region actualRegion = regionManager.getRegion(actualFqn, Region.Type.EVICTION, false); if (actualRegion == null) return null; //create a new region for this backup Region newRegion = regionManager.getRegion(Fqn.fromRelativeFqn(backupRoot, actualRegion.getFqn()), Region.Type.EVICTION, true); newRegion.setEvictionPolicy(actualRegion.getEvictionPolicyConfig()); return newRegion; } else return null; } } ././@LongLink0000000000000000000000000000014700000000000011567 Lustar rootrootjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/interceptors/OrderedSynchronizationHandler.javajbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/interceptors/OrderedSynchronizationHandler.ja0000644000175000017500000000534411010550166033443 0ustar twernertwernerpackage org.jboss.cache.interceptors; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import javax.transaction.RollbackException; import javax.transaction.Synchronization; import javax.transaction.SystemException; import javax.transaction.Transaction; import java.util.LinkedList; /** * Maintains a list of Synchronization handlers. Reason is that we have to * invoke certain handlers before others. See the description in * SyncTxUnitTestCase.testConcurrentPuts(). For example, for synchronous * replication, we have to execute the ReplicationInterceptor's * afterCompletion() before the TransactionInterceptor's. * * @author Bela Ban * @version $Id: OrderedSynchronizationHandler.java 5808 2008-05-08 10:05:42Z manik.surtani@jboss.com $ */ public class OrderedSynchronizationHandler implements Synchronization { static final Log log = LogFactory.getLog(OrderedSynchronizationHandler.class); private Transaction tx = null; private final LinkedList handlers = new LinkedList(); public OrderedSynchronizationHandler(Transaction tx) throws SystemException, RollbackException { this.tx = tx; tx.registerSynchronization(this); } public void registerAtHead(Synchronization handler) { register(handler, true); } public void registerAtTail(Synchronization handler) { register(handler, false); } void register(Synchronization handler, boolean head) { if (handler != null && !handlers.contains(handler)) { if (head) handlers.addFirst(handler); else handlers.addLast(handler); } } public void beforeCompletion() { for (Synchronization sync : handlers) { sync.beforeCompletion(); } } public void afterCompletion(int status) { RuntimeException exceptionInAfterCompletion = null; for (Synchronization sync : handlers) { try { sync.afterCompletion(status); } catch (Throwable t) { log.error("failed calling afterCompletion() on " + sync, t); exceptionInAfterCompletion = (RuntimeException) t; } } // throw the exception so the TM can deal with it. if (exceptionInAfterCompletion != null) throw exceptionInAfterCompletion; } @Override public String toString() { return "tx=" + getTxAsString() + ", handlers=" + handlers; } private String getTxAsString() { // JBCACHE-1114 -- don't call toString() on tx or it can lead to stack overflow if (tx == null) return null; return tx.getClass().getName() + "@" + System.identityHashCode(tx); } } ././@LongLink0000000000000000000000000000014600000000000011566 Lustar rootrootjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/interceptors/OptimisticLockingInterceptor.javajbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/interceptors/OptimisticLockingInterceptor.jav0000755000175000017500000001070011032145203033466 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.interceptors; import org.jboss.cache.CacheException; import org.jboss.cache.InvocationContext; import org.jboss.cache.NodeSPI; import org.jboss.cache.commands.VisitableCommand; import org.jboss.cache.commands.tx.CommitCommand; import org.jboss.cache.commands.tx.OptimisticPrepareCommand; import org.jboss.cache.commands.tx.RollbackCommand; import org.jboss.cache.factories.annotations.Start; import static org.jboss.cache.lock.LockType.READ; import static org.jboss.cache.lock.LockType.WRITE; import org.jboss.cache.optimistic.TransactionWorkspace; import org.jboss.cache.optimistic.WorkspaceNode; import org.jboss.cache.transaction.GlobalTransaction; import org.jboss.cache.transaction.TransactionEntry; /** * Locks nodes during transaction boundaries. Only affects prepare/commit/rollback method calls; other method calls * are simply passed up the interceptor stack. * * @author Manik Surtani (manik@jboss.org) * @author Steve Woodcock (stevew@jofti.com) */ public class OptimisticLockingInterceptor extends OptimisticInterceptor { @Start private void init() { if (txManager == null) log.fatal("No transaction manager lookup class has been defined. Transactions cannot be used and thus OPTIMISTIC locking cannot be used! Expect errors!!"); } @Override public Object visitOptimisticPrepareCommand(InvocationContext ctx, OptimisticPrepareCommand command) throws Throwable { //try and acquire the locks - before passing on GlobalTransaction gtx = getGlobalTransaction(ctx); boolean succeeded = false; try { TransactionWorkspace workspace = getTransactionWorkspace(ctx); if (log.isDebugEnabled()) log.debug("Locking nodes in transaction workspace for GlobalTransaction " + gtx); for (WorkspaceNode workspaceNode : workspace.getNodes().values()) { NodeSPI node = workspaceNode.getNode(); boolean isWriteLockNeeded = workspaceNode.isDirty() || (workspaceNode.isChildrenModified() && (configuration.isLockParentForChildInsertRemove() || node.isLockForChildInsertRemove())); boolean acquired = lockManager.lockAndRecord(node, isWriteLockNeeded ? WRITE : READ, ctx); if (acquired) { if (trace) log.trace("Acquired lock on node " + node.getFqn()); } else { throw new CacheException("Unable to acquire lock on node " + node.getFqn()); } } // locks have acquired so lets pass on up Object retval = invokeNextInterceptor(ctx, command); succeeded = true; return retval; } catch (Throwable e) { succeeded = false; log.debug("Caught exception attempting to lock nodes ", e); //we have failed - set to rollback and throw exception throw e; } finally { if (!succeeded) unlock(ctx); } } @Override public Object visitCommitCommand(InvocationContext ctx, CommitCommand command) throws Throwable { return transactionFinalized(ctx, command); } @Override public Object visitRollbackCommand(InvocationContext ctx, RollbackCommand command) throws Throwable { return transactionFinalized(ctx, command); } private Object transactionFinalized(InvocationContext ctx, VisitableCommand command) throws Throwable { Object retval = null; // we need to let the stack run its commits or rollbacks first - // we unlock last - even if an exception occurs try { retval = invokeNextInterceptor(ctx, command); } finally { unlock(ctx); } return retval; } /** * Releases all locks held by the specified global transaction. * * @param ctx Invocation Context */ private void unlock(InvocationContext ctx) { try { TransactionEntry entry = ctx.getTransactionEntry(); if (entry != null) { lockManager.unlock(ctx); } } catch (Exception e) { // we have failed to unlock - now what? log.error("Failed to unlock nodes after a commit or rollback! Locks are possibly in a very inconsistent state now!", e); } } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/interceptors/InvalidationInterceptor.java0000644000175000017500000003356411014645153032640 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.interceptors; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.commands.AbstractVisitor; import org.jboss.cache.commands.ReversibleCommand; import org.jboss.cache.commands.VisitableCommand; import org.jboss.cache.commands.tx.CommitCommand; import org.jboss.cache.commands.tx.OptimisticPrepareCommand; import org.jboss.cache.commands.tx.PrepareCommand; import org.jboss.cache.commands.tx.RollbackCommand; import org.jboss.cache.commands.write.*; import org.jboss.cache.config.Option; import org.jboss.cache.factories.CommandsFactory; import org.jboss.cache.factories.annotations.Inject; import org.jboss.cache.factories.annotations.Start; import org.jboss.cache.optimistic.DataVersion; import org.jboss.cache.optimistic.DefaultDataVersion; import org.jboss.cache.optimistic.TransactionWorkspace; import org.jboss.cache.optimistic.WorkspaceNode; import org.jboss.cache.transaction.GlobalTransaction; import org.jboss.cache.transaction.OptimisticTransactionEntry; import org.jboss.cache.transaction.TransactionEntry; import org.jboss.cache.transaction.TransactionTable; import javax.transaction.SystemException; import javax.transaction.Transaction; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; /** * This interceptor acts as a replacement to the replication interceptor when * the CacheImpl is configured with ClusteredSyncMode as INVALIDATE. *

* The idea is that rather than replicating changes to all caches in a cluster * when CRUD (Create, Remove, Update, Delete) methods are called, simply call * evict(Fqn) on the remote caches for each changed node. This allows the * remote node to look up the value in a shared cache loader which would have * been updated with the changes. * * @author Manik Surtani (manik@jboss.org) */ public class InvalidationInterceptor extends BaseRpcInterceptor implements InvalidationInterceptorMBean { private long invalidations = 0; protected Map> txMods; protected boolean optimistic; private CommandsFactory commandsFactory; @Inject public void injectDependencies(CommandsFactory commandsFactory) { this.commandsFactory = commandsFactory; } @Start private void initTxMap() { optimistic = configuration.isNodeLockingOptimistic(); if (optimistic) txMods = new ConcurrentHashMap>(); } @Override public Object visitPutDataMapCommand(InvocationContext ctx, PutDataMapCommand command) throws Throwable { return handleWriteMethod(ctx, command.getFqn(), null, command); } @Override public Object visitPutForExternalReadCommand(InvocationContext ctx, PutForExternalReadCommand command) throws Throwable { // these are always local more, as far as invalidation is concerned if (ctx.getTransaction() != null) ctx.getTransactionEntry().addLocalModification(command); return invokeNextInterceptor(ctx, command); } @Override public Object visitPutKeyValueCommand(InvocationContext ctx, PutKeyValueCommand command) throws Throwable { return handleWriteMethod(ctx, command.getFqn(), null, command); } @Override public Object visitRemoveNodeCommand(InvocationContext ctx, RemoveNodeCommand command) throws Throwable { return handleWriteMethod(ctx, command.getFqn(), null, command); } @Override public Object visitRemoveKeyCommand(InvocationContext ctx, RemoveKeyCommand command) throws Throwable { return handleWriteMethod(ctx, command.getFqn(), null, command); } @Override public Object visitMoveCommand(InvocationContext ctx, MoveCommand command) throws Throwable { return handleWriteMethod(ctx, command.getTo(), command.getFqn(), command); } @Override public Object visitClearDataCommand(InvocationContext ctx, ClearDataCommand command) throws Throwable { return handleWriteMethod(ctx, command.getFqn(), null, command); } @Override public Object visitPrepareCommand(InvocationContext ctx, PrepareCommand command) throws Throwable { Object retval = invokeNextInterceptor(ctx, command); Transaction tx = ctx.getTransaction(); if (tx != null) { if (trace) log.trace("Entering InvalidationInterceptor's prepare phase"); // fetch the modifications before the transaction is committed (and thus removed from the txTable) GlobalTransaction gtx = ctx.getGlobalTransaction(); TransactionEntry entry = ctx.getTransactionEntry(); if (entry == null) throw new IllegalStateException("cannot find transaction entry for " + gtx); if (entry.hasModifications()) { List mods; if (entry.hasLocalModifications()) { mods = new ArrayList(command.getModifications()); mods.removeAll(entry.getLocalModifications()); } else { mods = command.getModifications(); } broadcastInvalidate(mods, tx, ctx); } else { if (trace) log.trace("Nothing to invalidate - no modifications in the transaction."); } } return retval; } @Override public Object visitOptimisticPrepareCommand(InvocationContext ctx, OptimisticPrepareCommand command) throws Throwable { Object retval = invokeNextInterceptor(ctx, command); Transaction tx = ctx.getTransaction(); if (tx != null) { // here we just record the modifications but actually do the invalidate in commit. GlobalTransaction gtx = ctx.getGlobalTransaction(); TransactionEntry entry = ctx.getTransactionEntry(); if (entry == null) throw new IllegalStateException("cannot find transaction entry for " + gtx); if (entry.hasModifications()) { List mods = new ArrayList(entry.getModifications()); if (entry.hasLocalModifications()) mods.removeAll(entry.getLocalModifications()); txMods.put(gtx, mods); } } return retval; } @Override public Object visitCommitCommand(InvocationContext ctx, CommitCommand command) throws Throwable { Object retval = invokeNextInterceptor(ctx, command); Transaction tx = ctx.getTransaction(); if (tx != null && optimistic) { GlobalTransaction gtx = ctx.getGlobalTransaction(); List modifications = txMods.remove(gtx); broadcastInvalidate(modifications, tx, ctx); if (trace) log.trace("Committing. Broadcasting invalidations."); } return retval; } @Override public Object visitRollbackCommand(InvocationContext ctx, RollbackCommand command) throws Throwable { Object retval = invokeNextInterceptor(ctx, command); Transaction tx = ctx.getTransaction(); if (tx != null && optimistic) { GlobalTransaction gtx = ctx.getGlobalTransaction(); txMods.remove(gtx); log.debug("Caught a rollback. Clearing modification in txMods"); } return retval; } /** * @param from is only present for move operations, else pass it in as null * @param command */ private Object handleWriteMethod(InvocationContext ctx, Fqn targetFqn, Fqn from, VisitableCommand command) throws Throwable { Object retval = invokeNextInterceptor(ctx, command); Transaction tx = ctx.getTransaction(); Option optionOverride = ctx.getOptionOverrides(); if (log.isDebugEnabled()) log.debug("Is a CRUD method"); Set fqns = new HashSet(); if (from != null) { fqns.add(from); } fqns.add(targetFqn); if (!fqns.isEmpty()) { // could be potentially TRANSACTIONAL. Ignore if it is, until we see a prepare(). if (tx == null || !TransactionTable.isValid(tx)) { // the no-tx case: //replicate an evict call. for (Fqn fqn : fqns) invalidateAcrossCluster(fqn, null, isSynchronous(optionOverride), ctx); } else { if (isLocalModeForced(ctx)) ctx.getTransactionEntry().addLocalModification((ReversibleCommand) command); } } return retval; } private void broadcastInvalidate(List modifications, Transaction tx, InvocationContext ctx) throws Throwable { if (ctx.getTransaction() != null && !isLocalModeForced(ctx)) { if (modifications == null || modifications.isEmpty()) return; InvalidationFilterVisitor filterVisitor = new InvalidationFilterVisitor(modifications.size()); filterVisitor.visitCollection(null, modifications); if (filterVisitor.containsPutForExternalRead) { log.debug("Modification list contains a putForExternalRead operation. Not invalidating."); } else { try { TransactionWorkspace workspace = configuration.isNodeLockingOptimistic() ? getWorkspace(ctx) : null; for (Fqn fqn : filterVisitor.result) invalidateAcrossCluster(fqn, workspace, defaultSynchronous, ctx); } catch (Throwable t) { log.warn("Unable to broadcast evicts as a part of the prepare phase. Rolling back.", t); try { tx.setRollbackOnly(); } catch (SystemException se) { throw new RuntimeException("setting tx rollback failed ", se); } if (t instanceof RuntimeException) throw (RuntimeException) t; else throw new RuntimeException("Unable to broadcast invalidation messages", t); } } } } public static class InvalidationFilterVisitor extends AbstractVisitor { Set result; public boolean containsPutForExternalRead; public InvalidationFilterVisitor(int maxSetSize) { result = new HashSet(maxSetSize); } @Override public Object visitPutKeyValueCommand(InvocationContext ctx, PutKeyValueCommand command) throws Throwable { result.add(command.getFqn()); return null; } @Override public Object visitPutForExternalReadCommand(InvocationContext ctx, PutForExternalReadCommand command) throws Throwable { containsPutForExternalRead = true; return null; } @Override public Object visitPutDataMapCommand(InvocationContext ctx, PutDataMapCommand command) throws Throwable { result.add(command.getFqn()); return null; } @Override public Object visitRemoveNodeCommand(InvocationContext ctx, RemoveNodeCommand command) throws Throwable { result.add(command.getFqn()); return null; } @Override public Object visitClearDataCommand(InvocationContext ctx, ClearDataCommand command) throws Throwable { result.add(command.getFqn()); return null; } @Override public Object visitRemoveKeyCommand(InvocationContext ctx, RemoveKeyCommand command) throws Throwable { result.add(command.getFqn()); return null; } @Override public Object visitMoveCommand(InvocationContext ctx, MoveCommand command) throws Throwable { result.add(command.getFqn()); // now if this is a "move" operation, then we also have another Fqn - Object le = command.getFqn().getLastElement(); Fqn parent = command.getTo(); result.add(Fqn.fromRelativeElements(parent, le)); return null; } } public long getInvalidations() { return invalidations; } @Override public void resetStatistics() { invalidations = 0; } @Override public Map dumpStatistics() { Map retval = new HashMap(); retval.put("Invalidations", invalidations); return retval; } protected void invalidateAcrossCluster(Fqn fqn, TransactionWorkspace workspace, boolean synchronous, InvocationContext ctx) throws Throwable { if (!isLocalModeForced(ctx)) { // increment invalidations counter if statistics maintained incrementInvalidations(); InvalidateCommand command = commandsFactory.buildInvalidateCommand(fqn); DataVersion dataVersion = getNodeVersion(workspace, fqn); if (dataVersion != null) ((OptimisticInvalidateCommand) command).setDataVersion(dataVersion); if (log.isDebugEnabled()) log.debug("Cache [" + rpcManager.getLocalAddress() + "] replicating " + command); // voila, invalidated! replicateCall(ctx, command, synchronous, ctx.getOptionOverrides()); } } private void incrementInvalidations() { if (getStatisticsEnabled()) invalidations++; } protected DataVersion getNodeVersion(TransactionWorkspace w, Fqn f) { if (w == null) return null; WorkspaceNode wn = w.getNode(f); if (wn == null) return null; // JBCACHE-1297 DataVersion v = wn.getVersion(); if (wn.isVersioningImplicit()) { // then send back an incremented version v = ((DefaultDataVersion) v).increment(); } return v; } protected TransactionWorkspace getWorkspace(InvocationContext ctx) { OptimisticTransactionEntry entry = (OptimisticTransactionEntry) ctx.getTransactionEntry(); return entry.getTransactionWorkSpace(); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/interceptors/PessimisticLockInterceptor.java0000755000175000017500000003413511026046072033320 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.interceptors; import org.jboss.cache.DataContainer; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.NodeSPI; import org.jboss.cache.commands.DataCommand; import org.jboss.cache.commands.VisitableCommand; import org.jboss.cache.commands.read.GetChildrenNamesCommand; import org.jboss.cache.commands.read.GetKeyValueCommand; import org.jboss.cache.commands.read.GetKeysCommand; import org.jboss.cache.commands.read.GetNodeCommand; import org.jboss.cache.commands.tx.CommitCommand; import org.jboss.cache.commands.tx.PrepareCommand; import org.jboss.cache.commands.tx.RollbackCommand; import org.jboss.cache.commands.write.ClearDataCommand; import org.jboss.cache.commands.write.EvictCommand; import org.jboss.cache.commands.write.MoveCommand; import org.jboss.cache.commands.write.PutDataMapCommand; import org.jboss.cache.commands.write.PutForExternalReadCommand; import org.jboss.cache.commands.write.PutKeyValueCommand; import org.jboss.cache.commands.write.RemoveKeyCommand; import org.jboss.cache.commands.write.RemoveNodeCommand; import org.jboss.cache.factories.CommandsFactory; import org.jboss.cache.factories.annotations.Inject; import org.jboss.cache.interceptors.base.PostProcessingCommandInterceptor; import org.jboss.cache.lock.IsolationLevel; import org.jboss.cache.lock.LockManager; import static org.jboss.cache.lock.LockType.READ; import static org.jboss.cache.lock.LockType.WRITE; import org.jboss.cache.lock.LockUtil; import org.jboss.cache.lock.PessimisticNodeBasedLockManager; import org.jboss.cache.transaction.GlobalTransaction; import org.jboss.cache.transaction.TransactionEntry; import java.util.LinkedList; import java.util.List; import java.util.Map; /* * // TODO: 2.2.0: refactorings ideas * - thre are many places in code that handles that coputes the lock owners: either GTX or Thread.local. The * lockOwner can be abstractised as a LockOwner that can be extended by CurrentThreadLock owner and GlobalTransaction owner. This would make the code nicer. */ /** * An interceptor that handles locking. When a TX is associated, we register * for TX completion and unlock the locks acquired within the scope of the TX. * When no TX is present, we keep track of the locks acquired during the * current method and unlock when the method returns. * * @author Bela Ban * @version $Id: PessimisticLockInterceptor.java 6000 2008-06-17 23:43:54Z manik.surtani@jboss.com $ */ public class PessimisticLockInterceptor extends PostProcessingCommandInterceptor { private DataContainer dataContainer; private PessimisticNodeBasedLockManager lockManager; private CommandsFactory commandsFactory; @Inject public void injectDependencies(DataContainer dataContainer, LockManager lockManager, CommandsFactory commandsFactory) { this.dataContainer = dataContainer; this.lockManager = (PessimisticNodeBasedLockManager) lockManager; this.commandsFactory = commandsFactory; } @Override protected Object handlePutDataMapCommand(InvocationContext ctx, PutDataMapCommand command) throws Throwable { return handlePutCommand(ctx, command, false); } @Override protected Object handlePutKeyValueCommand(InvocationContext ctx, PutKeyValueCommand command) throws Throwable { return handlePutCommand(ctx, command, false); } @Override protected Object handlePutForExternalReadCommand(InvocationContext ctx, PutForExternalReadCommand command) throws Throwable { return handlePutCommand(ctx, command, true); } private Object handlePutCommand(InvocationContext ctx, DataCommand command, boolean zeroAcquisitionTimeout) throws Throwable { if ((ctx.isLockingSuppressed()) || configuration.getIsolationLevel() == IsolationLevel.NONE) { if (trace) log.trace("Suppressing locking, creating nodes if necessary"); int treeNodeSize = command.getFqn().size(); NodeSPI n = dataContainer.getRoot(); for (int i = 0; i < treeNodeSize; i++) { Object childName = command.getFqn().get(i); Fqn childFqn = Fqn.fromElements(childName); NodeSPI childNode = n.getChildDirect(childFqn); if (childNode == null) childNode = n.addChildDirect(childFqn); LockUtil.manageReverseRemove(ctx, childNode, true, null, commandsFactory); n = childNode; } } else { lockManager.lockPessimistically(ctx, command.getFqn(), WRITE, true, zeroAcquisitionTimeout, false, true, null, false); } return invokeNextInterceptor(ctx, command); } @Override protected Object handlePrepareCommand(InvocationContext ctx, PrepareCommand command) throws Throwable { // 2-phase commit prepares are no-ops here. if (!command.isOnePhaseCommit()) return invokeNextInterceptor(ctx, command); // commit propagated up from the tx interceptor commit(ctx.getTransactionEntry(), ctx.getGlobalTransaction()); Object retVal = invokeNextInterceptor(ctx, command); lockManager.unlock(ctx); return retVal; } @Override protected Object handleCommitCommand(InvocationContext ctx, CommitCommand command) throws Throwable { commit(ctx.getTransactionEntry(), command.getGlobalTransaction()); if (trace) log.trace("bypassed locking as method commit() doesn't require locking"); Object retVal = invokeNextInterceptor(ctx, command); lockManager.unlock(ctx); return retVal; } @Override protected Object handleRollbackCommand(InvocationContext ctx, RollbackCommand command) throws Throwable { TransactionEntry entry = ctx.getTransactionEntry(); if (trace) { log.trace("called to rollback cache with GlobalTransaction=" + command.getGlobalTransaction()); } if (entry == null) { log.error("entry for transaction " + command.getGlobalTransaction() + " not found (transaction has possibly already been rolled back)"); } else { for (Fqn fqn : entry.getRemovedNodes()) { dataContainer.removeFromDataStructure(fqn, false); } // 1. Revert the modifications by running the undo-op list in reverse. This *cannot* throw any exceptions ! entry.undoOperations(); } if (trace) { log.trace("bypassed locking as method rollback() doesn't require locking"); } Object retVal = invokeNextInterceptor(ctx, command); lockManager.unlock(ctx); return retVal; } @Override protected Object handleMoveCommand(InvocationContext ctx, MoveCommand command) throws Throwable { if (ctx.isLockingSuppressed()) return invokeNextInterceptor(ctx, command); // this call will ensure the node gets a WL and it's current parent gets RL. if (trace) log.trace("Attempting to get WL on node to be moved [" + command.getFqn() + "]"); if (command.getFqn() != null && !(configuration.getIsolationLevel() == IsolationLevel.NONE)) { lockManager.lockPessimistically(ctx, command.getFqn(), WRITE, false, false, true, false, null, false); if (ctx.getGlobalTransaction() != null) { ctx.getTransactionEntry().addRemovedNode(command.getFqn()); } lockManager.lockAllAndRecord(dataContainer.peek(command.getFqn(), true, false), WRITE, ctx); } if (command.getTo() != null && !(configuration.getIsolationLevel() == IsolationLevel.NONE)) { //now for an RL for the new parent. if (trace) log.trace("Attempting to get RL on new parent [" + command.getTo() + "]"); lockManager.lockPessimistically(ctx, command.getTo(), READ, false, false, false, false, null, false); lockManager.lockAllAndRecord(dataContainer.peek(command.getTo(), true, false), READ, ctx); } Object retValue = invokeNextInterceptor(ctx, command); if (ctx.getTransaction() == null) // not transactional { // do a REAL remove here. NodeSPI n = dataContainer.peek(command.getFqn(), true, false); if (n != null) lockManager.unlockAll(n, Thread.currentThread()); } return retValue; } @Override protected Object handleRemoveNodeCommand(InvocationContext ctx, RemoveNodeCommand command) throws Throwable { if (ctx.isLockingSuppressed()) return invokeNextInterceptor(ctx, command); // need to make a note of ALL nodes created here!! List createdNodes = new LinkedList(); // we need to mark new nodes created as deleted since they are only created to form a path to the node being removed, to // create a lock. boolean created = lockManager.lockPessimistically(ctx, command.getFqn(), WRITE, true, false, true, true, createdNodes, true); TransactionEntry entry = null; if (ctx.getGlobalTransaction() != null) { entry = ctx.getTransactionEntry(); entry.addRemovedNode(command.getFqn()); for (NodeSPI nodeSPI : createdNodes) { entry.addRemovedNode(nodeSPI.getFqn()); nodeSPI.markAsDeleted(true); } } lockAllForRemoval(dataContainer.peek(command.getFqn(), false, false), ctx, entry); if (!createdNodes.isEmpty()) { if (trace) log.trace("There were new nodes created, skipping notification on delete"); if (trace) log.trace("Changing 'skipNotification' for command 'remove' from " + command.isSkipSendingNodeEvents() + " to true"); command.setSkipSendingNodeEvents(true); } Object retVal = invokeNextInterceptor(ctx, command); // and make sure we remove all nodes we've created for the sake of later removal. //TODO: 2.2.0: [mmarkus] this logic should be moved within moveNodeCommand, as it is plain removal logic if (ctx.getGlobalTransaction() == null) { for (NodeSPI nodeSPI : createdNodes) dataContainer.removeFromDataStructure(nodeSPI.getFqn(), true); dataContainer.removeFromDataStructure(command.getFqn(), true); //TODO: 2.2.0: end of the logic that needs to be moved NodeSPI n = dataContainer.peek(command.getFqn(), true, false); if (n != null) lockManager.unlockAll(n, Thread.currentThread()); } // if this is a delete op and we had to create the node, return a FALSE as nothing *really* was deleted! return created ? false : retVal; } /** * Acquires write locks on the node and all child nodes, adding children to the list of removed nodes in the context. * * @param node node to inspect * @param ctx invocation context * @param entry transaction entry * @throws InterruptedException in the event of interruption */ public void lockAllForRemoval(NodeSPI node, InvocationContext ctx, TransactionEntry entry) throws InterruptedException { if (node == null) return; // lock node lockManager.lockAndRecord(node, WRITE, ctx); // add to deleted list if (entry != null) entry.addRemovedNode(node.getFqn()); // now children. Map children = node.getChildrenMapDirect(); if (children != null) { for (NodeSPI child : children.values()) { // lock child. lockAllForRemoval(child, ctx, entry); } } } @Override protected Object handleRemoveKeyCommand(InvocationContext ctx, RemoveKeyCommand command) throws Throwable { lockManager.lockPessimistically(ctx, command.getFqn(), WRITE, false, false, false, false, null, false); return invokeNextInterceptor(ctx, command); } @Override protected Object handleClearDataCommand(InvocationContext ctx, ClearDataCommand command) throws Throwable { lockManager.lockPessimistically(ctx, command.getFqn(), WRITE, false, false, false, false, null, false); return invokeNextInterceptor(ctx, command); } @Override protected Object handleEvictFqnCommand(InvocationContext ctx, EvictCommand command) throws Throwable { lockManager.lockPessimistically(ctx, command.getFqn(), WRITE, false, true, false, false, null, false); return invokeNextInterceptor(ctx, command); } @Override protected Object handleGetKeyValueCommand(InvocationContext ctx, GetKeyValueCommand command) throws Throwable { lockManager.lockPessimistically(ctx, command.getFqn(), READ, false, false, false, false, null, false); return invokeNextInterceptor(ctx, command); } @Override protected Object handleGetNodeCommand(InvocationContext ctx, GetNodeCommand command) throws Throwable { lockManager.lockPessimistically(ctx, command.getFqn(), READ, false, false, false, false, null, false); return invokeNextInterceptor(ctx, command); } @Override protected Object handleGetKeysCommand(InvocationContext ctx, GetKeysCommand command) throws Throwable { lockManager.lockPessimistically(ctx, command.getFqn(), READ, false, false, false, false, null, false); return invokeNextInterceptor(ctx, command); } @Override protected Object handleGetChildrenNamesCommand(InvocationContext ctx, GetChildrenNamesCommand command) throws Throwable { lockManager.lockPessimistically(ctx, command.getFqn(), READ, false, false, false, false, null, false); return invokeNextInterceptor(ctx, command); } @Override public void doAfterCall(InvocationContext ctx, VisitableCommand command) { if (!ctx.isValidTransaction()) lockManager.unlock(ctx); } /** * Remove all locks held by tx, remove the transaction from the transaction table */ private void commit(TransactionEntry entry, GlobalTransaction gtx) { if (trace) log.trace("committing cache with gtx " + gtx); if (entry == null) { log.error("entry for transaction " + gtx + " not found (maybe already committed)"); return; } // first remove nodes that should be deleted. for (Fqn fqn : entry.getRemovedNodes()) { dataContainer.removeFromDataStructure(fqn, false); } } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/interceptors/base/0000755000175000017500000000000011376174015026041 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/interceptors/base/CommandInterceptor.java0000644000175000017500000001311711006057544032501 0ustar twernertwernerpackage org.jboss.cache.interceptors.base; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.InvocationContext; import org.jboss.cache.commands.AbstractVisitor; import org.jboss.cache.commands.VisitableCommand; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.annotations.Inject; import org.jboss.cache.factories.annotations.Start; import org.jboss.cache.interceptors.InterceptorMBean; import java.util.Collections; import java.util.Map; /** * This is the base class for all interceptors to extend, and implements the {@link org.jboss.cache.commands.Visitor} interface * allowing it to intercept invocations on {@link org.jboss.cache.commands.VisitableCommand}s. *

* Commands are created either by the {@link org.jboss.cache.invocation.CacheInvocationDelegate} (for invocations on the {@link org.jboss.cache.Cache} * public interface), the {@link org.jboss.cache.invocation.NodeInvocationDelegate} for invocations on the {@link org.jboss.cache.Node} * public interface, or by the {@link org.jboss.cache.marshall.CommandAwareRpcDispatcher} for remotely originating invocations, and * are passed up the interceptor chain by using the {@link org.jboss.cache.interceptors.InterceptorChain} helper class. *

* When writing interceptors, authors can either override a specific visitXXX() method (such as {@link #visitGetKeyValueCommand(org.jboss.cache.InvocationContext, org.jboss.cache.commands.read.GetKeyValueCommand)}) * or the more generic {@link #handleDefault(org.jboss.cache.InvocationContext, org.jboss.cache.commands.VisitableCommand)} which is the default behaviour of * any visit method, as defined in {@link org.jboss.cache.commands.AbstractVisitor#handleDefault(org.jboss.cache.InvocationContext, org.jboss.cache.commands.VisitableCommand)}. *

* The preferred approach is to override the specific visitXXX() methods that are of interest rather than to override {@link #handleDefault(org.jboss.cache.InvocationContext, org.jboss.cache.commands.VisitableCommand)} * and then write a series of if statements or a switch block, if command-specific behaviour is needed. *

* * @author Mircea.Markus@jboss.com * @see VisitableCommand * @see org.jboss.cache.commands.Visitor * @see org.jboss.cache.interceptors.InterceptorChain * @since 2.2 */ public class CommandInterceptor extends AbstractVisitor implements InterceptorMBean { private boolean statsEnabled = false; private CommandInterceptor next; protected Log log; protected boolean trace; protected Configuration configuration; public CommandInterceptor() { log = LogFactory.getLog(getClass()); trace = log.isTraceEnabled(); } @Inject private void injectConfiguration(Configuration configuration) { this.configuration = configuration; } @Start private void checkStatisticsUsed() { setStatisticsEnabled(configuration.getExposeManagementStatistics()); } /** * @return true if gathering statistics for JMX is enabled on this interceptor. */ public boolean getStatisticsEnabled() { return statsEnabled; } /** * @param enabled whether gathering statistics for JMX are enabled. */ public void setStatisticsEnabled(boolean enabled) { statsEnabled = enabled; } /** * Returns a map of statistics. This is a default implementation which returns an empty map and should be overridden * if it is to be meaningful. * * @return an empty map */ public Map dumpStatistics() { return Collections.emptyMap(); } /** * Resets statistics gathered. Is a no-op, and should be overridden if it is to be meaningful. */ public void resetStatistics() { } /** * Retrieves the next interceptor in the chain. * * @return the next interceptor in the chain. */ public CommandInterceptor getNext() { return next; } /** * @return true if there is another interceptor in the chain after this; false otherwise. */ public boolean hasNext() { return getNext() != null; } /** * Sets the next interceptor in the chain to the interceptor passed in. * * @param next next interceptor in the chain. */ public void setNext(CommandInterceptor next) { this.next = next; } /** * Invokes the next interceptor in the chain. This is how interceptor implementations should pass a call up the chain * to the next interceptor. In previous (pre-2.2.0) implementations of JBoss Cache, this was done by calling *

super.invoke()
. * * @param ctx invocation context * @param command command to pass up the chain. * @return return value of the invocation * @throws Throwable in the event of problems */ public Object invokeNextInterceptor(InvocationContext ctx, VisitableCommand command) throws Throwable { return command.acceptVisitor(ctx, next); } /** * The default behaviour of the visitXXX methods, which is to ignore the call and pass the call up to the next * interceptor in the chain. * * @param ctx invocation context * @param command command to invoke * @return return value * @throws Throwable in the event of problems */ @Override protected Object handleDefault(InvocationContext ctx, VisitableCommand command) throws Throwable { return invokeNextInterceptor(ctx, command); } } ././@LongLink0000000000000000000000000000015200000000000011563 Lustar rootrootjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/interceptors/base/SkipCheckChainedInterceptor.javajbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/interceptors/base/SkipCheckChainedInterceptor0000644000175000017500000003147011032145203033312 0ustar twernertwernerpackage org.jboss.cache.interceptors.base; import org.jboss.cache.InvocationContext; import org.jboss.cache.commands.VisitableCommand; import org.jboss.cache.commands.read.ExistsCommand; import org.jboss.cache.commands.read.GetChildrenNamesCommand; import org.jboss.cache.commands.read.GetDataMapCommand; import org.jboss.cache.commands.read.GetKeyValueCommand; import org.jboss.cache.commands.read.GetKeysCommand; import org.jboss.cache.commands.read.GetNodeCommand; import org.jboss.cache.commands.read.GravitateDataCommand; import org.jboss.cache.commands.tx.CommitCommand; import org.jboss.cache.commands.tx.OptimisticPrepareCommand; import org.jboss.cache.commands.tx.PrepareCommand; import org.jboss.cache.commands.tx.RollbackCommand; import org.jboss.cache.commands.write.ClearDataCommand; import org.jboss.cache.commands.write.EvictCommand; import org.jboss.cache.commands.write.InvalidateCommand; import org.jboss.cache.commands.write.MoveCommand; import org.jboss.cache.commands.write.PutDataMapCommand; import org.jboss.cache.commands.write.PutForExternalReadCommand; import org.jboss.cache.commands.write.PutKeyValueCommand; import org.jboss.cache.commands.write.RemoveKeyCommand; import org.jboss.cache.commands.write.RemoveNodeCommand; /** * This interceptor will call {@link #skipInterception(org.jboss.cache.InvocationContext,org.jboss.cache.commands.VisitableCommand)} before invoking each visit method * (and the {@link #handleDefault(org.jboss.cache.InvocationContext, org.jboss.cache.commands.VisitableCommand)} method). If * {@link #skipInterception(org.jboss.cache.InvocationContext,org.jboss.cache.commands.VisitableCommand)} returns false, the invocation will be skipped * and passed up the interceptor chain instead. *

* Instead of overriding visitXXX() methods, implementations should override their handleXXX() counterparts defined in this class * instead, as well as the {@link #skipInterception(org.jboss.cache.InvocationContext,org.jboss.cache.commands.VisitableCommand)} method. * Also, instead of overriding {@link #handleDefault(org.jboss.cache.InvocationContext, org.jboss.cache.commands.VisitableCommand)}, implementors * should override {@link #handleAll(org.jboss.cache.InvocationContext, org.jboss.cache.commands.VisitableCommand)}. * * @author Mircea.Markus@jboss.com * @since 2.2 */ public abstract class SkipCheckChainedInterceptor extends CommandInterceptor { @Override public final Object visitPutDataMapCommand(InvocationContext ctx, PutDataMapCommand command) throws Throwable { if (skipInterception(ctx, command)) { return invokeNextInterceptor(ctx, command); } return handlePutDataMapCommand(ctx, command); } protected Object handlePutDataMapCommand(InvocationContext ctx, PutDataMapCommand command) throws Throwable { return handleAll(ctx, command); } @Override public final Object visitPutKeyValueCommand(InvocationContext ctx, PutKeyValueCommand command) throws Throwable { if (skipInterception(ctx, command)) { return invokeNextInterceptor(ctx, command); } return handlePutKeyValueCommand(ctx, command); } @Override public final Object visitPutForExternalReadCommand(InvocationContext ctx, PutForExternalReadCommand command) throws Throwable { if (skipInterception(ctx, command)) { return invokeNextInterceptor(ctx, command); } return handlePutForExternalReadCommand(ctx, command); } protected Object handlePutKeyValueCommand(InvocationContext ctx, PutKeyValueCommand command) throws Throwable { return handleAll(ctx, command); } protected Object handlePutForExternalReadCommand(InvocationContext ctx, PutForExternalReadCommand command) throws Throwable { return handleAll(ctx, command); } @Override public final Object visitRemoveNodeCommand(InvocationContext ctx, RemoveNodeCommand command) throws Throwable { if (skipInterception(ctx, command)) { return invokeNextInterceptor(ctx, command); } return handleRemoveNodeCommand(ctx, command); } protected Object handleRemoveNodeCommand(InvocationContext ctx, RemoveNodeCommand command) throws Throwable { return handleAll(ctx, command); } @Override public final Object visitClearDataCommand(InvocationContext ctx, ClearDataCommand command) throws Throwable { if (skipInterception(ctx, command)) { return invokeNextInterceptor(ctx, command); } return handleRemoveDataCommand(ctx, command); } protected Object handleRemoveDataCommand(InvocationContext ctx, ClearDataCommand command) throws Throwable { return handleAll(ctx, command); } @Override public final Object visitEvictFqnCommand(InvocationContext ctx, EvictCommand command) throws Throwable { if (skipInterception(ctx, command)) { return invokeNextInterceptor(ctx, command); } return handleEvictFqnCommand(ctx, command); } protected Object handleEvictFqnCommand(InvocationContext ctx, EvictCommand command) throws Throwable { return handleAll(ctx, command); } @Override public final Object visitInvalidateCommand(InvocationContext ctx, InvalidateCommand command) throws Throwable { if (skipInterception(ctx, command)) { return invokeNextInterceptor(ctx, command); } return handleInvalidateCommand(ctx, command); } protected Object handleInvalidateCommand(InvocationContext ctx, InvalidateCommand command) throws Throwable { return handleAll(ctx, command); } @Override public final Object visitRemoveKeyCommand(InvocationContext ctx, RemoveKeyCommand command) throws Throwable { if (skipInterception(ctx, command)) { return invokeNextInterceptor(ctx, command); } return handleRemoveKeyCommand(ctx, command); } protected Object handleRemoveKeyCommand(InvocationContext ctx, RemoveKeyCommand command) throws Throwable { return handleAll(ctx, command); } @Override public final Object visitGetDataMapCommand(InvocationContext ctx, GetDataMapCommand command) throws Throwable { if (skipInterception(ctx, command)) { return invokeNextInterceptor(ctx, command); } return handleGetDataMapCommand(ctx, command); } protected Object handleGetDataMapCommand(InvocationContext ctx, GetDataMapCommand command) throws Throwable { return handleAll(ctx, command); } @Override public final Object visitExistsNodeCommand(InvocationContext ctx, ExistsCommand command) throws Throwable { if (skipInterception(ctx, command)) { return invokeNextInterceptor(ctx, command); } return handleExistsNodeCommand(ctx, command); } protected Object handleExistsNodeCommand(InvocationContext ctx, ExistsCommand command) throws Throwable { return handleAll(ctx, command); } @Override public final Object visitGetKeyValueCommand(InvocationContext ctx, GetKeyValueCommand command) throws Throwable { if (skipInterception(ctx, command)) { return invokeNextInterceptor(ctx, command); } return handleGetKeyValueCommand(ctx, command); } protected Object handleGetKeyValueCommand(InvocationContext ctx, GetKeyValueCommand command) throws Throwable { return handleAll(ctx, command); } @Override public final Object visitGetNodeCommand(InvocationContext ctx, GetNodeCommand command) throws Throwable { if (skipInterception(ctx, command)) { return invokeNextInterceptor(ctx, command); } return handleGetNodeCommand(ctx, command); } protected Object handleGetNodeCommand(InvocationContext ctx, GetNodeCommand command) throws Throwable { return handleAll(ctx, command); } @Override public final Object visitGetKeysCommand(InvocationContext ctx, GetKeysCommand command) throws Throwable { if (skipInterception(ctx, command)) { return invokeNextInterceptor(ctx, command); } return handleGetKeysCommand(ctx, command); } protected Object handleGetKeysCommand(InvocationContext ctx, GetKeysCommand command) throws Throwable { return handleAll(ctx, command); } @Override public final Object visitGetChildrenNamesCommand(InvocationContext ctx, GetChildrenNamesCommand command) throws Throwable { if (skipInterception(ctx, command)) { return invokeNextInterceptor(ctx, command); } return handleGetChildrenNamesCommand(ctx, command); } protected Object handleGetChildrenNamesCommand(InvocationContext ctx, GetChildrenNamesCommand command) throws Throwable { return handleAll(ctx, command); } @Override public final Object visitMoveCommand(InvocationContext ctx, MoveCommand command) throws Throwable { if (skipInterception(ctx, command)) { return invokeNextInterceptor(ctx, command); } return handleMoveCommand(ctx, command); } protected Object handleMoveCommand(InvocationContext ctx, MoveCommand command) throws Throwable { return handleAll(ctx, command); } @Override public final Object visitGravitateDataCommand(InvocationContext ctx, GravitateDataCommand command) throws Throwable { if (skipInterception(ctx, command)) { return invokeNextInterceptor(ctx, command); } return handleGravitateDataCommand(ctx, command); } protected Object handleGravitateDataCommand(InvocationContext ctx, GravitateDataCommand command) throws Throwable { return handleAll(ctx, command); } @Override public final Object visitPrepareCommand(InvocationContext ctx, PrepareCommand command) throws Throwable { if (skipInterception(ctx, command)) { return invokeNextInterceptor(ctx, command); } return handlePrepareCommand(ctx, command); } protected Object handlePrepareCommand(InvocationContext ctx, PrepareCommand command) throws Throwable { return handleAll(ctx, command); } @Override public final Object visitRollbackCommand(InvocationContext ctx, RollbackCommand command) throws Throwable { if (skipInterception(ctx, command)) { return invokeNextInterceptor(ctx, command); } return handleRollbackCommand(ctx, command); } protected Object handleRollbackCommand(InvocationContext ctx, RollbackCommand command) throws Throwable { return handleAll(ctx, command); } @Override public final Object visitCommitCommand(InvocationContext ctx, CommitCommand command) throws Throwable { if (skipInterception(ctx, command)) { return invokeNextInterceptor(ctx, command); } return handleCommitCommand(ctx, command); } protected Object handleCommitCommand(InvocationContext ctx, CommitCommand command) throws Throwable { return handleAll(ctx, command); } @Override public final Object visitOptimisticPrepareCommand(InvocationContext ctx, OptimisticPrepareCommand command) throws Throwable { if (skipInterception(ctx, command)) { return invokeNextInterceptor(ctx, command); } return handleOptimisticPrepareCommand(ctx, command); } protected Object handleOptimisticPrepareCommand(InvocationContext ctx, OptimisticPrepareCommand command) throws Throwable { return handleAll(ctx, command); } @Override public final Object handleDefault(InvocationContext ctx, VisitableCommand command) throws Throwable { if (skipInterception(ctx, command)) { return invokeNextInterceptor(ctx, command); } return handleAll(ctx, command); } /** * Default implementation, which just passes the call up the interceptor chain * * @param ctx invocation context * @param command command * @return return value * @throws Throwable in the event of problems */ protected Object handleAll(InvocationContext ctx, VisitableCommand command) throws Throwable { return invokeNextInterceptor(ctx, command); } /** * Tests whether the command should be intercepted or not. This is invoked before any of the handleXXX() methods. * * @param ctx invocation context * @param command command * @return true if the invocation should skip the current interceptor and move on to the next in the chain, false otherwise. */ protected abstract boolean skipInterception(InvocationContext ctx, VisitableCommand command); } ././@LongLink0000000000000000000000000000015700000000000011570 Lustar rootrootjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/interceptors/base/PostProcessingCommandInterceptor.javajbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/interceptors/base/PostProcessingCommandInterc0000644000175000017500000002665711026045012033414 0ustar twernertwernerpackage org.jboss.cache.interceptors.base; import org.jboss.cache.InvocationContext; import org.jboss.cache.commands.VisitableCommand; import org.jboss.cache.commands.read.ExistsCommand; import org.jboss.cache.commands.read.GetChildrenNamesCommand; import org.jboss.cache.commands.read.GetDataMapCommand; import org.jboss.cache.commands.read.GetKeyValueCommand; import org.jboss.cache.commands.read.GetKeysCommand; import org.jboss.cache.commands.read.GetNodeCommand; import org.jboss.cache.commands.read.GravitateDataCommand; import org.jboss.cache.commands.tx.CommitCommand; import org.jboss.cache.commands.tx.OptimisticPrepareCommand; import org.jboss.cache.commands.tx.PrepareCommand; import org.jboss.cache.commands.tx.RollbackCommand; import org.jboss.cache.commands.write.*; /** * This interceptor will call {@link #doAfterCall(org.jboss.cache.InvocationContext,org.jboss.cache.commands.VisitableCommand)} after invoking each visit method * (and the {@link #handleDefault(org.jboss.cache.InvocationContext, org.jboss.cache.commands.VisitableCommand)} method) in * a finally block. *

* It is useful if common cleanup code is required at the end of each call. *

* Instead of overriding visitXXX() methods, implementations should override their handleXXX() counterparts defined in this class * instead, as well as the {@link #doAfterCall(org.jboss.cache.InvocationContext,org.jboss.cache.commands.VisitableCommand)} method. * * @author Mircea.Markus@jboss.com * @since 2.2 */ public abstract class PostProcessingCommandInterceptor extends CommandInterceptor { @Override public final Object visitPutDataMapCommand(InvocationContext ctx, PutDataMapCommand command) throws Throwable { try { return handlePutDataMapCommand(ctx, command); } finally { doAfterCall(ctx, command); } } protected Object handlePutDataMapCommand(InvocationContext ctx, PutDataMapCommand command) throws Throwable { return handleDefault(ctx, command); } @Override public final Object visitPutKeyValueCommand(InvocationContext ctx, PutKeyValueCommand command) throws Throwable { try { return handlePutKeyValueCommand(ctx, command); } finally { doAfterCall(ctx, command); } } @Override public final Object visitPutForExternalReadCommand(InvocationContext ctx, PutForExternalReadCommand command) throws Throwable { try { return handlePutForExternalReadCommand(ctx, command); } finally { doAfterCall(ctx, command); } } protected Object handlePutKeyValueCommand(InvocationContext ctx, PutKeyValueCommand command) throws Throwable { return handleDefault(ctx, command); } protected Object handlePutForExternalReadCommand(InvocationContext ctx, PutForExternalReadCommand command) throws Throwable { return handleDefault(ctx, command); } @Override public final Object visitRemoveNodeCommand(InvocationContext ctx, RemoveNodeCommand command) throws Throwable { try { return handleRemoveNodeCommand(ctx, command); } finally { doAfterCall(ctx, command); } } protected Object handleRemoveNodeCommand(InvocationContext ctx, RemoveNodeCommand command) throws Throwable { return handleDefault(ctx, command); } @Override public final Object visitCreateNodeCommand(InvocationContext ctx, CreateNodeCommand command) throws Throwable { try { return handleCreateNodeCommand(ctx, command); } finally { doAfterCall(ctx, command); } } protected Object handleCreateNodeCommand(InvocationContext ctx, CreateNodeCommand command) throws Throwable { return handleDefault(ctx, command); } @Override public final Object visitClearDataCommand(InvocationContext ctx, ClearDataCommand command) throws Throwable { try { return handleClearDataCommand(ctx, command); } finally { doAfterCall(ctx, command); } } protected Object handleClearDataCommand(InvocationContext ctx, ClearDataCommand command) throws Throwable { return handleDefault(ctx, command); } @Override public final Object visitEvictFqnCommand(InvocationContext ctx, EvictCommand command) throws Throwable { try { return handleEvictFqnCommand(ctx, command); } finally { doAfterCall(ctx, command); } } protected Object handleEvictFqnCommand(InvocationContext ctx, EvictCommand command) throws Throwable { return handleDefault(ctx, command); } @Override public final Object visitInvalidateCommand(InvocationContext ctx, InvalidateCommand command) throws Throwable { try { return handleInvalidateCommand(ctx, command); } finally { doAfterCall(ctx, command); } } protected Object handleInvalidateCommand(InvocationContext ctx, InvalidateCommand command) throws Throwable { return handleDefault(ctx, command); } @Override public final Object visitRemoveKeyCommand(InvocationContext ctx, RemoveKeyCommand command) throws Throwable { try { return handleRemoveKeyCommand(ctx, command); } finally { doAfterCall(ctx, command); } } protected Object handleRemoveKeyCommand(InvocationContext ctx, RemoveKeyCommand command) throws Throwable { return handleDefault(ctx, command); } @Override public final Object visitGetDataMapCommand(InvocationContext ctx, GetDataMapCommand command) throws Throwable { try { return handleGetDataMapCommand(ctx, command); } finally { doAfterCall(ctx, command); } } protected Object handleGetDataMapCommand(InvocationContext ctx, GetDataMapCommand command) throws Throwable { return handleDefault(ctx, command); } @Override public final Object visitExistsNodeCommand(InvocationContext ctx, ExistsCommand command) throws Throwable { try { return handleExistsNodeCommand(ctx, command); } finally { doAfterCall(ctx, command); } } protected Object handleExistsNodeCommand(InvocationContext ctx, ExistsCommand command) throws Throwable { return handleDefault(ctx, command); } @Override public final Object visitGetKeyValueCommand(InvocationContext ctx, GetKeyValueCommand command) throws Throwable { try { return handleGetKeyValueCommand(ctx, command); } finally { doAfterCall(ctx, command); } } protected Object handleGetKeyValueCommand(InvocationContext ctx, GetKeyValueCommand command) throws Throwable { return handleDefault(ctx, command); } @Override public final Object visitGetNodeCommand(InvocationContext ctx, GetNodeCommand command) throws Throwable { try { return handleGetNodeCommand(ctx, command); } finally { doAfterCall(ctx, command); } } protected Object handleGetNodeCommand(InvocationContext ctx, GetNodeCommand command) throws Throwable { return handleDefault(ctx, command); } @Override public final Object visitGetKeysCommand(InvocationContext ctx, GetKeysCommand command) throws Throwable { try { return handleGetKeysCommand(ctx, command); } finally { doAfterCall(ctx, command); } } protected Object handleGetKeysCommand(InvocationContext ctx, GetKeysCommand command) throws Throwable { return handleDefault(ctx, command); } @Override public final Object visitGetChildrenNamesCommand(InvocationContext ctx, GetChildrenNamesCommand command) throws Throwable { try { return handleGetChildrenNamesCommand(ctx, command); } finally { doAfterCall(ctx, command); } } protected Object handleGetChildrenNamesCommand(InvocationContext ctx, GetChildrenNamesCommand command) throws Throwable { return handleDefault(ctx, command); } @Override public final Object visitMoveCommand(InvocationContext ctx, MoveCommand command) throws Throwable { try { return handleMoveCommand(ctx, command); } finally { doAfterCall(ctx, command); } } protected Object handleMoveCommand(InvocationContext ctx, MoveCommand command) throws Throwable { return handleDefault(ctx, command); } @Override public final Object visitGravitateDataCommand(InvocationContext ctx, GravitateDataCommand command) throws Throwable { try { return handleGravitateDataCommand(ctx, command); } finally { doAfterCall(ctx, command); } } protected Object handleGravitateDataCommand(InvocationContext ctx, GravitateDataCommand command) throws Throwable { return handleDefault(ctx, command); } @Override public final Object visitPrepareCommand(InvocationContext ctx, PrepareCommand command) throws Throwable { try { return handlePrepareCommand(ctx, command); } finally { doAfterCall(ctx, command); } } protected Object handlePrepareCommand(InvocationContext ctx, PrepareCommand command) throws Throwable { return handleDefault(ctx, command); } @Override public final Object visitRollbackCommand(InvocationContext ctx, RollbackCommand command) throws Throwable { try { return handleRollbackCommand(ctx, command); } finally { doAfterCall(ctx, command); } } protected Object handleRollbackCommand(InvocationContext ctx, RollbackCommand command) throws Throwable { return handleDefault(ctx, command); } @Override public final Object visitCommitCommand(InvocationContext ctx, CommitCommand command) throws Throwable { try { return handleCommitCommand(ctx, command); } finally { doAfterCall(ctx, command); } } protected Object handleCommitCommand(InvocationContext ctx, CommitCommand command) throws Throwable { return handleDefault(ctx, command); } @Override public final Object visitOptimisticPrepareCommand(InvocationContext ctx, OptimisticPrepareCommand command) throws Throwable { try { return handleOptimisticPrepareCommand(ctx, command); } finally { doAfterCall(ctx, command); } } protected Object handleOptimisticPrepareCommand(InvocationContext ctx, OptimisticPrepareCommand command) throws Throwable { return handleDefault(ctx, command); } /** * Callback that is invoked after every handleXXX() method defined above. * * @param ctx invocation context * @param command command which was invoked */ protected abstract void doAfterCall(InvocationContext ctx, VisitableCommand command); } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/interceptors/PassivationInterceptor.java0000644000175000017500000000772511026046072032515 0ustar twernertwernerpackage org.jboss.cache.interceptors; import org.jboss.cache.DataContainer; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.NodeSPI; import org.jboss.cache.commands.write.EvictCommand; import org.jboss.cache.factories.annotations.Inject; import org.jboss.cache.interceptors.base.CommandInterceptor; import org.jboss.cache.loader.CacheLoader; import org.jboss.cache.loader.CacheLoaderManager; import org.jboss.cache.notifications.Notifier; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicLong; /** * Writes evicted nodes back to the store on the way in through the * CacheLoader, either before each method call (no TXs), or at TX commit. * * @author {Hany Mesha} * @version $Id: PassivationInterceptor.java 6000 2008-06-17 23:43:54Z manik.surtani@jboss.com $ */ public class PassivationInterceptor extends CommandInterceptor implements PassivationInterceptorMBean { private final AtomicLong passivations = new AtomicLong(0); protected CacheLoader loader; private Notifier notifier; private DataContainer dataContainer; @Inject public void setDependencies(Notifier notifier, DataContainer dataContainer, CacheLoaderManager loaderManager) { this.notifier = notifier; this.dataContainer = dataContainer; this.loader = loaderManager.getCacheLoader(); } /** * Notifies the cache instance listeners that the evicted node is about to * be passivated and stores the evicted node and its attributes back to the * store using the CacheLoader. */ @Override public Object visitEvictFqnCommand(InvocationContext ctx, EvictCommand command) throws Throwable { if (command.isRecursive()) { List fqnsToEvict = dataContainer.getNodesForEviction(command.getFqn(), true); if (fqnsToEvict != null) { for (Fqn f : fqnsToEvict) { passivate(ctx, f); } } } else { passivate(ctx, command.getFqn()); } return invokeNextInterceptor(ctx, command); } private void passivate(InvocationContext ctx, Fqn fqn) throws Throwable { try { // evict method local doesn't hold attributes therefore we have // to get them manually Map attributes = getNodeAttributes(fqn); // notify listeners that this node is about to be passivated notifier.notifyNodePassivated(fqn, true, attributes, ctx); if (trace) log.trace("Passivating " + fqn); loader.put(fqn, attributes); notifier.notifyNodePassivated(fqn, false, Collections.emptyMap(), ctx); if (getStatisticsEnabled()) passivations.getAndIncrement(); } catch (NodeNotLoadedException e) { if (trace) { log.trace("Node " + fqn + " not loaded in memory; passivation skipped"); } } } public long getPassivations() { return passivations.get(); } @Override public void resetStatistics() { passivations.set(0); } @Override public Map dumpStatistics() { Map retval = new HashMap(); retval.put("Passivations", passivations.get()); return retval; } /** * Returns attributes for a node. */ private Map getNodeAttributes(Fqn fqn) throws NodeNotLoadedException { if (fqn == null) { throw new NodeNotLoadedException(); } NodeSPI n = dataContainer.peek(fqn, true, false); if (n != null) { return n.getDataDirect(); } else { throw new NodeNotLoadedException(); } } private static class NodeNotLoadedException extends Exception { /** * The serialVersionUID */ private static final long serialVersionUID = -4078972305344328905L; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/interceptors/CallInterceptor.java0000644000175000017500000001270211032145203031050 0ustar twernertwernerpackage org.jboss.cache.interceptors; import org.jboss.cache.InvocationContext; import org.jboss.cache.commands.ReplicableCommand; import org.jboss.cache.commands.ReversibleCommand; import org.jboss.cache.commands.VisitableCommand; import org.jboss.cache.commands.tx.CommitCommand; import org.jboss.cache.commands.tx.OptimisticPrepareCommand; import org.jboss.cache.commands.tx.PrepareCommand; import org.jboss.cache.commands.tx.RollbackCommand; import org.jboss.cache.commands.write.ClearDataCommand; import org.jboss.cache.commands.write.MoveCommand; import org.jboss.cache.commands.write.PutDataMapCommand; import org.jboss.cache.commands.write.PutForExternalReadCommand; import org.jboss.cache.commands.write.PutKeyValueCommand; import org.jboss.cache.commands.write.RemoveKeyCommand; import org.jboss.cache.commands.write.RemoveNodeCommand; import org.jboss.cache.interceptors.base.CommandInterceptor; import org.jboss.cache.transaction.GlobalTransaction; import javax.transaction.Transaction; /** * Always at the end of the chain, directly in front of the cache. Simply calls into the cache using reflection. * If the call resulted in a modification, add the Modification to the end of the modification list * keyed by the current transaction. *

* Although always added to the end of an optimistically locked chain as well, calls should not make it down to * this interceptor unless it is a call the OptimisticNodeInterceptor knows nothing about. * * @author Bela Ban * @version $Id: CallInterceptor.java 6120 2008-06-30 11:58:59Z manik.surtani@jboss.com $ */ public class CallInterceptor extends CommandInterceptor { @Override public Object visitPrepareCommand(InvocationContext ctx, PrepareCommand command) throws Throwable { if (trace) log.trace("Suppressing invocation of method handlePrepareCommand."); return null; } @Override public Object visitOptimisticPrepareCommand(InvocationContext ctx, OptimisticPrepareCommand command) throws Throwable { if (trace) log.trace("Suppressing invocation of method handleOptimisticPrepareCommand."); return null; } @Override public Object visitCommitCommand(InvocationContext ctx, CommitCommand command) throws Throwable { if (trace) log.trace("Suppressing invocation of method handleCommitCommand."); return null; } @Override public Object visitRollbackCommand(InvocationContext ctx, RollbackCommand command) throws Throwable { if (trace) log.trace("Suppressing invocation of method handleRollbackCommand."); return null; } @Override public Object handleDefault(InvocationContext ctx, VisitableCommand command) throws Throwable { if (trace) log.trace("Executing command: " + command + "."); return invokeCommand(ctx, command); } private Object invokeCommand(InvocationContext ctx, ReplicableCommand command) throws Throwable { Object retval; try { retval = command.perform(ctx); } catch (Throwable t) { Transaction tx = ctx.getTransaction(); if (ctx.isValidTransaction()) { tx.setRollbackOnly(); } throw t; } return retval; } @Override public Object visitPutDataMapCommand(InvocationContext ctx, PutDataMapCommand command) throws Throwable { return handleAlterCacheMethod(ctx, command); } @Override public Object visitPutKeyValueCommand(InvocationContext ctx, PutKeyValueCommand command) throws Throwable { return handleAlterCacheMethod(ctx, command); } @Override public Object visitPutForExternalReadCommand(InvocationContext ctx, PutForExternalReadCommand command) throws Throwable { return handleAlterCacheMethod(ctx, command); } @Override public Object visitRemoveNodeCommand(InvocationContext ctx, RemoveNodeCommand command) throws Throwable { return handleAlterCacheMethod(ctx, command); } @Override public Object visitClearDataCommand(InvocationContext ctx, ClearDataCommand command) throws Throwable { return handleAlterCacheMethod(ctx, command); } @Override public Object visitRemoveKeyCommand(InvocationContext ctx, RemoveKeyCommand command) throws Throwable { return handleAlterCacheMethod(ctx, command); } @Override public Object visitMoveCommand(InvocationContext ctx, MoveCommand command) throws Throwable { return handleAlterCacheMethod(ctx, command); } /** * only add the modification to the modification list if we are using pessimistic locking. * Optimistic locking calls *should* not make it this far down the interceptor chain, but just * in case a method has been invoked that the OptimisticNodeInterceptor knows nothing about, it will * filter down here. */ private Object handleAlterCacheMethod(InvocationContext ctx, ReversibleCommand command) throws Throwable { Object result = invokeCommand(ctx, command); if (ctx.isValidTransaction() && !configuration.isNodeLockingOptimistic()) { GlobalTransaction gtx = ctx.getGlobalTransaction(); if (gtx == null) { if (log.isDebugEnabled()) { log.debug("didn't find GlobalTransaction for " + ctx.getTransaction() + "; won't add modification to transaction list"); } } else { ctx.getTransactionEntry().addModification(command); } } return result; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/interceptors/BaseRpcInterceptor.java0000644000175000017500000001620311075447240031531 0ustar twernertwerner/** * */ package org.jboss.cache.interceptors; import org.jboss.cache.InvocationContext; import org.jboss.cache.RPCManager; import org.jboss.cache.buddyreplication.BuddyManager; import org.jboss.cache.cluster.ReplicationQueue; import org.jboss.cache.commands.ReplicableCommand; import org.jboss.cache.commands.VisitableCommand; import org.jboss.cache.config.Option; import org.jboss.cache.factories.CommandsFactory; import org.jboss.cache.factories.annotations.Inject; import org.jboss.cache.factories.annotations.Start; import org.jboss.cache.interceptors.base.CommandInterceptor; import org.jboss.cache.transaction.GlobalTransaction; import org.jboss.cache.transaction.TransactionEntry; import org.jboss.cache.transaction.TransactionTable; import org.jgroups.Address; import javax.transaction.Transaction; import java.util.List; import java.util.Vector; /** * Acts as a base for all RPC calls - subclassed by * {@link ReplicationInterceptor} and {@link OptimisticReplicationInterceptor}. * * @author Manik Surtani (manik@jboss.org) */ public abstract class BaseRpcInterceptor extends CommandInterceptor { private BuddyManager buddyManager; private boolean usingBuddyReplication; private ReplicationQueue replicationQueue; protected TransactionTable txTable; private CommandsFactory commandsFactory; protected RPCManager rpcManager; protected boolean defaultSynchronous; @Inject public void injectComponents(RPCManager rpcManager, BuddyManager buddyManager, ReplicationQueue replicationQueue, TransactionTable txTable, CommandsFactory commandsFactory) { this.rpcManager = rpcManager; this.replicationQueue = replicationQueue; this.buddyManager = buddyManager; this.txTable = txTable; this.commandsFactory = commandsFactory; } @Start private void init() { usingBuddyReplication = configuration.getBuddyReplicationConfig() != null && configuration.getBuddyReplicationConfig().isEnabled(); defaultSynchronous = configuration.getCacheMode().isSynchronous(); } /** * Checks whether any of the responses are exceptions. If yes, re-throws * them (as exceptions or runtime exceptions). */ protected void checkResponses(List rsps) throws Throwable { if (rsps != null) { for (Object rsp : rsps) { if (rsp != null && rsp instanceof Throwable) { // lets print a stack trace first. if (log.isDebugEnabled()) log.debug("Received Throwable from remote node", (Throwable) rsp); throw (Throwable) rsp; } } } } protected void replicateCall(InvocationContext ctx, ReplicableCommand call, boolean sync, Option o, boolean useOutOfBandMessage) throws Throwable { replicateCall(ctx, null, call, sync, o, useOutOfBandMessage); } protected void replicateCall(InvocationContext ctx, ReplicableCommand call, boolean sync, Option o) throws Throwable { replicateCall(ctx, null, call, sync, o, false); } protected void replicateCall(InvocationContext ctx, Vector

recipients, ReplicableCommand c, boolean sync, Option o, boolean useOutOfBandMessage) throws Throwable { long syncReplTimeout = configuration.getSyncReplTimeout(); // test for option overrides if (o != null) { if (o.isForceAsynchronous()) sync = false; else if (o.isForceSynchronous()) sync = true; if (o.getSyncReplTimeout() > 0) syncReplTimeout = o.getSyncReplTimeout(); } // tx-level overrides are more important Transaction tx = ctx.getTransaction(); if (tx != null) { TransactionEntry te = ctx.getTransactionEntry(); if (te != null) { if (te.isForceAsyncReplication()) sync = false; else if (te.isForceSyncReplication()) sync = true; } } replicateCall(recipients, c, sync, true, useOutOfBandMessage, false, syncReplTimeout); } protected void replicateCall(Vector
recipients, ReplicableCommand call, boolean sync, boolean wrapCacheCommandInReplicateMethod, boolean useOutOfBandMessage, boolean isBroadcast, long timeout) throws Throwable { if (trace) log.trace("Broadcasting call " + call + " to recipient list " + recipients); if (!sync && replicationQueue != null && !usingBuddyReplication) { if (trace) log.trace("Putting call " + call + " on the replication queue."); replicationQueue.add(commandsFactory.buildReplicateCommand(call)); } else { if (usingBuddyReplication && !isBroadcast) call = buddyManager.transformFqns((VisitableCommand) call); Vector
callRecipients = recipients; if (callRecipients == null) { callRecipients = usingBuddyReplication && !isBroadcast ? buddyManager.getBuddyAddressesAsVector() : null; if (trace) log.trace("Setting call recipients to " + callRecipients + " since the original list of recipients passed in is null."); } ReplicableCommand toCall = wrapCacheCommandInReplicateMethod ? commandsFactory.buildReplicateCommand(call) : call; List rsps = rpcManager.callRemoteMethods(callRecipients, toCall, sync, // is synchronised? timeout, useOutOfBandMessage ); if (trace) log.trace("responses=" + rsps); if (sync) checkResponses(rsps); } } /** * It does not make sense replicating a transaction method(commit, rollback, prepare) if one of the following is true: *
    *    - call was not initiated here, but on other member of the cluster
    *    - there is no transaction. Why broadcast a commit or rollback if there is no transaction going on?
    *    - the current transaction did not modify any data
    * 
*/ protected boolean skipReplicationOfTransactionMethod(InvocationContext ctx) { GlobalTransaction gtx = ctx.getGlobalTransaction(); return ctx.getTransaction() == null || gtx == null || gtx.isRemote() || ctx.getOptionOverrides().isCacheModeLocal() || !ctx.isTxHasMods(); } /** * The call runs in a transaction and it was initiated on this node of the cluster. */ protected boolean isTransactionalAndLocal(InvocationContext ctx) { GlobalTransaction gtx = ctx.getGlobalTransaction(); boolean isInitiatedHere = gtx != null && !gtx.isRemote(); return isInitiatedHere && (ctx.getTransaction() != null); } protected boolean isSynchronous(Option option) { if (option != null) { if (option.isForceSynchronous()) return true; else if (option.isForceAsynchronous()) return false; } return defaultSynchronous; } protected boolean isLocalModeForced(InvocationContext ctx) { if (ctx.getOptionOverrides() != null && ctx.getOptionOverrides().isCacheModeLocal()) { if (trace) log.trace("LOCAL mode forced on invocation. Suppressing clustered events."); return true; } return false; } }jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/interceptors/OptimisticInterceptor.java0000644000175000017500000001123311017017011032315 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.interceptors; import org.jboss.cache.CacheException; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.NodeFactory; import org.jboss.cache.NodeSPI; import org.jboss.cache.factories.annotations.Inject; import org.jboss.cache.interceptors.base.CommandInterceptor; import org.jboss.cache.lock.LockManager; import static org.jboss.cache.lock.LockType.READ; import org.jboss.cache.lock.TimeoutException; import org.jboss.cache.optimistic.TransactionWorkspace; import org.jboss.cache.optimistic.WorkspaceNode; import org.jboss.cache.transaction.GlobalTransaction; import org.jboss.cache.transaction.OptimisticTransactionEntry; import org.jboss.cache.transaction.TransactionTable; import javax.transaction.Transaction; import javax.transaction.TransactionManager; import java.util.List; /** * Abstract interceptor for optimistic locking * * @author Manik Surtani (manik@jboss.org) */ public abstract class OptimisticInterceptor extends CommandInterceptor { protected TransactionManager txManager; protected TransactionTable txTable; protected LockManager lockManager; @Inject private void injectDependencies(TransactionManager txManager, TransactionTable txTable, LockManager lockManager) { this.txManager = txManager; this.txTable = txTable; this.lockManager = lockManager; } protected TransactionWorkspace getTransactionWorkspace(InvocationContext ctx) throws CacheException { OptimisticTransactionEntry transactionEntry = (OptimisticTransactionEntry) ctx.getTransactionEntry(); if (transactionEntry == null) { throw new CacheException("Unable to map global transaction " + ctx.getGlobalTransaction() + " to transaction entry when trying to retrieve transaction workspace."); } // try and get the workspace from the transaction return transactionEntry.getTransactionWorkSpace(); } /** * Adds the Fqn of the node as well as all children and childrens children to the list. */ protected void greedyGetFqns(List list, NodeSPI n, Fqn newBase) { list.add(n.getFqn()); Fqn newFqn = Fqn.fromRelativeElements(newBase, n.getFqn().getLastElement()); list.add(newFqn); for (NodeSPI child : n.getChildrenDirect()) { greedyGetFqns(list, child, newFqn); } } /** * @return the {@link org.jboss.cache.transaction.GlobalTransaction}, extracted from the current {@link org.jboss.cache.InvocationContext}. * @throws CacheException if the {@link org.jboss.cache.transaction.GlobalTransaction} or {@link javax.transaction.Transaction} associated with the * {@link org.jboss.cache.InvocationContext} is null. */ protected GlobalTransaction getGlobalTransaction(InvocationContext ctx) throws CacheException { Transaction tx = ctx.getTransaction(); if (tx == null) throw new CacheException("Transaction associated with the current invocation is null!"); GlobalTransaction gtx = ctx.getGlobalTransaction(); if (gtx == null) throw new CacheException("GlobalTransaction associated with the current invocation is null!"); return gtx; } protected void undeleteWorkspaceNode(WorkspaceNode nodeToUndelete, TransactionWorkspace workspace) { undeleteWorkspaceNode(nodeToUndelete, workspace.getNode(nodeToUndelete.getFqn().getParent())); } /** * Undeletes a node that already exists in the workspace, by setting appropriate flags and re-adding to parent's child map. * * @param nodeToUndelete WorkspaceNode to undelete * @param parent parent of node to undelete */ @SuppressWarnings("unchecked") protected void undeleteWorkspaceNode(WorkspaceNode nodeToUndelete, WorkspaceNode parent) { nodeToUndelete.markAsDeleted(false); nodeToUndelete.clearData(); // add in parent again parent.addChild(nodeToUndelete); nodeToUndelete.markAsResurrected(true); } @SuppressWarnings("unchecked") protected WorkspaceNode lockAndCreateWorkspaceNode(NodeFactory nodeFactory, NodeSPI node, TransactionWorkspace workspace, GlobalTransaction gtx, long timeout) { boolean locked = lockManager.lock(node, READ, gtx, timeout); if (!locked) throw new TimeoutException("Unable to lock node " + node.getFqn() + " after timeout " + timeout + " for copying into workspace"); WorkspaceNode wn = nodeFactory.createWorkspaceNode(node, workspace); lockManager.unlock(node, gtx); return wn; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/interceptors/InterceptorChain.java0000644000175000017500000002116511012076160031225 0ustar twernertwernerpackage org.jboss.cache.interceptors; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.CacheException; import org.jboss.cache.InvocationContext; import org.jboss.cache.commands.VisitableCommand; import org.jboss.cache.factories.annotations.Inject; import org.jboss.cache.factories.annotations.Start; import org.jboss.cache.interceptors.base.CommandInterceptor; import org.jboss.cache.invocation.InvocationContextContainer; import org.jboss.cache.util.CachePrinter; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.ArrayList; /** * Knows how to build and manage an chain of interceptors. Also in charge with invoking methods on the chain. * * @author Mircea.Markus@jboss.com * @since 2.2 */ public class InterceptorChain { /** * reference to the first interceptor in the chain */ private CommandInterceptor firstInChain; /** * used for invoking commands on the chain */ private InvocationContextContainer invocationContextContainer; private static final Log log = LogFactory.getLog(InterceptorChain.class); /** * Constructs an interceptor chain having the supplied interceptor as first. */ public InterceptorChain(CommandInterceptor first) { this.firstInChain = first; } @Inject public void initialize(InvocationContextContainer invocationContextContainer) { this.invocationContextContainer = invocationContextContainer; } @Start private void printChainInfo() { if (log.isDebugEnabled()) log.debug("Interceptor chain is: " + toString()); } /** * Inserts the given interceptor at the specified position in the chain (o based indexing). * * @throws IllegalArgumentException if the position is invalid (e.g. 5 and there are only 2 interceptors in the chain) */ public synchronized void addInterceptor(CommandInterceptor interceptor, int position) { if (position == 0) { interceptor.setNext(firstInChain); firstInChain = interceptor; return; } if (firstInChain == null) return; CommandInterceptor it = firstInChain; int index = 0; while (it != null) { if (++index == position) { interceptor.setNext(it.getNext()); it.setNext(interceptor); return; } it = it.getNext(); } throw new IllegalArgumentException("Invalid index: " + index + " !"); } /** * Removes the interceptor at the given postion. * * @throws IllegalArgumentException if the position is invalid (e.g. 5 and there are only 2 interceptors in the chain) */ public synchronized void removeInterceptor(int position) { if (firstInChain == null) return; if (position == 0) { firstInChain = firstInChain.getNext(); return; } CommandInterceptor it = firstInChain; int index = 0; while (it != null) { if (++index == position) { if (it.getNext() == null) return; //nothing to remove it.setNext(it.getNext().getNext()); return; } it = it.getNext(); } throw new IllegalArgumentException("Invalid position: " + position + " !"); } /** * Returns the number of interceptors in the chain. */ public int size() { int size = 0; CommandInterceptor it = firstInChain; while (it != null) { size++; it = it.getNext(); } return size; } /** * Returns an unmofiable list with all the interceptors in sequence. * If first in chain is null an empty list is returned. */ public List asList() { if (firstInChain == null) return Collections.emptyList(); List retval = new LinkedList(); CommandInterceptor tmp = firstInChain; do { retval.add(tmp); tmp = tmp.getNext(); } while (tmp != null); return Collections.unmodifiableList(retval); } /** * Removes all the occurences of supplied interceptor type from the chain. */ public synchronized void removeInterceptor(Class clazz) { if (firstInChain.getClass() == clazz) { firstInChain = firstInChain.getNext(); } CommandInterceptor it = firstInChain.getNext(); CommandInterceptor prevIt = firstInChain; while (it != null) { if (it.getClass() == clazz) { prevIt.setNext(it.getNext()); } prevIt = it; it = it.getNext(); } } /** * Adds a new interceptor in list after an interceptor of a given type. * * @return true if the interceptor was added; i.e. the afterInterceptor exists */ public synchronized boolean addInterceptor(CommandInterceptor toAdd, Class afterInterceptor) { CommandInterceptor it = firstInChain; while (it != null) { if (it.getClass().equals(afterInterceptor)) { toAdd.setNext(it.getNext()); it.setNext(toAdd); return true; } it = it.getNext(); } return false; } /** * Appends at the end. */ public void appendIntereceptor(CommandInterceptor ci) { CommandInterceptor it = firstInChain; while (it.hasNext()) it = it.getNext(); it.setNext(ci); // make sure we nullify the "next" pointer in the last interceptor. ci.setNext(null); } /** * Walks the command through the interceptor chain. The received ctx is being passed in. */ @SuppressWarnings("deprecation") public Object invoke(InvocationContext ctx, VisitableCommand command) { ctx.setCommand(command); try { return command.acceptVisitor(ctx, firstInChain); } catch (CacheException e) { throw e; } catch (RuntimeException e) { throw e; } catch (Throwable t) { throw new CacheException(t); } } /** * Similar to {@link #invoke(org.jboss.cache.InvocationContext, org.jboss.cache.commands.VisitableCommand)}, but * constructs a invocation context on the fly, using {@link InvocationContextContainer#get()} */ public Object invokeRemote(VisitableCommand cacheCommand) throws Throwable { InvocationContext ctxt = invocationContextContainer.get(); ctxt.setOriginLocal(false); return cacheCommand.acceptVisitor(ctxt, firstInChain); } /** * Similar to {@link #invoke(org.jboss.cache.InvocationContext, org.jboss.cache.commands.VisitableCommand)}, but * constructs a invocation context on the fly, using {@link InvocationContextContainer#get()} and setting the origin local flag to it's default value. */ public Object invoke(VisitableCommand cacheCommand) throws Throwable { InvocationContext ctxt = invocationContextContainer.get(); return cacheCommand.acceptVisitor(ctxt, firstInChain); } /** * @return the first interceptor in the chain. */ public CommandInterceptor getFirstInChain() { return firstInChain; } /** * Mainly used by unit tests to replace the interceptor chain with the starting point passed in. * * @param interceptor interceptor to be used as the first interceptor in the chain. */ public void setFirstInChain(CommandInterceptor interceptor) { this.firstInChain = interceptor; } public InvocationContext getInvocationContext() { return invocationContextContainer.get(); } /** * Returns all interceptors which extend the given command interceptor. */ public List getInterceptorsWhichExtend(Class interceptorClass) { List result = new ArrayList(); for (CommandInterceptor interceptor : asList()) { boolean isSubclass = interceptorClass.isAssignableFrom(interceptor.getClass()); if (isSubclass) { result.add(interceptor); } } return result; } public String toString() { return "InterceptorChain{\n" + CachePrinter.printInterceptorChain(firstInChain) + "\n}"; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/interceptors/CacheMgmtInterceptor.java0000644000175000017500000001450211026046072032034 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * Copyright 2005, JBoss Inc., and individual contributors as indicated * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.cache.interceptors; import org.jboss.cache.DataContainer; import org.jboss.cache.InvocationContext; import org.jboss.cache.commands.read.GetKeyValueCommand; import org.jboss.cache.commands.write.EvictCommand; import org.jboss.cache.commands.write.PutDataMapCommand; import org.jboss.cache.commands.write.PutForExternalReadCommand; import org.jboss.cache.commands.write.PutKeyValueCommand; import org.jboss.cache.factories.annotations.Inject; import org.jboss.cache.interceptors.base.CommandInterceptor; import java.util.HashMap; import java.util.Map; /** * Captures cache management statistics * * @author Jerry Gauthier * @version $Id: CacheMgmtInterceptor.java 6000 2008-06-17 23:43:54Z manik.surtani@jboss.com $ */ public class CacheMgmtInterceptor extends CommandInterceptor implements CacheMgmtInterceptorMBean { private long m_hit_times = 0; private long m_miss_times = 0; private long m_store_times = 0; private long m_hits = 0; private long m_misses = 0; private long m_stores = 0; private long m_evictions = 0; private long m_start = System.currentTimeMillis(); private long m_reset = m_start; private DataContainer dataContainer; @Inject public void setDependencies(DataContainer dataContainer) { this.dataContainer = dataContainer; } @Override public Object visitEvictFqnCommand(InvocationContext ctx, EvictCommand command) throws Throwable { Object returnValue = invokeNextInterceptor(ctx, command); m_evictions++; return returnValue; } @Override public Object visitGetKeyValueCommand(InvocationContext ctx, GetKeyValueCommand command) throws Throwable { long t1 = System.currentTimeMillis(); Object retval = invokeNextInterceptor(ctx, command); long t2 = System.currentTimeMillis(); if (retval == null) { m_miss_times = m_miss_times + (t2 - t1); m_misses++; } else { m_hit_times = m_hit_times + (t2 - t1); m_hits++; } return retval; } @Override public Object visitPutDataMapCommand(InvocationContext ctx, PutDataMapCommand command) throws Throwable { Map data = command.getData(); long t1 = System.currentTimeMillis(); Object retval = invokeNextInterceptor(ctx, command); long t2 = System.currentTimeMillis(); if (data != null && data.size() > 0) { m_store_times = m_store_times + (t2 - t1); m_stores = m_stores + data.size(); } return retval; } @Override public Object visitPutForExternalReadCommand(InvocationContext ctx, PutForExternalReadCommand command) throws Throwable { return visitPutKeyValueCommand(ctx, command); } @Override public Object visitPutKeyValueCommand(InvocationContext ctx, PutKeyValueCommand command) throws Throwable { long t1 = System.currentTimeMillis(); Object retval = invokeNextInterceptor(ctx, command); long t2 = System.currentTimeMillis(); m_store_times = m_store_times + (t2 - t1); m_stores++; return retval; } public long getHits() { return m_hits; } public long getMisses() { return m_misses; } public long getStores() { return m_stores; } public long getEvictions() { return m_evictions; } public double getHitMissRatio() { double total = m_hits + m_misses; if (total == 0) return 0; return (m_hits / total); } public double getReadWriteRatio() { if (m_stores == 0) return 0; return (((double) (m_hits + m_misses) / (double) m_stores)); } public long getAverageReadTime() { long total = m_hits + m_misses; if (total == 0) return 0; return (m_hit_times + m_miss_times) / total; } public long getAverageWriteTime() { if (m_stores == 0) return 0; return (m_store_times) / m_stores; } public int getNumberOfAttributes() { return dataContainer.getNumberOfAttributes(); } public int getNumberOfNodes() { return dataContainer.getNumberOfNodes(); } public long getElapsedTime() { return (System.currentTimeMillis() - m_start) / 1000; } public long getTimeSinceReset() { return (System.currentTimeMillis() - m_reset) / 1000; } @Override public Map dumpStatistics() { Map retval = new HashMap(); retval.put("Hits", m_hits); retval.put("Misses", m_misses); retval.put("Stores", m_stores); retval.put("Evictions", m_evictions); retval.put("NumberOfAttributes", dataContainer.getNumberOfAttributes()); retval.put("NumberOfNodes", dataContainer.getNumberOfNodes()); retval.put("ElapsedTime", getElapsedTime()); retval.put("TimeSinceReset", getTimeSinceReset()); retval.put("AverageReadTime", getAverageReadTime()); retval.put("AverageWriteTime", getAverageWriteTime()); retval.put("HitMissRatio", getHitMissRatio()); retval.put("ReadWriteRatio", getReadWriteRatio()); return retval; } @Override public void resetStatistics() { m_hits = 0; m_misses = 0; m_stores = 0; m_evictions = 0; m_hit_times = 0; m_miss_times = 0; m_store_times = 0; m_reset = System.currentTimeMillis(); } } ././@LongLink0000000000000000000000000000014500000000000011565 Lustar rootrootjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/interceptors/CacheLoaderInterceptorMBean.javajbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/interceptors/CacheLoaderInterceptorMBean.java0000644000175000017500000000312610372207443033245 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * Copyright 2005, JBoss Inc., and individual contributors as indicated * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.cache.interceptors; /** * Interface capturing cache loader load statistics * @author Jerry Gauthier * @version $Id: CacheLoaderInterceptorMBean.java 1159 2006-02-07 21:20:35Z jerrygauth $ */ public interface CacheLoaderInterceptorMBean extends InterceptorMBean { /** * Returns the number of cache loader node loads * * @return the number of cache loader node loads */ long getCacheLoaderLoads(); /** * Returns the number of cache loader node misses * * @return the number of cache loader node misses */ long getCacheLoaderMisses(); } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/interceptors/InterceptorMBean.java0000644000175000017500000000403310477336565031206 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * Copyright 2005, JBoss Inc., and individual contributors as indicated * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.cache.interceptors; import java.util.Map; /** * Interface containing common cache management operations * * @author Jerry Gauthier * @version $Id: InterceptorMBean.java 2478 2006-09-05 18:09:57Z msurtani $ */ public interface InterceptorMBean { /** * Returns whether an interceptor's statistics are * being captured. * * @return true if statistics are captured */ boolean getStatisticsEnabled(); /** * Enables an interceptor's cache statistics * If true, the interceptor will capture statistics * and make them available through the mbean. * * @param enabled true if statistics should be captured */ void setStatisticsEnabled(boolean enabled); /** * Returns a map of the cache interceptor's statistics * Map is keyed on statistic names (which are Strings) and values are Objects. * * @return a map containing statistics */ Map dumpStatistics(); /** * Resets an interceptor's cache statistics */ void resetStatistics(); } ././@LongLink0000000000000000000000000000015000000000000011561 Lustar rootrootjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/interceptors/OptimisticValidatorInterceptor.javajbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/interceptors/OptimisticValidatorInterceptor.j0000755000175000017500000003152011026046072033510 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.interceptors; import org.jboss.cache.CacheException; import org.jboss.cache.DataContainer; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.NodeSPI; import org.jboss.cache.commands.tx.CommitCommand; import org.jboss.cache.commands.tx.OptimisticPrepareCommand; import org.jboss.cache.commands.tx.RollbackCommand; import static org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.factories.annotations.Inject; import org.jboss.cache.optimistic.DataVersioningException; import org.jboss.cache.optimistic.DefaultDataVersion; import org.jboss.cache.optimistic.TransactionWorkspace; import org.jboss.cache.optimistic.WorkspaceNode; import org.jboss.cache.transaction.GlobalTransaction; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; /** * Validates the data in the {@link TransactionWorkspace} against data in the underlying data structure * (versions only) and then applies changes to the underlying data structure. This is only triggered when commit, * rollback or prepare method calls are encountered. Other method calls are directly passed up the interceptor chain, * untouched. Note that prepare/commit/rollbacks are not passed up the interceptor chain after being processed. *

* When preparting, this interceptor does nothing more than validate versions. * The validation scheme used is based on the {@link org.jboss.cache.optimistic.DataVersion} implementation used. * {@link org.jboss.cache.optimistic.DataVersion#newerThan(org.jboss.cache.optimistic.DataVersion)} is used to determine * whether the version of one instance is newer than the version of another. It is up to the {@link org.jboss.cache.optimistic.DataVersion} * implementation to deal with attempting to compare incompatible version types (and potentially throwing {@link org.jboss.cache.optimistic.DataVersioningException}s. *

* Upon successful commit, changes in the workspace are applied to the underlying data structure in the cache. *

* On rollback clears the nodes in the workspace and leaves the underlying data structure untouched. * * @author Manik Surtani (manik@jboss.org) * @author Steve Woodcock (stevew@jofti.com) */ public class OptimisticValidatorInterceptor extends OptimisticInterceptor { private boolean useTombstones; private DataContainer dataContainer; @Inject public void initialize(DataContainer dataContainer) { this.dataContainer = dataContainer; } @Inject private void init() { CacheMode mode = configuration.getCacheMode(); useTombstones = (mode == CacheMode.INVALIDATION_ASYNC) || (mode == CacheMode.INVALIDATION_SYNC); } @Override public Object visitOptimisticPrepareCommand(InvocationContext ctx, OptimisticPrepareCommand command) throws Throwable { TransactionWorkspace workspace = getTransactionWorkspace(ctx); // There is no guarantee that this collection is in any order! Collection nodes = workspace.getNodes().values(); //we ought to have all necessary locks here so lets try and validate if (log.isDebugEnabled()) log.debug("Validating " + nodes.size() + " nodes."); for (WorkspaceNode workspaceNode : nodes) { if (workspaceNode.isDirty()) { Fqn fqn = workspaceNode.getFqn(); if (trace) log.trace("Validating version for node [" + fqn + "]"); NodeSPI underlyingNode; underlyingNode = dataContainer.peek(fqn, true, true); // if this is a newly created node then we expect the underlying node to be null. // also, if the node has been deleted in the WS and the underlying node is null, this *may* be ok ... will test again later when comparing versions // if not, we have a problem... if (underlyingNode == null && !workspaceNode.isCreated() && !workspaceNode.isDeleted()) { throw new DataVersioningException("Underlying node for " + fqn + " is null, and this node wasn't newly created in this transaction! We have a concurrent deletion event."); } // needs to have been created AND modified - we allow concurrent creation if no data is put into the node if (underlyingNode != null && underlyingNode.isValid() && workspaceNode.isCreated() && workspaceNode.isModified()) { throw new DataVersioningException("Transaction attempted to create " + fqn + " anew. It has already been created since this transaction started, by another (possibly remote) transaction. We have a concurrent creation event."); } if (underlyingNode != null && !underlyingNode.isValid()) { // we havea tombstone if (!workspaceNode.isCreated() && !workspaceNode.isDeleted()) throw new DataVersioningException("Underlying node doesn't exist but a tombstone does; workspace node should be marked as created!"); if (underlyingNode.getVersion().newerThan(workspaceNode.getVersion())) { // we have an out of date node here throw new DataVersioningException("Version mismatch for node " + fqn + ": underlying node with version " + workspaceNode.getNode().getVersion() + " is newer than workspace node, with version " + workspaceNode.getVersion()); } } if (!workspaceNode.isCreated() && (workspaceNode.isDeleted() || workspaceNode.isModified())) { // if the real node is null, throw a DVE if (underlyingNode == null) { // but not if the WSN has also been deleted if (!workspaceNode.isDeleted()) throw new DataVersioningException("Unable to compare versions since the underlying node has been deleted by a concurrent transaction!"); else if (trace) log.trace("The data node [" + fqn + "] is null, but this is ok since the workspace node is marked as deleted as well"); } // if there is a DataVersion type mismatch here, leave it up to the DataVersion impl to barf if necessary. - JBCACHE-962 else if (underlyingNode.getVersion().newerThan(workspaceNode.getVersion())) { // we have an out of date node here throw new DataVersioningException("Version mismatch for node " + fqn + ": underlying node with version " + workspaceNode.getNode().getVersion() + " is newer than workspace node, with version " + workspaceNode.getVersion()); } } } else { if (trace) log.trace("Node [" + workspaceNode.getFqn() + "] doesn't need validating as it isn't dirty"); } } log.debug("Successfully validated nodes"); return invokeNextInterceptor(ctx, command); } private void commitTransaction(InvocationContext ctx) { GlobalTransaction gtx = getGlobalTransaction(ctx); TransactionWorkspace workspace; workspace = getTransactionWorkspace(ctx); if (log.isDebugEnabled()) log.debug("Commiting successfully validated changes for GlobalTransaction " + gtx); Collection workspaceNodes = workspace.getNodes().values(); for (WorkspaceNode workspaceNode : workspaceNodes) { NodeSPI underlyingNode = workspaceNode.getNode(); // short circuit if this node is deleted? if (workspaceNode.isDeleted()) { if (trace) log.trace("Workspace node " + workspaceNode.getFqn() + " deleted; removing"); if (underlyingNode.getFqn().isRoot()) { throw new CacheException("An illegal attempt to delete the root node!"); } else { // mark it as invalid so any direct references are marked as such underlyingNode.setValid(false, true); // we need to update versions here, too performVersionUpdate(underlyingNode, workspaceNode); if (!useTombstones) { // don't retain the tombstone NodeSPI parent = underlyingNode.getParent(); if (parent == null) { throw new CacheException("Underlying node " + underlyingNode + " has no parent"); } parent.removeChildDirect(underlyingNode.getFqn().getLastElement()); } } } else { boolean updateVersion = false; if (workspaceNode.isChildrenModified() || workspaceNode.isResurrected()) // if it is newly created make sure we remove all underlying children that may exist, to solve a remove-and-create-in-tx condition { if (trace) log.trace("Updating children since node has modified children"); // merge children. List> deltas = workspaceNode.getMergedChildren(); if (trace) log.trace("Applying children deltas to parent node " + underlyingNode.getFqn()); if (workspaceNode.isResurrected()) { // mark it as invalid so any direct references are marked as such Map childNode = underlyingNode.getChildrenMapDirect(); if (childNode != null) { for (Object o : childNode.values()) { NodeSPI cn = (NodeSPI) o; cn.setValid(false, true); if (!useTombstones) underlyingNode.removeChildDirect(cn.getFqn().getLastElement()); } } } for (Fqn child : deltas.get(0)) { NodeSPI childNode = workspace.getNode(child).getNode(); underlyingNode.addChildDirect(childNode); childNode.setValid(true, false); } for (Fqn child : deltas.get(1)) { // mark it as invalid so any direct references are marked as such NodeSPI childNode = underlyingNode.getChildDirect(child.getLastElement()); if (childNode != null) childNode.setValid(false, true); if (!useTombstones) underlyingNode.removeChildDirect(child.getLastElement()); } updateVersion = underlyingNode.isLockForChildInsertRemove(); // do we need to notify listeners of a modification?? If all we've done is added children then don't // notify. } if (workspaceNode.isModified()) { if (trace) log.trace("Merging data since node is dirty"); Map mergedData = workspaceNode.getMergedData(); underlyingNode.clearDataDirect(); underlyingNode.putAllDirect(mergedData); // mark node and any parents as valid- if available. Versioning parents are tough though - leave as old versions? validateNodeAndParents(underlyingNode); updateVersion = true; } if (updateVersion) { performVersionUpdate(underlyingNode, workspaceNode); } } } } @Override public Object visitCommitCommand(InvocationContext ctx, CommitCommand command) throws Throwable { commitTransaction(ctx); return invokeNextInterceptor(ctx, command); } @Override public Object visitRollbackCommand(InvocationContext ctx, RollbackCommand command) throws Throwable { TransactionWorkspace workspace; workspace = getTransactionWorkspace(ctx); workspace.clearNodes(); return invokeNextInterceptor(ctx, command); } private void validateNodeAndParents(NodeSPI node) { node.setValid(true, false); if (!node.getFqn().isRoot()) validateNodeAndParents(node.getParent()); } private void performVersionUpdate(NodeSPI underlyingNode, WorkspaceNode workspaceNode) { if (workspaceNode.isVersioningImplicit()) { if (trace) log.trace("Versioning is implicit; incrementing."); underlyingNode.setVersion(((DefaultDataVersion) workspaceNode.getVersion()).increment()); } else { if (trace) log.trace("Versioning is explicit; not attempting an increment."); underlyingNode.setVersion(workspaceNode.getVersion()); } if (trace) log.trace("Setting version of node " + underlyingNode.getFqn() + " from " + workspaceNode.getVersion() + " to " + underlyingNode.getVersion()); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/interceptors/OptimisticTxInterceptor.java0000644000175000017500000001605511015052412032642 0ustar twernertwernerpackage org.jboss.cache.interceptors; import org.jboss.cache.InvocationContext; import org.jboss.cache.commands.AbstractVisitor; import org.jboss.cache.commands.VersionedDataCommand; import org.jboss.cache.commands.VisitableCommand; import org.jboss.cache.commands.tx.OptimisticPrepareCommand; import org.jboss.cache.commands.tx.PrepareCommand; import org.jboss.cache.commands.write.ClearDataCommand; import org.jboss.cache.commands.write.PutDataMapCommand; import org.jboss.cache.commands.write.PutKeyValueCommand; import org.jboss.cache.commands.write.RemoveKeyCommand; import org.jboss.cache.commands.write.RemoveNodeCommand; import org.jboss.cache.config.Option; import org.jboss.cache.transaction.GlobalTransaction; import org.jboss.cache.transaction.OptimisticTransactionEntry; import org.jboss.cache.transaction.TransactionEntry; import javax.transaction.Transaction; import java.util.List; /** * A new interceptor to simplify functionality in the {@link org.jboss.cache.interceptors.TxInterceptor}. * * @author Manik Surtani (manik@jboss.org) * @since 2.2.0 */ public class OptimisticTxInterceptor extends TxInterceptor { protected final ModificationsReplayVisitor replayVisitor = new ModificationsReplayVisitor(); public OptimisticTxInterceptor() { optimistic = true; } @Override public Object visitOptimisticPrepareCommand(InvocationContext ctx, OptimisticPrepareCommand command) throws Throwable { // nothing really different from a pessimistic prepare command. return visitPrepareCommand(ctx, command); } @Override public Object handleDefault(InvocationContext ctx, VisitableCommand command) throws Throwable { try { Transaction tx = ctx.getTransaction(); boolean implicitTransaction = tx == null; if (implicitTransaction) { tx = createLocalTx(); // we need to attach this tx to the InvocationContext. ctx.setTransaction(tx); } try { Object retval = attachGtxAndPassUpChain(ctx, command); if (implicitTransaction) { copyInvocationScopeOptionsToTxScope(ctx); copyForcedCacheModeToTxScope(ctx); txManager.commit(); } return retval; } catch (Throwable t) { if (implicitTransaction) { log.warn("Rolling back, exception encountered", t); try { copyInvocationScopeOptionsToTxScope(ctx); copyForcedCacheModeToTxScope(ctx); txManager.rollback(); } catch (Throwable th) { log.warn("Roll back failed encountered", th); } throw t; } } } catch (Throwable th) { ctx.throwIfNeeded(th); } return null; } private void copyForcedCacheModeToTxScope(InvocationContext ctx) { Option optionOverride = ctx.getOptionOverrides(); if (optionOverride != null && (optionOverride.isForceAsynchronous() || optionOverride.isForceSynchronous())) { TransactionEntry entry = ctx.getTransactionEntry(); if (entry != null) { if (optionOverride.isForceAsynchronous()) entry.setForceAsyncReplication(true); else entry.setForceSyncReplication(true); } } } @Override protected PrepareCommand buildPrepareCommand(GlobalTransaction gtx, List modifications, boolean onePhaseCommit) { // optimistic locking NEVER does one-phase prepares. return commandsFactory.buildOptimisticPrepareCommand(gtx, modifications, null, rpcManager.getLocalAddress(), false); } /** * Replays modifications by passing them up the interceptor chain. * * @throws Throwable */ @Override protected void replayModifications(InvocationContext ctx, Transaction ltx, PrepareCommand command) throws Throwable { if (log.isDebugEnabled()) log.debug("Handling optimistic remote prepare " + ctx.getGlobalTransaction()); // invoke all modifications by passing them up the chain, setting data versions first. try { replayVisitor.visitCollection(ctx, command.getModifications()); } catch (Throwable t) { log.error("Prepare failed!", t); throw t; } } @Override protected void cleanupStaleLocks(InvocationContext ctx) throws Throwable { super.cleanupStaleLocks(ctx); TransactionEntry entry = ctx.getTransactionEntry(); if (entry != null) { ((OptimisticTransactionEntry) entry).getTransactionWorkSpace().clearNodes(); } } @Override protected TransactionEntry createNewTransactionEntry(Transaction tx) throws Exception { return new OptimisticTransactionEntry(tx); } private class ModificationsReplayVisitor extends AbstractVisitor { @Override public Object handleDefault(InvocationContext ctx, VisitableCommand command) throws Throwable { Object result = invokeNextInterceptor(ctx, command); assertTxIsStillValid(ctx.getTransaction()); return result; } @Override public Object visitPutDataMapCommand(InvocationContext ctx, PutDataMapCommand command) throws Throwable { return handleDataVersionCommand(ctx, command); } @Override public Object visitPutKeyValueCommand(InvocationContext ctx, PutKeyValueCommand command) throws Throwable { return handleDataVersionCommand(ctx, command); } @Override public Object visitRemoveNodeCommand(InvocationContext ctx, RemoveNodeCommand command) throws Throwable { return handleDataVersionCommand(ctx, command); } @Override public Object visitClearDataCommand(InvocationContext ctx, ClearDataCommand command) throws Throwable { return handleDataVersionCommand(ctx, command); } @Override public Object visitRemoveKeyCommand(InvocationContext ctx, RemoveKeyCommand command) throws Throwable { return handleDataVersionCommand(ctx, command); } private Object handleDataVersionCommand(InvocationContext ctx, VersionedDataCommand command) throws Throwable { Option originalOption = ctx.getOptionOverrides(); if (command.isVersioned()) { Option option = new Option(); option.setDataVersion(command.getDataVersion()); ctx.setOptionOverrides(option); } Object retval; try { retval = invokeNextInterceptor(ctx, command); assertTxIsStillValid(ctx.getTransaction()); } catch (Throwable t) { log.error("method invocation failed", t); throw t; } finally { ctx.setOptionOverrides(originalOption); } return retval; } } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/interceptors/CacheStoreInterceptorMBean.java0000644000175000017500000000264510370672716033147 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * Copyright 2005, JBoss Inc., and individual contributors as indicated * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.cache.interceptors; /** * Interface capturing cache loader statistics * @author Jerry Gauthier * @version $Id: CacheStoreInterceptorMBean.java 1120 2006-02-03 15:25:02Z jerrygauth $ */ public interface CacheStoreInterceptorMBean extends InterceptorMBean { /** * Returns the number of cache loader stores * * @return the number of cache loader stores */ long getCacheLoaderStores(); } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/interceptors/MarshalledValueInterceptor.java0000644000175000017500000001647111032145203033255 0ustar twernertwernerpackage org.jboss.cache.interceptors; import org.jboss.cache.InvocationContext; import org.jboss.cache.commands.read.GetChildrenNamesCommand; import org.jboss.cache.commands.read.GetDataMapCommand; import org.jboss.cache.commands.read.GetKeyValueCommand; import org.jboss.cache.commands.read.GetKeysCommand; import org.jboss.cache.commands.read.GetNodeCommand; import org.jboss.cache.commands.write.ClearDataCommand; import org.jboss.cache.commands.write.PutDataMapCommand; import org.jboss.cache.commands.write.PutForExternalReadCommand; import org.jboss.cache.commands.write.PutKeyValueCommand; import org.jboss.cache.commands.write.RemoveKeyCommand; import org.jboss.cache.interceptors.base.CommandInterceptor; import org.jboss.cache.marshall.MarshalledValue; import org.jboss.cache.marshall.MarshalledValueHelper; import org.jboss.cache.marshall.MarshalledValueMap; import java.io.IOException; import java.io.NotSerializableException; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; /** * Interceptor that handles the wrapping and unwrapping of cached data using {@link org.jboss.cache.marshall.MarshalledValue}s. * Known "excluded" types are not wrapped/unwrapped, which at this time include {@link String}, Java primitives * and their Object wrappers, as well as arrays of excluded types. *

* The {@link org.jboss.cache.marshall.MarshalledValue} wrapper handles lazy deserialization from byte array representations. * * @author Manik Surtani (manik@jboss.org) * @see org.jboss.cache.marshall.MarshalledValue * @since 2.1.0 */ public class MarshalledValueInterceptor extends CommandInterceptor { @Override public Object visitPutDataMapCommand(InvocationContext ctx, PutDataMapCommand command) throws Throwable { if (command.getDataVersion() != null) { return invokeNextInterceptor(ctx, command); } Set marshalledValues = new HashSet(); command.setData(wrapMap(command.getData(), marshalledValues, ctx)); Object retVal = invokeNextInterceptor(ctx, command); return compactAndProcessRetVal(marshalledValues, retVal); } @Override public Object visitGetDataMapCommand(InvocationContext ctx, GetDataMapCommand command) throws Throwable { Object retVal = invokeNextInterceptor(ctx, command); if (retVal instanceof Map) { if (trace) log.trace("Return value is a Map and we're retrieving data. Wrapping as a MarshalledValueMap."); Map retValMap = (Map) retVal; if (!retValMap.isEmpty()) retVal = new MarshalledValueMap(retValMap); } return retVal; } @Override public Object visitPutForExternalReadCommand(InvocationContext ctx, PutForExternalReadCommand command) throws Throwable { return visitPutKeyValueCommand(ctx, command); } @Override public Object visitPutKeyValueCommand(InvocationContext ctx, PutKeyValueCommand command) throws Throwable { Set marshalledValues = new HashSet(); if (!MarshalledValueHelper.isTypeExcluded(command.getKey().getClass())) { Object newKey = createAndAddMarshalledValue(command.getKey(), marshalledValues, ctx); command.setKey(newKey); } if (!MarshalledValueHelper.isTypeExcluded(command.getValue().getClass())) { Object value = createAndAddMarshalledValue(command.getValue(), marshalledValues, ctx); command.setValue(value); } Object retVal = invokeNextInterceptor(ctx, command); return compactAndProcessRetVal(marshalledValues, retVal); } @Override public Object visitGetNodeCommand(InvocationContext ctx, GetNodeCommand command) throws Throwable { Object retVal = invokeNextInterceptor(ctx, command); return processRetVal(retVal); } @Override public Object visitClearDataCommand(InvocationContext ctx, ClearDataCommand command) throws Throwable { Object retVal = invokeNextInterceptor(ctx, command); return processRetVal(retVal); } @Override public Object visitRemoveKeyCommand(InvocationContext ctx, RemoveKeyCommand command) throws Throwable { Set marshalledValues = new HashSet(); if (!MarshalledValueHelper.isTypeExcluded(command.getKey().getClass())) { Object value = createAndAddMarshalledValue(command.getKey(), marshalledValues, ctx); command.setKey(value); } Object retVal = invokeNextInterceptor(ctx, command); return compactAndProcessRetVal(marshalledValues, retVal); } @Override public Object visitGetChildrenNamesCommand(InvocationContext ctx, GetChildrenNamesCommand command) throws Throwable { Object retVal = invokeNextInterceptor(ctx, command); return processRetVal(retVal); } @Override public Object visitGetKeysCommand(InvocationContext ctx, GetKeysCommand command) throws Throwable { Object retVal = invokeNextInterceptor(ctx, command); return processRetVal(retVal); } @Override public Object visitGetKeyValueCommand(InvocationContext ctx, GetKeyValueCommand command) throws Throwable { Set marshalledValues = new HashSet(); if (!MarshalledValueHelper.isTypeExcluded(command.getKey().getClass())) { Object value = createAndAddMarshalledValue(command.getKey(), marshalledValues, ctx); command.setKey(value); } Object retVal = invokeNextInterceptor(ctx, command); return compactAndProcessRetVal(marshalledValues, retVal); } private Object compactAndProcessRetVal(Set marshalledValues, Object retVal) throws IOException, ClassNotFoundException { if (trace) log.trace("Compacting MarshalledValues created"); for (MarshalledValue mv : marshalledValues) mv.compact(false, false); return processRetVal(retVal); } private Object processRetVal(Object retVal) throws IOException, ClassNotFoundException { if (retVal instanceof MarshalledValue) { if (trace) log.trace("Return value is a MarshalledValue. Unwrapping."); retVal = ((MarshalledValue) retVal).get(); } return retVal; } @SuppressWarnings("unchecked") protected Map wrapMap(Map m, Set marshalledValues, InvocationContext ctx) throws NotSerializableException { if (trace) log.trace("Wrapping map contents of argument " + m); Map copy = new HashMap(); for (Map.Entry me : m.entrySet()) { Object key = me.getKey(); Object value = me.getValue(); copy.put((key == null || MarshalledValueHelper.isTypeExcluded(key.getClass())) ? key : createAndAddMarshalledValue(key, marshalledValues, ctx), (value == null || MarshalledValueHelper.isTypeExcluded(value.getClass())) ? value : createAndAddMarshalledValue(value, marshalledValues, ctx)); } return copy; } protected MarshalledValue createAndAddMarshalledValue(Object toWrap, Set marshalledValues, InvocationContext ctx) throws NotSerializableException { MarshalledValue mv = new MarshalledValue(toWrap); marshalledValues.add(mv); if (!ctx.isOriginLocal()) mv.setEqualityPreferenceForInstance(false); return mv; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/interceptors/NotificationInterceptor.java0000644000175000017500000000307711026046072032637 0ustar twernertwernerpackage org.jboss.cache.interceptors; import org.jboss.cache.InvocationContext; import org.jboss.cache.commands.tx.CommitCommand; import org.jboss.cache.commands.tx.PrepareCommand; import org.jboss.cache.commands.tx.RollbackCommand; import org.jboss.cache.factories.annotations.Inject; import org.jboss.cache.notifications.Notifier; /** * The interceptor in charge of firing off notifications to cache listeners * * @author Manik Surtani * @since 2.0.0 */ public class NotificationInterceptor extends BaseTransactionalContextInterceptor { private Notifier notifier; @Inject public void injectDependencies(Notifier notifier) { this.notifier = notifier; } @Override public Object visitPrepareCommand(InvocationContext ctx, PrepareCommand command) throws Throwable { Object retval = invokeNextInterceptor(ctx, command); if (command.isOnePhaseCommit()) notifier.notifyTransactionCompleted(ctx.getTransaction(), true, ctx); return retval; } @Override public Object visitCommitCommand(InvocationContext ctx, CommitCommand command) throws Throwable { Object retval = invokeNextInterceptor(ctx, command); notifier.notifyTransactionCompleted(ctx.getTransaction(), true, ctx); return retval; } @Override public Object visitRollbackCommand(InvocationContext ctx, RollbackCommand command) throws Throwable { Object retval = invokeNextInterceptor(ctx, command); notifier.notifyTransactionCompleted(ctx.getTransaction(), false, ctx); return retval; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/interceptors/ReplicationInterceptor.java0000755000175000017500000001555111017455042032466 0ustar twernertwernerpackage org.jboss.cache.interceptors; import org.jboss.cache.InvocationContext; import org.jboss.cache.commands.ReversibleCommand; import org.jboss.cache.commands.VisitableCommand; import org.jboss.cache.commands.tx.CommitCommand; import org.jboss.cache.commands.tx.PrepareCommand; import org.jboss.cache.commands.tx.RollbackCommand; import org.jboss.cache.commands.write.MoveCommand; import org.jboss.cache.commands.write.PutDataMapCommand; import org.jboss.cache.commands.write.PutForExternalReadCommand; import org.jboss.cache.commands.write.PutKeyValueCommand; import org.jboss.cache.commands.write.ClearDataCommand; import org.jboss.cache.commands.write.RemoveKeyCommand; import org.jboss.cache.commands.write.RemoveNodeCommand; import org.jboss.cache.config.Configuration; import org.jboss.cache.transaction.GlobalTransaction; import org.jboss.cache.transaction.TransactionEntry; /** * Takes care of replicating modifications to other nodes in a cluster. Also * listens for prepare(), commit() and rollback() messages which are received * 'side-ways' (see docs/design/Refactoring.txt). * * @author Bela Ban * @version $Id: ReplicationInterceptor.java 5906 2008-05-29 07:24:18Z mircea.markus $ */ public class ReplicationInterceptor extends BaseRpcInterceptor { @Override public Object visitCommitCommand(InvocationContext ctx, CommitCommand command) throws Throwable { if (!skipReplicationOfTransactionMethod(ctx)) replicateCall(ctx, command, configuration.isSyncCommitPhase(), ctx.getOptionOverrides(), true); return invokeNextInterceptor(ctx, command); } @Override public Object visitPrepareCommand(InvocationContext ctx, PrepareCommand command) throws Throwable { Object retVal = invokeNextInterceptor(ctx, command); TransactionEntry te = ctx.getTransactionEntry(); if (te.hasLocalModifications()) { PrepareCommand replicablePrepareCommand = command.clone(); // makre sure we remove any "local" transactions replicablePrepareCommand.removeModifications(te.getLocalModifications()); command = replicablePrepareCommand; } if (!skipReplicationOfTransactionMethod(ctx)) runPreparePhase(command, command.getGlobalTransaction(), ctx); return retVal; } @Override public Object visitRollbackCommand(InvocationContext ctx, RollbackCommand command) throws Throwable { if (!skipReplicationOfTransactionMethod(ctx) && !ctx.isLocalRollbackOnly()) { replicateCall(ctx, command, configuration.isSyncRollbackPhase(), ctx.getOptionOverrides()); } return invokeNextInterceptor(ctx, command); } @Override public Object visitPutForExternalReadCommand(InvocationContext ctx, PutForExternalReadCommand command) throws Throwable { boolean local = isLocalModeForced(ctx); if (local && ctx.getTransaction() == null) return invokeNextInterceptor(ctx, command); if (isTransactionalAndLocal(ctx)) { Object returnValue = invokeNextInterceptor(ctx, command); ctx.getTransactionEntry().setForceAsyncReplication(true); if (local) ctx.getTransactionEntry().addLocalModification(command); return returnValue; } else { return handleCrudMethod(ctx, command, true); } } @Override public Object visitPutKeyValueCommand(InvocationContext ctx, PutKeyValueCommand command) throws Throwable { return handleCrudMethod(ctx, command, false); } @Override public Object visitPutDataMapCommand(InvocationContext ctx, PutDataMapCommand command) throws Throwable { return handleCrudMethod(ctx, command, false); } @Override public Object visitMoveCommand(InvocationContext ctx, MoveCommand command) throws Throwable { return handleCrudMethod(ctx, command, false); } @Override public Object visitRemoveNodeCommand(InvocationContext ctx, RemoveNodeCommand command) throws Throwable { return handleCrudMethod(ctx, command, false); } @Override public Object visitRemoveKeyCommand(InvocationContext ctx, RemoveKeyCommand command) throws Throwable { return handleCrudMethod(ctx, command, false); } @Override public Object visitClearDataCommand(InvocationContext ctx, ClearDataCommand command) throws Throwable { return handleCrudMethod(ctx, command, false); } /** * If we are within one transaction we won't do any replication as replication would only be performed at commit time. * If the operation didn't originate locally we won't do any replication either. */ private Object handleCrudMethod(InvocationContext ctx, VisitableCommand command, boolean forceAsync) throws Throwable { boolean local = isLocalModeForced(ctx); if (local && ctx.getTransaction() == null) return invokeNextInterceptor(ctx, command); // FIRST pass this call up the chain. Only if it succeeds (no exceptions) locally do we attempt to replicate. Object returnValue = invokeNextInterceptor(ctx, command); if (ctx.getTransaction() == null && ctx.isOriginLocal()) { if (trace) { log.trace("invoking method " + command.getClass().getSimpleName() + ", members=" + rpcManager.getMembers() + ", mode=" + configuration.getCacheMode() + ", exclude_self=" + true + ", timeout=" + configuration.getSyncReplTimeout()); } replicateCall(ctx, command, !forceAsync && isSynchronous(ctx.getOptionOverrides()), ctx.getOptionOverrides()); } else { if (local) ctx.getTransactionEntry().addLocalModification((ReversibleCommand) command); } return returnValue; } /** * Calls prepare(GlobalTransaction,List,org.jgroups.Address,boolean)) in all members except self. * Waits for all responses. If one of the members failed to prepare, its return value * will be an exception. If there is one exception we rethrow it. This will mark the * current transaction as rolled back, which will cause the * afterCompletion(int) callback to have a status * of MARKED_ROLLBACK. When we get that call, we simply roll back the * transaction.
* If everything runs okay, the afterCompletion(int) * callback will trigger the @link #runCommitPhase(GlobalTransaction)). *
* * @throws Exception */ protected void runPreparePhase(PrepareCommand prepareMethod, GlobalTransaction gtx, InvocationContext ctx) throws Throwable { boolean async = configuration.getCacheMode() == Configuration.CacheMode.REPL_ASYNC; if (trace) { log.trace("(" + rpcManager.getLocalAddress() + "): running remote prepare for global tx " + gtx + " with async mode=" + async); } // this method will return immediately if we're the only member (because exclude_self=true) replicateCall(ctx, prepareMethod, !async, ctx.getOptionOverrides()); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/interceptors/OptimisticNodeInterceptor.java0000755000175000017500000005505511032145203033142 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.interceptors; import org.apache.commons.logging.LogFactory; import org.jboss.cache.CacheException; import org.jboss.cache.DataContainer; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.NodeFactory; import org.jboss.cache.NodeNotExistsException; import org.jboss.cache.NodeSPI; import org.jboss.cache.commands.ReversibleCommand; import org.jboss.cache.commands.read.GetChildrenNamesCommand; import org.jboss.cache.commands.read.GetDataMapCommand; import org.jboss.cache.commands.read.GetKeyValueCommand; import org.jboss.cache.commands.read.GetKeysCommand; import org.jboss.cache.commands.read.GetNodeCommand; import org.jboss.cache.commands.write.ClearDataCommand; import org.jboss.cache.commands.write.MoveCommand; import org.jboss.cache.commands.write.PutDataMapCommand; import org.jboss.cache.commands.write.PutForExternalReadCommand; import org.jboss.cache.commands.write.PutKeyValueCommand; import org.jboss.cache.commands.write.RemoveKeyCommand; import org.jboss.cache.commands.write.RemoveNodeCommand; import org.jboss.cache.factories.annotations.Inject; import org.jboss.cache.factories.annotations.Start; import org.jboss.cache.notifications.Notifier; import static org.jboss.cache.notifications.event.NodeModifiedEvent.ModificationType.*; import org.jboss.cache.optimistic.DataVersion; import org.jboss.cache.optimistic.DefaultDataVersion; import org.jboss.cache.optimistic.TransactionWorkspace; import org.jboss.cache.optimistic.WorkspaceNode; import org.jboss.cache.transaction.GlobalTransaction; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.SortedMap; /** * Operations on nodes are done on the copies that exist in the workspace rather than passed down * to the {@link org.jboss.cache.interceptors.CallInterceptor}. These operations happen in this interceptor. * * @author Manik Surtani (manik@jboss.org) * @author Steve Woodcock (stevew@jofti.com) */ public class OptimisticNodeInterceptor extends OptimisticInterceptor { /** * Needed for the creation of workspace nodes based on underlying nodes in the cache. */ private NodeFactory nodeFactory; private Notifier notifier; private DataContainer dataContainer; private long lockAcquisitionTimeout; @Inject protected void injectDependencies(Notifier notifier, NodeFactory nodeFactory, DataContainer dataContainer) { this.notifier = notifier; this.nodeFactory = nodeFactory; this.dataContainer = dataContainer; } @Start private void init() { lockAcquisitionTimeout = configuration.getLockAcquisitionTimeout(); } public OptimisticNodeInterceptor() { log = LogFactory.getLog(getClass()); trace = log.isTraceEnabled(); } @Override public Object visitRemoveNodeCommand(InvocationContext ctx, RemoveNodeCommand command) throws Throwable { TransactionWorkspace workspace = getTransactionWorkspace(ctx); WorkspaceNode workspaceNode = fetchWorkspaceNode(ctx, command.getFqn(), workspace, false, true); if (workspaceNode != null) { setVersioning(ctx, workspace, workspaceNode); } Object result = removeNode(workspace, workspaceNode, true, ctx); addToModificationList(command, ctx); return result; } @Override public Object visitPutForExternalReadCommand(InvocationContext ctx, PutForExternalReadCommand command) throws Throwable { return visitPutKeyValueCommand(ctx, command); } @Override public Object visitPutKeyValueCommand(InvocationContext ctx, PutKeyValueCommand command) throws Throwable { TransactionWorkspace workspace = getTransactionWorkspace(ctx); WorkspaceNode workspaceNode = fetchWorkspaceNode(ctx, command.getFqn(), workspace, true, true); if (workspaceNode != null) { setVersioning(ctx, workspace, workspaceNode); } else { // "fail-more-silently" patch thanks to Owen Taylor - JBCACHE-767 if ((ctx.getOptionOverrides() == null || !ctx.getOptionOverrides().isFailSilently())) { throw new CacheException("Unable to set node version for " + command.getFqn() + ", node is null."); } } Object result = putDataKeyValueAndNotify(command.getKey(), command.getValue(), workspace, workspaceNode, ctx); addToModificationList(command, ctx); return result; } @Override public Object visitPutDataMapCommand(InvocationContext ctx, PutDataMapCommand command) throws Throwable { TransactionWorkspace workspace = getTransactionWorkspace(ctx); WorkspaceNode workspaceNode = fetchWorkspaceNode(ctx, command.getFqn(), workspace, true, true); if (workspaceNode != null) { setVersioning(ctx, workspace, workspaceNode); } else { // "fail-more-silently" patch thanks to Owen Taylor - JBCACHE-767 if ((ctx.getOptionOverrides() == null || !ctx.getOptionOverrides().isFailSilently())) { throw new CacheException("Unable to set node version for " + command.getFqn() + ", node is null."); } } putDataMapAndNotify(command.getData(), workspace, workspaceNode, ctx); addToModificationList(command, ctx); return null; } @Override public Object visitMoveCommand(InvocationContext ctx, MoveCommand command) throws Throwable { TransactionWorkspace workspace = getTransactionWorkspace(ctx); WorkspaceNode workspaceNode = fetchWorkspaceNode(ctx, command.getFqn(), workspace, true, true); if (ctx.isOriginLocal() && ctx.getOptionOverrides() != null && ctx.getOptionOverrides().getDataVersion() != null) { throw new CacheException("Setting a data version while performing a move() is not supported!!"); } if (workspaceNode != null) { setVersioning(ctx, workspace, workspaceNode); } moveNodeAndNotify(command.getTo(), workspaceNode, workspace, ctx); addToModificationList(command, ctx); return null; } @Override public Object visitRemoveKeyCommand(InvocationContext ctx, RemoveKeyCommand command) throws Throwable { TransactionWorkspace workspace = getTransactionWorkspace(ctx); WorkspaceNode workspaceNode = fetchWorkspaceNode(ctx, command.getFqn(), workspace, true, true); if (workspaceNode != null) { setVersioning(ctx, workspace, workspaceNode); } Object result = removeKeyAndNotify(command.getKey(), workspace, workspaceNode, ctx); addToModificationList(command, ctx); return result; } @Override public Object visitClearDataCommand(InvocationContext ctx, ClearDataCommand command) throws Throwable { TransactionWorkspace workspace = getTransactionWorkspace(ctx); WorkspaceNode workspaceNode = fetchWorkspaceNode(ctx, command.getFqn(), workspace, true, true); if (workspaceNode != null) { setVersioning(ctx, workspace, workspaceNode); } removeDataAndNotify(workspace, workspaceNode, ctx); addToModificationList(command, ctx); return null; } @Override public Object visitGetKeyValueCommand(InvocationContext ctx, GetKeyValueCommand command) throws Throwable { TransactionWorkspace workspace = getTransactionWorkspace(ctx); Object result; WorkspaceNode workspaceNode = fetchWorkspaceNode(ctx, command.getFqn(), workspace, false, false); if (workspaceNode == null) { if (trace) log.debug("Unable to find node " + command.getFqn() + " in workspace."); result = null; } else { //add this node into the wrokspace notifier.notifyNodeVisited(command.getFqn(), true, ctx); Object val = workspaceNode.get(command.getKey()); workspace.addNode(workspaceNode); notifier.notifyNodeVisited(command.getFqn(), false, ctx); result = val; } return result; } @Override public Object visitGetKeysCommand(InvocationContext ctx, GetKeysCommand command) throws Throwable { TransactionWorkspace workspace = getTransactionWorkspace(ctx); Object result; Fqn fqn = command.getFqn(); WorkspaceNode workspaceNode = fetchWorkspaceNode(ctx, fqn, workspace, false, false); if (workspaceNode == null) { if (trace) log.trace("unable to find node " + fqn + " in workspace."); result = null; } else { notifier.notifyNodeVisited(fqn, true, ctx); Object keySet = workspaceNode.getKeys(); workspace.addNode(workspaceNode); notifier.notifyNodeVisited(fqn, false, ctx); result = keySet; } return result; } @Override public Object visitGetDataMapCommand(InvocationContext ctx, GetDataMapCommand command) throws Throwable { TransactionWorkspace workspace = getTransactionWorkspace(ctx); Object result; WorkspaceNode workspaceNode = fetchWorkspaceNode(ctx, command.getFqn(), workspace, false, false); if (workspaceNode == null) { if (trace) log.trace("unable to find node " + command.getFqn() + " in workspace."); result = null; } else { notifier.notifyNodeVisited(command.getFqn(), true, ctx); Object data = workspaceNode.getData(); workspace.addNode(workspaceNode); notifier.notifyNodeVisited(command.getFqn(), false, ctx); result = data; } return result; } @Override public Object visitGetChildrenNamesCommand(InvocationContext ctx, GetChildrenNamesCommand command) throws Throwable { TransactionWorkspace workspace = getTransactionWorkspace(ctx); Object result; WorkspaceNode workspaceNode = fetchWorkspaceNode(ctx, command.getFqn(), workspace, false, false); if (workspaceNode == null) { if (trace) log.trace("Unable to find node " + command.getFqn() + " in workspace."); result = null; } else { notifier.notifyNodeVisited(command.getFqn(), true, ctx); Object nameSet = workspaceNode.getChildrenNames(); workspace.addNode(workspaceNode); notifier.notifyNodeVisited(command.getFqn(), false, ctx); result = nameSet; } return result; } @Override public Object visitGetNodeCommand(InvocationContext ctx, GetNodeCommand command) throws Throwable { TransactionWorkspace workspace = getTransactionWorkspace(ctx); Object result; WorkspaceNode workspaceNode = fetchWorkspaceNode(ctx, command.getFqn(), workspace, false, false); if (workspaceNode == null) { if (trace) log.trace("Unable to find node " + command.getFqn() + " in workspace."); result = null; } else if (workspaceNode.isDeleted()) { if (trace) log.trace("Attempted to retrieve node " + command.getFqn() + " but it has been deleted!"); result = null; } else { notifier.notifyNodeVisited(command.getFqn(), true, ctx); workspace.addNode(workspaceNode); notifier.notifyNodeVisited(command.getFqn(), false, ctx); result = workspaceNode.getNode(); } return result; } private void setVersioning(InvocationContext ctx, TransactionWorkspace workspace, WorkspaceNode workspaceNode) { // use explicit versioning if (ctx.getOptionOverrides() != null && ctx.getOptionOverrides().getDataVersion() != null) { workspace.setVersioningImplicit(false); DataVersion version = ctx.getOptionOverrides().getDataVersion(); workspaceNode.setVersion(version); if (trace) log.trace("Setting versioning for node " + workspaceNode.getFqn() + " to explicit"); workspaceNode.setVersioningImplicit(false); } else { if (trace) log.trace("Setting versioning for node " + workspaceNode.getFqn() + " to implicit"); workspaceNode.setVersioningImplicit(true); } } /** * Retrieves a backup fqn in an array of arguments. This is typically used to parse arguments from a data gravitation cleanup method. * * @param args array of arguments to parse * @return an Fqn */ private Fqn getBackupFqn(Object[] args) { return (Fqn) args[1]; } /** * Adds a method call to the modification list of a given transaction's transaction entry */ private void addToModificationList(ReversibleCommand command, InvocationContext ctx) { ctx.getTransactionEntry().addModification(command); if (log.isDebugEnabled()) log.debug("Adding command " + command + " to modification list"); } // ----------------------------------------------------------------- // Methods that mimic core functionality in the cache, except that they work on WorkspaceNodes in the TransactionWorkspace. // ----------------------------------------------------------------- /** * Performs a move within the workspace. * * @param parentFqn parent under which the node is to be moved * @param node node to move * @param ws transaction workspace * @param ctx */ private void moveNodeAndNotify(Fqn parentFqn, WorkspaceNode node, TransactionWorkspace ws, InvocationContext ctx) { Fqn nodeFqn = node.getFqn(); if (nodeFqn.isRoot()) { log.warn("Attempting to move the root node. Not taking any action, treating this as a no-op."); return; } WorkspaceNode oldParent = fetchWorkspaceNode(ctx, nodeFqn.getParent(), ws, false, true); if (oldParent == null) throw new NodeNotExistsException("Node " + nodeFqn.getParent() + " does not exist!"); if (parentFqn.equals(oldParent.getFqn())) { log.warn("Attempting to move a node in same place. Not taking any action, treating this as a no-op."); return; } // retrieve parent WorkspaceNode parent = fetchWorkspaceNode(ctx, parentFqn, ws, false, true); if (parent == null) throw new NodeNotExistsException("Node " + parentFqn + " does not exist!"); Object nodeName = nodeFqn.getLastElement(); // now that we have the parent and target nodes: // first correct the pointers at the pruning point oldParent.removeChild(nodeName); // parent pointer is calculated on the fly using Fqns. // now adjust Fqns of node and all children. Fqn nodeNewFqn = Fqn.fromRelativeElements(parent.getFqn(), nodeFqn.getLastElement()); // pre-notify notifier.notifyNodeMoved(nodeFqn, nodeNewFqn, true, ctx); recursiveMoveNode(ctx, node, parent.getFqn(), ws); // remove old nodes. this may mark some nodes which have already been moved as deleted removeNode(ws, node, false, ctx); // post-notify notifier.notifyNodeMoved(nodeFqn, nodeNewFqn, false, ctx); } /** * Moves a node to a new base. * * @param node node to move * @param newBase new base Fqn under which the given node will now exist * @param ws transaction workspace */ private void recursiveMoveNode(InvocationContext ctx, WorkspaceNode node, Fqn newBase, TransactionWorkspace ws) { Fqn newFqn = Fqn.fromRelativeElements(newBase, node.getFqn().getLastElement()); WorkspaceNode movedNode = fetchWorkspaceNode(ctx, newFqn, ws, true, true); movedNode.putAll(node.getData()); // invoke children for (Object n : node.getChildrenNames()) { WorkspaceNode child = fetchWorkspaceNode(ctx, Fqn.fromRelativeElements(node.getFqn(), n), ws, false, true); if (child != null) recursiveMoveNode(ctx, child, newFqn, ws); } } private void putDataMapAndNotify(Map data, TransactionWorkspace workspace, WorkspaceNode workspaceNode, InvocationContext ctx) { if (workspaceNode == null) throw new NodeNotExistsException("optimisticCreateIfNotExistsInterceptor should have created this node!"); // pre-notify notifier.notifyNodeModified(workspaceNode.getFqn(), true, PUT_MAP, workspaceNode.getData(), ctx); workspaceNode.putAll(data); workspace.addNode(workspaceNode); // post-notify notifier.notifyNodeModified(workspaceNode.getFqn(), false, PUT_MAP, workspaceNode.getData(), ctx); } private Object putDataKeyValueAndNotify(Object key, Object value, TransactionWorkspace workspace, WorkspaceNode workspaceNode, InvocationContext ctx) { if (workspaceNode == null) throw new NodeNotExistsException("optimisticCreateIfNotExistsInterceptor should have created this node!"); if (notifier.shouldNotifyOnNodeModified())// pre-notify { notifier.notifyNodeModified(workspaceNode.getFqn(), true, PUT_DATA, workspaceNode.getData(), ctx); } Object old = workspaceNode.put(key, value); workspace.addNode(workspaceNode); if (notifier.shouldNotifyOnNodeModified())// post-notify { Map addedData = Collections.singletonMap(key, value); notifier.notifyNodeModified(workspaceNode.getFqn(), false, PUT_DATA, addedData, ctx); } return old; } private boolean removeNode(TransactionWorkspace workspace, WorkspaceNode workspaceNode, boolean notify, InvocationContext ctx) throws CacheException { // it is already removed - we can ignore it if (workspaceNode == null) return false; Fqn parentFqn = workspaceNode.getFqn().getParent(); WorkspaceNode parentNode = fetchWorkspaceNode(ctx, parentFqn, workspace, false, true); if (parentNode == null) throw new NodeNotExistsException("Unable to find parent node with fqn " + parentFqn); // pre-notify if (notify) notifier.notifyNodeRemoved(workspaceNode.getFqn(), true, workspaceNode.getData(), ctx); Fqn nodeFqn = workspaceNode.getFqn(); parentNode.removeChild(nodeFqn.getLastElement()); SortedMap tailMap = workspace.getNodesAfter(workspaceNode.getFqn()); for (WorkspaceNode toDelete : tailMap.values()) { if (toDelete.getFqn().isChildOrEquals(nodeFqn)) { if (trace) log.trace("marking node " + toDelete.getFqn() + " as deleted"); toDelete.markAsDeleted(true); } else { break;// no more children, we came to the end } } // post-notify if (notify) notifier.notifyNodeRemoved(workspaceNode.getFqn(), false, null, ctx); return workspaceNode.getNode().isValid(); } private Object removeKeyAndNotify(Object removeKey, TransactionWorkspace workspace, WorkspaceNode workspaceNode, InvocationContext ctx) { if (workspaceNode == null) return null; if (notifier.shouldNotifyOnNodeModified())// pre-notify { notifier.notifyNodeModified(workspaceNode.getFqn(), true, REMOVE_DATA, workspaceNode.getData(), ctx); } Object old = workspaceNode.remove(removeKey); workspace.addNode(workspaceNode); if (notifier.shouldNotifyOnNodeModified()) { Map removedData = Collections.singletonMap(removeKey, old); // post-notify notifier.notifyNodeModified(workspaceNode.getFqn(), false, REMOVE_DATA, removedData, ctx); } return old; } private void removeDataAndNotify(TransactionWorkspace workspace, WorkspaceNode workspaceNode, InvocationContext ctx) { if (workspaceNode == null) return; Map data = new HashMap(workspaceNode.getData()); // pre-notify notifier.notifyNodeModified(workspaceNode.getFqn(), true, REMOVE_DATA, data, ctx); workspaceNode.clearData(); workspace.addNode(workspaceNode); // post-notify notifier.notifyNodeModified(workspaceNode.getFqn(), false, REMOVE_DATA, data, ctx); } // ----------------------------------------------------------------- // Methods to help retrieval of nodes from the transaction workspace. // ----------------------------------------------------------------- /** * Retrieves a node for a given Fqn from the workspace. If the node does not exist in the workspace it is retrieved * from the cache's data structure. Note that at no point is a NEW node created in the underlying data structure. * That is up to the {@link org.jboss.cache.interceptors.OptimisticCreateIfNotExistsInterceptor}. *

* If the node requested is in the workspace but marked as deleted, this method will NOT retrieve it, unless undeleteIfNecessary * is true, in which case the node's deleted property is set to false first before being retrieved. * * @param fqn Fqn of the node to retrieve * @param workspace transaction workspace to look in * @param undeleteIfNecessary if the node is in the workspace but marked as deleted, this meth * @param includeInvalidNodes * @return a node, if found, or null if not. */ private WorkspaceNode fetchWorkspaceNode(InvocationContext ctx, Fqn fqn, TransactionWorkspace workspace, boolean undeleteIfNecessary, boolean includeInvalidNodes) { WorkspaceNode workspaceNode = workspace.getNode(fqn); // if we do not have the node then we need to add it to the workspace if (workspaceNode == null) { NodeSPI node = dataContainer.peek(fqn, true, includeInvalidNodes); if (node == null) return null; GlobalTransaction gtx = ctx.getGlobalTransaction(); workspaceNode = lockAndCreateWorkspaceNode(nodeFactory, node, workspace, gtx, lockAcquisitionTimeout); // and add the node to the workspace. workspace.addNode(workspaceNode); } // Check that the workspace node has been marked as deleted. if (workspaceNode.isDeleted()) { if (trace) log.trace("Node " + fqn + " has been deleted in the workspace."); if (undeleteIfNecessary) { undeleteWorkspaceNode(workspaceNode, fetchWorkspaceNode(ctx, fqn.getParent(), workspace, undeleteIfNecessary, includeInvalidNodes)); } else if (!includeInvalidNodes) { // don't return deleted nodes if undeleteIfNecessary is false! workspaceNode = null; } } // set implicit node versioning flag. if (workspaceNode != null && !(workspaceNode.getVersion() instanceof DefaultDataVersion)) { workspaceNode.setVersioningImplicit(false); } // now make sure all parents are in the wsp as well if (workspaceNode != null && !fqn.isRoot()) fetchWorkspaceNode(ctx, fqn.getParent(), workspace, false, includeInvalidNodes); return workspaceNode; } } ././@LongLink0000000000000000000000000000014500000000000011565 Lustar rootrootjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/interceptors/PassivationInterceptorMBean.javajbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/interceptors/PassivationInterceptorMBean.java0000644000175000017500000000265110370152356033415 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * Copyright 2005, JBoss Inc., and individual contributors as indicated * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.cache.interceptors; /** * Interface capturing passivation statistics * @author Jerry Gauthier * @version $Id: PassivationInterceptorMBean.java 1104 2006-02-01 15:34:06Z jerrygauth $ */ public interface PassivationInterceptorMBean extends InterceptorMBean { /** * Returns the number of cache node passivations * * @return the number of cache node passivations */ long getPassivations(); } ././@LongLink0000000000000000000000000000014600000000000011566 Lustar rootrootjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/interceptors/InvocationContextInterceptor.javajbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/interceptors/InvocationContextInterceptor.jav0000644000175000017500000001775311032145203033525 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.interceptors; import org.jboss.cache.InvocationContext; import org.jboss.cache.RPCManager; import org.jboss.cache.commands.VisitableCommand; import org.jboss.cache.commands.tx.CommitCommand; import org.jboss.cache.commands.tx.OptimisticPrepareCommand; import org.jboss.cache.commands.tx.PrepareCommand; import org.jboss.cache.commands.tx.RollbackCommand; import org.jboss.cache.commands.write.ClearDataCommand; import org.jboss.cache.commands.write.PutDataMapCommand; import org.jboss.cache.commands.write.PutForExternalReadCommand; import org.jboss.cache.commands.write.PutKeyValueCommand; import org.jboss.cache.commands.write.RemoveKeyCommand; import org.jboss.cache.commands.write.RemoveNodeCommand; import org.jboss.cache.config.Option; import org.jboss.cache.factories.annotations.Inject; import org.jboss.cache.transaction.GlobalTransaction; import org.jboss.cache.transaction.TransactionTable; import javax.transaction.SystemException; import javax.transaction.Transaction; /** * Always place this interceptor at the start of the interceptor chain to ensure invocation contexts and set up and cleaned up correctly. * * @author Manik Surtani (manik@jboss.org) */ public class InvocationContextInterceptor extends BaseTransactionalContextInterceptor implements InvocationContextInterceptorMBean { private RPCManager rpcManager; @Inject public void setDependencies(RPCManager rpcManager) { this.rpcManager = rpcManager; } @Override public Object visitPutDataMapCommand(InvocationContext ctx, PutDataMapCommand command) throws Throwable { return handleAll(ctx, command, command.getGlobalTransaction(), false); } @Override public Object visitPutForExternalReadCommand(InvocationContext ctx, PutForExternalReadCommand command) throws Throwable { return handleAll(ctx, command, command.getGlobalTransaction(), false); } @Override public Object visitPutKeyValueCommand(InvocationContext ctx, PutKeyValueCommand command) throws Throwable { return handleAll(ctx, command, command.getGlobalTransaction(), false); } @Override public Object visitRemoveNodeCommand(InvocationContext ctx, RemoveNodeCommand command) throws Throwable { return handleAll(ctx, command, command.getGlobalTransaction(), false); } @Override public Object visitClearDataCommand(InvocationContext ctx, ClearDataCommand command) throws Throwable { return handleAll(ctx, command, command.getGlobalTransaction(), false); } @Override public Object visitRemoveKeyCommand(InvocationContext ctx, RemoveKeyCommand command) throws Throwable { return handleAll(ctx, command, command.getGlobalTransaction(), false); } @Override public Object visitPrepareCommand(InvocationContext ctx, PrepareCommand command) throws Throwable { return handleAll(ctx, command, command.getGlobalTransaction(), true); } @Override public Object visitRollbackCommand(InvocationContext ctx, RollbackCommand command) throws Throwable { return handleAll(ctx, command, command.getGlobalTransaction(), true); } @Override public Object visitCommitCommand(InvocationContext ctx, CommitCommand command) throws Throwable { return handleAll(ctx, command, command.getGlobalTransaction(), true); } @Override public Object visitOptimisticPrepareCommand(InvocationContext ctx, OptimisticPrepareCommand command) throws Throwable { return handleAll(ctx, command, command.getGlobalTransaction(), true); } @Override public Object handleDefault(InvocationContext ctx, VisitableCommand command) throws Throwable { return handleAll(ctx, command, null, false); } @SuppressWarnings("deprecation") private Object handleAll(InvocationContext ctx, VisitableCommand command, GlobalTransaction gtx, boolean scrubContextOnCompletion) throws Throwable { Option optionOverride = ctx.getOptionOverrides(); boolean suppressExceptions = false; Transaction suspendedTransaction = null; boolean resumeSuspended = false; if (trace) log.trace("Invoked with command " + command + " and InvocationContext [" + ctx + "]"); try { if (txManager != null) { Transaction tx = getTransaction(); GlobalTransaction realGtx = getGlobalTransaction(tx, gtx); if (tx == null && realGtx != null && realGtx.isRemote()) tx = txTable.getLocalTransaction(gtx); setTransactionalContext(tx, realGtx, null, ctx); } else { setTransactionalContext(null, null, null, ctx); } if (optionOverride != null && optionOverride.isFailSilently()) { log.debug("FAIL_SILENTLY Option is present - suspending any ongoing transaction."); suppressExceptions = true; if (ctx.getTransaction() != null) { suspendedTransaction = txManager.suspend(); setTransactionalContext(null, null, null, ctx); if (trace) log.trace("Suspending transaction " + suspendedTransaction); resumeSuspended = true; } else { if (trace) log.trace("No ongoing transaction to suspend"); } } Object retval; try { return invokeNextInterceptor(ctx, command); } catch (Throwable th) { retval = th; // if fail silently return a null if (suppressExceptions) return null; Throwable t = (Throwable) retval; if (t instanceof RuntimeException && t.getCause() != null) { throw t.getCause(); } else { throw t; } } // assume we're the first interceptor in the chain. Handle the exception-throwing. } finally { /* * we should scrub txs after every call to prevent race conditions * basically any other call coming in on the same thread and hijacking any running tx's * was highlighted in JBCACHE-606 */ if (scrubContextOnCompletion) setTransactionalContext(null, null, null, ctx); // clean up any invocation-scope options set up if (trace) log.trace("Resetting invocation-scope options"); ctx.getOptionOverrides().reset(); // if this is a prepare, opt prepare or if (resumeSuspended) { txManager.resume(suspendedTransaction); } else { if (ctx.getTransaction() != null && (TransactionTable.isValid(ctx.getTransaction()))) { copyInvocationScopeOptionsToTxScope(ctx); } } // reset the context to prevent leakage of internals ctx.setCommand(null); ctx.setMethodCall(null); } } private GlobalTransaction getGlobalTransaction(Transaction tx, GlobalTransaction gtx) { if (gtx == null) gtx = txTable.getCurrentTransaction(tx, false); if (gtx != null) gtx.setRemote(isRemoteGlobalTx(gtx)); return gtx; } private Transaction getTransaction() throws SystemException { // this creates a context if one did not exist. if (txManager == null) { if (trace) log.trace("no transaction manager configured, setting tx as null."); return null; } else { return txManager.getTransaction(); } } /** * Tests if a global transaction originated from a different cache in the cluster * * @param gtx * @return true if the gtx is remote, false if it originated locally. */ private boolean isRemoteGlobalTx(GlobalTransaction gtx) { return gtx != null && (gtx.getAddress() != null) && (!gtx.getAddress().equals(rpcManager.getLocalAddress())); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/interceptors/DataGravitatorInterceptor.java0000644000175000017500000003436411032145203033121 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.interceptors; import org.jboss.cache.CacheException; import org.jboss.cache.CacheSPI; import org.jboss.cache.DataContainer; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.buddyreplication.BuddyFqnTransformer; import org.jboss.cache.buddyreplication.BuddyManager; import org.jboss.cache.buddyreplication.GravitateResult; import org.jboss.cache.commands.DataCommand; import org.jboss.cache.commands.ReplicableCommand; import org.jboss.cache.commands.read.ExistsCommand; import org.jboss.cache.commands.read.GetChildrenNamesCommand; import org.jboss.cache.commands.read.GetDataMapCommand; import org.jboss.cache.commands.read.GetKeyValueCommand; import org.jboss.cache.commands.read.GetKeysCommand; import org.jboss.cache.commands.read.GetNodeCommand; import org.jboss.cache.commands.read.GravitateDataCommand; import org.jboss.cache.commands.remote.DataGravitationCleanupCommand; import org.jboss.cache.commands.tx.CommitCommand; import org.jboss.cache.commands.tx.RollbackCommand; import org.jboss.cache.factories.CommandsFactory; import org.jboss.cache.factories.annotations.Inject; import org.jboss.cache.marshall.NodeData; import org.jboss.cache.transaction.GlobalTransaction; import org.jgroups.Address; import org.jgroups.blocks.GroupRequest; import org.jgroups.blocks.RspFilter; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * The Data Gravitator interceptor intercepts cache misses and attempts to * gravitate data from other parts of the cluster. *

* Only used if Buddy Replication is enabled. Also, the interceptor only kicks * in if an {@link org.jboss.cache.config.Option} is passed in to force Data * Gravitation for a specific invocation or if autoDataGravitation is * set to true when configuring Buddy Replication. *

* See the JBoss Cache User Guide for more details on configuration options. * There is a section dedicated to Buddy Replication in the Replication * chapter. *

* In terms of functionality, if a gravitation call has occured and a cleanup call is needed (based on * how BR is configured), a cleanup call will be broadcast immediately after the gravitation call (no txs) * or if txs are used, an asynchronous call is made to perform the cleanup outside the scope * of the tx that caused the gravitation event. *

* * @author Manik Surtani (manik@jboss.org) */ public class DataGravitatorInterceptor extends BaseRpcInterceptor { private BuddyManager buddyManager; /** * Map that contains commands that need cleaning up. This is keyed on global transaction, and contains a list of * cleanup commands corresponding to all gravitate calls made during the course of the transaction in question. */ private Map> cleanupCommands = new ConcurrentHashMap>(); private DataContainer dataContainer; private CommandsFactory commandsFactory; private CacheSPI cacheSPI; private BuddyFqnTransformer buddyFqnTransformer; @Inject public void injectComponents(BuddyManager buddyManager, DataContainer dataContainer, CommandsFactory commandsFactory, CacheSPI cacheSPI, BuddyFqnTransformer transformer) { this.buddyManager = buddyManager; this.dataContainer = dataContainer; this.commandsFactory = commandsFactory; this.cacheSPI = cacheSPI; buddyFqnTransformer = transformer; } @Override public Object visitGetChildrenNamesCommand(InvocationContext ctx, GetChildrenNamesCommand command) throws Throwable { return handleGetMethod(ctx, command); } @Override public Object visitGetDataMapCommand(InvocationContext ctx, GetDataMapCommand command) throws Throwable { return handleGetMethod(ctx, command); } @Override public Object visitExistsNodeCommand(InvocationContext ctx, ExistsCommand command) throws Throwable { return handleGetMethod(ctx, command); } @Override public Object visitGetKeysCommand(InvocationContext ctx, GetKeysCommand command) throws Throwable { return handleGetMethod(ctx, command); } @Override public Object visitGetKeyValueCommand(InvocationContext ctx, GetKeyValueCommand command) throws Throwable { return handleGetMethod(ctx, command); } @Override public Object visitGetNodeCommand(InvocationContext ctx, GetNodeCommand command) throws Throwable { return handleGetMethod(ctx, command); } @Override public Object visitRollbackCommand(InvocationContext ctx, RollbackCommand command) throws Throwable { try { return invokeNextInterceptor(ctx, command); } finally { cleanupCommands.remove(ctx.getGlobalTransaction()); } } @Override public Object visitCommitCommand(InvocationContext ctx, CommitCommand command) throws Throwable { GlobalTransaction gtx = ctx.getGlobalTransaction(); try { doCommit(gtx); return invokeNextInterceptor(ctx, command); } finally { cleanupCommands.remove(gtx); } } private Object handleGetMethod(InvocationContext ctx, DataCommand command) throws Throwable { if (isGravitationEnabled(ctx)) { // test that the Fqn being requested exists locally in the cache. if (trace) log.trace("Checking local existence of command " + command); if (buddyFqnTransformer.isBackupFqn(command.getFqn())) { log.info("Is call for a backup Fqn, not performing any gravitation. Direct calls on internal backup nodes are *not* supported."); } else { if (dataContainer.peek(command.getFqn(), false, false) == null) { // gravitation is necessary. if (trace) log.trace("Gravitating from local backup tree"); BackupData data = localBackupGet(command.getFqn(), ctx); if (data == null) { if (trace) log.trace("Gravitating from remote backup tree"); // gravitate remotely. data = remoteBackupGet(command.getFqn()); } if (data != null) { if (trace) log.trace("Passing the put call locally to make sure state is persisted and ownership is correctly established."); // store the gravitated node locally. This will cause it being backed up in the current instance's buddy. createNode(data.backupData); cleanBackupData(data, ctx); } } else { if (trace) log.trace("No need to gravitate; have this already."); } } } else { if (trace) { log.trace("Suppressing data gravitation for this call."); } } return invokeNextInterceptor(ctx, command); } private boolean isGravitationEnabled(InvocationContext ctx) { boolean enabled = ctx.isOriginLocal(); if (enabled && !buddyManager.isAutoDataGravitation()) { enabled = ctx.getOptionOverrides().getForceDataGravitation(); } return enabled; } private void doCommit(GlobalTransaction gtx) throws Throwable { if (cleanupCommands.containsKey(gtx)) { if (trace) log.trace("Broadcasting cleanup commands for gtx " + gtx); for (ReplicableCommand command : cleanupCommands.get(gtx)) { try { doCleanup(command); } catch (Throwable th) { log.warn("Problems performing gravitation cleanup. Cleanup command: " + command, th); } } } else { if (trace) log.trace("No cleanups to broadcast in commit phase for gtx " + gtx); } } private BackupData remoteBackupGet(Fqn name) throws Exception { BackupData result = null; GravitateResult gr = gravitateData(name); if (gr.isDataFound()) { if (trace) { log.trace("Got response " + gr); } result = new BackupData(name, gr); } return result; } private void cleanBackupData(BackupData backup, InvocationContext ctx) throws Throwable { DataGravitationCleanupCommand cleanupCommand = commandsFactory.buildDataGravitationCleanupCommand(backup.primaryFqn, backup.backupFqn); GlobalTransaction gtx = ctx.getGlobalTransaction(); if (gtx == null) { // broadcast removes // remove main Fqn if (trace) log.trace("Performing cleanup on [" + backup.primaryFqn + "]"); // remove backup Fqn doCleanup(cleanupCommand); } else { if (trace) log.trace("Data gravitation performed under global transaction " + gtx + ". Not broadcasting cleanups until the tx commits. Recording cleanup command for later use."); List commands; if (cleanupCommands.containsKey(gtx)) { commands = cleanupCommands.get(gtx); } else { commands = new LinkedList(); } commands.add(cleanupCommand); cleanupCommands.put(gtx, commands); } } private void doCleanup(ReplicableCommand cleanupCommand) throws Throwable { // cleanup commands are always ASYNCHRONOUS and is broadcast to *everyone* (even members of the current buddy // group as they may be members of > 1 buddy group) replicateCall(null, cleanupCommand, false, false, false, true, -1); } @SuppressWarnings("deprecation") private GravitateResult gravitateData(Fqn fqn) throws Exception { if (trace) log.trace("Requesting data gravitation for Fqn " + fqn); List

mbrs = rpcManager.getMembers(); Boolean searchSubtrees = buddyManager.isDataGravitationSearchBackupTrees() ? Boolean.TRUE : Boolean.FALSE; GravitateDataCommand command = commandsFactory.buildGravitateDataCommand(fqn, searchSubtrees); // doing a GET_ALL is crappy but necessary since JGroups' GET_FIRST could return null results from nodes that do // not have either the primary OR backup, and stop polling other valid nodes. List resps = rpcManager.callRemoteMethods(null, command, GroupRequest.GET_ALL, buddyManager.getBuddyCommunicationTimeout(), new ResponseValidityFilter(mbrs, rpcManager.getLocalAddress()), false); if (trace) log.trace("got responses " + resps); if (resps == null) { if (mbrs.size() > 1) log.error("No replies to call " + command); return GravitateResult.noDataFound(); } // test for and remove exceptions GravitateResult result = GravitateResult.noDataFound(); for (Object o : resps) { if (o instanceof Throwable) { if (log.isDebugEnabled()) { log.debug("Found remote Throwable among responses - removing from responses list", (Exception) o); } } else if (o != null) { result = (GravitateResult) o; if (result.isDataFound()) { break; } } else if (!configuration.isUseRegionBasedMarshalling()) { // Null is OK if we are using region based marshalling; it // is what is returned if a region is inactive. Otherwise // getting a null is an error condition log.error("Unexpected null response to call " + command + "."); } } return result; } @SuppressWarnings("unchecked") private void createNode(List nodeData) throws CacheException { for (NodeData data : nodeData) { cacheSPI.put(data.getFqn(), data.getAttributes()); } } private BackupData localBackupGet(Fqn fqn, InvocationContext ctx) throws CacheException { GravitateResult result = cacheSPI.gravitateData(fqn, true, ctx);// a "local" gravitation boolean found = result.isDataFound(); BackupData data = null; if (found) { Fqn backupFqn = result.getBuddyBackupFqn(); data = new BackupData(fqn, result); // now the cleanup if (buddyManager.isDataGravitationRemoveOnFind()) { // Remove locally only; the remote call will // be broadcast later ctx.getOptionOverrides().setCacheModeLocal(true); cacheSPI.removeNode(backupFqn); } else { cacheSPI.evict(backupFqn, true); } } return data; } private static class BackupData { Fqn primaryFqn; Fqn backupFqn; List backupData; public BackupData(Fqn primaryFqn, GravitateResult gr) { this.primaryFqn = primaryFqn; this.backupFqn = gr.getBuddyBackupFqn(); this.backupData = gr.getNodeData(); } } public static class ResponseValidityFilter implements RspFilter { private int numValidResponses = 0; private List
pendingResponders; public ResponseValidityFilter(List
expected, Address localAddress) { // so for now I used a list to keep it consistent this.pendingResponders = new ArrayList
(expected); // We'll never get a response from ourself this.pendingResponders.remove(localAddress); } public boolean isAcceptable(Object object, Address address) { pendingResponders.remove(address); if (object instanceof GravitateResult) { GravitateResult response = (GravitateResult) object; if (response.isDataFound()) numValidResponses++; } // always return true to make sure a response is logged by the JGroups RpcDispatcher. return true; } public boolean needMoreResponses() { return numValidResponses < 1 && pendingResponders.size() > 0; } } } ././@LongLink0000000000000000000000000000015200000000000011563 Lustar rootrootjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/interceptors/OptimisticReplicationInterceptor.javajbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/interceptors/OptimisticReplicationInterceptor0000755000175000017500000003550311015045001033576 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.interceptors; import org.jboss.cache.CacheException; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.commands.AbstractVisitor; import org.jboss.cache.commands.ReplicableCommand; import org.jboss.cache.commands.ReversibleCommand; import org.jboss.cache.commands.VersionedDataCommand; import org.jboss.cache.commands.VisitableCommand; import org.jboss.cache.commands.read.GravitateDataCommand; import org.jboss.cache.commands.tx.CommitCommand; import org.jboss.cache.commands.tx.OptimisticPrepareCommand; import org.jboss.cache.commands.tx.RollbackCommand; import org.jboss.cache.commands.write.CreateNodeCommand; import org.jboss.cache.commands.write.MoveCommand; import org.jboss.cache.commands.write.PutDataMapCommand; import org.jboss.cache.commands.write.PutForExternalReadCommand; import org.jboss.cache.commands.write.PutKeyValueCommand; import org.jboss.cache.commands.write.ClearDataCommand; import org.jboss.cache.commands.write.RemoveKeyCommand; import org.jboss.cache.commands.write.RemoveNodeCommand; import org.jboss.cache.factories.CommandsFactory; import org.jboss.cache.factories.annotations.Inject; import org.jboss.cache.optimistic.DataVersion; import org.jboss.cache.optimistic.DefaultDataVersion; import org.jboss.cache.optimistic.TransactionWorkspace; import org.jboss.cache.optimistic.WorkspaceNode; import org.jboss.cache.transaction.GlobalTransaction; import org.jboss.cache.transaction.OptimisticTransactionEntry; import org.jboss.cache.transaction.TransactionEntry; import org.jboss.cache.util.concurrent.ConcurrentHashSet; import java.util.ArrayList; import java.util.List; import java.util.Set; /** * Replication interceptor for the optimistically locked interceptor chain. Responsible for replicating * state to remote nodes. Unlike it's cousin, the {@link org.jboss.cache.interceptors.ReplicationInterceptor}, this interceptor * only deals with transactional calls. Just like all things to do with Optimistic Locking, it is a requirement that * everything is done in a transaction and the transaction context is available via {@link org.jboss.cache.InvocationContext#getTransaction()} * and {@link org.jboss.cache.InvocationContext#getGlobalTransaction()}. * * @author Manik Surtani (manik@jboss.org) * @author Steve Woodcock (stevew@jofti.com) */ public class OptimisticReplicationInterceptor extends BaseRpcInterceptor { /** * record of local broacasts - so we do not broadcast rollbacks/commits that resuted from local prepare failures we * really just need a set here, but concurrent CopyOnWriteArraySet has poor performance when writing. */ private final Set broadcastTxs = new ConcurrentHashSet(); private CommandsFactory commandsFactory; @Inject public void initialize(CommandsFactory commandsFactory) { this.commandsFactory = commandsFactory; } @Override public Object visitOptimisticPrepareCommand(InvocationContext ctx, OptimisticPrepareCommand command) throws Throwable { // pass up the chain. Object retval = invokeNextInterceptor(ctx, command); if (!skipReplicationOfTransactionMethod(ctx)) { GlobalTransaction gtx = getGlobalTransaction(ctx); TransactionEntry te = ctx.getTransactionEntry(); if (te.hasLocalModifications()) { OptimisticPrepareCommand replicablePrepareCommand = command.clone(); // makre sure we remove any "local" transactions replicablePrepareCommand.removeModifications(te.getLocalModifications()); command = replicablePrepareCommand; } // replicate the prepare call. broadcastPrepare(command, gtx, ctx); } return retval; } @Override public Object visitCommitCommand(InvocationContext ctx, CommitCommand command) throws Throwable { //lets broadcast the commit first Throwable remoteCommitException = null; GlobalTransaction gtx = getGlobalTransaction(ctx); if (!gtx.isRemote() && ctx.isOriginLocal() && broadcastTxs.contains(gtx)) { try { if (!skipReplicationOfTransactionMethod(ctx)) broadcastCommit(gtx, ctx); } catch (Throwable t) { log.error("A problem occurred with remote commit", t); remoteCommitException = t; } } Object retval = invokeNextInterceptor(ctx, command); if (remoteCommitException != null) { throw remoteCommitException; } return retval; } @Override public Object visitRollbackCommand(InvocationContext ctx, RollbackCommand command) throws Throwable { // lets broadcast the rollback first GlobalTransaction gtx = getGlobalTransaction(ctx); Throwable remoteRollbackException = null; if (!gtx.isRemote() && ctx.isOriginLocal() && broadcastTxs.contains(gtx)) { //we dont do anything try { if (!skipReplicationOfTransactionMethod(ctx)) broadcastRollback(gtx, ctx); } catch (Throwable t) { log.error(" a problem occurred with remote rollback", t); remoteRollbackException = t; } } Object retval = invokeNextInterceptor(ctx, command); if (remoteRollbackException != null) { throw remoteRollbackException; } return retval; } @Override public Object visitPutForExternalReadCommand(InvocationContext ctx, PutForExternalReadCommand command) throws Throwable { ctx.getTransactionEntry().setForceAsyncReplication(true); return handleDefault(ctx, command); } public Object handleDefault(InvocationContext ctx, VisitableCommand command) throws Throwable { if (isLocalModeForced(ctx) && command instanceof ReversibleCommand) ctx.getTransactionEntry().addLocalModification((ReversibleCommand) command); return invokeNextInterceptor(ctx, command); } private GlobalTransaction getGlobalTransaction(InvocationContext ctx) { // get the current globalTransaction GlobalTransaction gtx = ctx.getGlobalTransaction(); if (gtx == null) { throw new CacheException("failed to get global transaction"); } return gtx; } protected void broadcastPrepare(OptimisticPrepareCommand command, GlobalTransaction gtx, InvocationContext ctx) throws Throwable { // this method will return immediately if we're the only member if (rpcManager.getMembers() != null && rpcManager.getMembers().size() > 1) { // Map method calls to data versioned equivalents. // See JBCACHE-843 and docs/design/DataVersioning.txt DataVersionPopulator populator = new DataVersionPopulator(getTransactionWorkspace(ctx), command.getModifications().size()); // visit all elements in the collection and apply the DataVersionPopulator to ensure all commands have data versions set. populator.visitCollection(null, command.getModifications()); ReplicableCommand toBroadcast = commandsFactory.buildOptimisticPrepareCommand(gtx, populator.versionedCommands, command.getData(), command.getLocalAddress(), command.isOnePhaseCommit()); //record the things we have possibly sent broadcastTxs.add(gtx); if (log.isDebugEnabled()) { log.debug("(" + rpcManager.getLocalAddress() + "): broadcasting prepare for " + gtx + " (" + command.getModificationsCount() + " modifications"); } replicateCall(ctx, toBroadcast, defaultSynchronous, ctx.getOptionOverrides()); } else { //no members, ignoring if (log.isDebugEnabled()) { log.debug("(" + rpcManager.getLocalAddress() + "):not broadcasting prepare as members are " + rpcManager.getMembers()); } } } protected void broadcastCommit(GlobalTransaction gtx, InvocationContext ctx) throws Throwable { boolean remoteCallSync = configuration.isSyncCommitPhase(); // Broadcast commit() to all members (exclude myself though) if (rpcManager.getMembers() != null && rpcManager.getMembers().size() > 1) { try { broadcastTxs.remove(gtx); CommitCommand commitCommand = commandsFactory.buildCommitCommand(gtx); if (log.isDebugEnabled()) log.debug("running remote commit for " + gtx + " and coord=" + rpcManager.getLocalAddress()); // for an optimistic commit we don't need to force an OOB message since O/L means we have non-blocking reads. replicateCall(ctx, commitCommand, remoteCallSync, ctx.getOptionOverrides(), false); } catch (Exception e) { log.error("Commit failed", e); throw e; } } } protected void broadcastRollback(GlobalTransaction gtx, InvocationContext ctx) throws Throwable { boolean remoteCallSync = configuration.isSyncRollbackPhase(); if (rpcManager.getMembers() != null && rpcManager.getMembers().size() > 1) { // Broadcast rollback() to all other members (excluding myself) try { broadcastTxs.remove(gtx); RollbackCommand rollbackCommand = commandsFactory.buildRollbackCommand(null); if (log.isDebugEnabled()) log.debug("running remote rollback for " + gtx + " and coord=" + rpcManager.getLocalAddress()); replicateCall(ctx, rollbackCommand, remoteCallSync, ctx.getOptionOverrides()); } catch (Exception e) { log.error("Rollback failed", e); throw e; } } } public class DataVersionPopulator extends AbstractVisitor { final TransactionWorkspace workspace; final List versionedCommands; public DataVersionPopulator(TransactionWorkspace workspace, int numCommands) { this.workspace = workspace; versionedCommands = new ArrayList(numCommands); } private void setDataVersion(VersionedDataCommand clone, Fqn fqn) { DataVersion versionToBroadcast = getVersionToBroadcast(workspace, fqn); clone.setDataVersion(versionToBroadcast); versionedCommands.add(clone); } @Override public Object visitGravitateDataCommand(InvocationContext ctx, GravitateDataCommand command) throws Throwable { return command; } @Override public Object visitPutDataMapCommand(InvocationContext ctx, PutDataMapCommand command) throws Throwable { VersionedDataCommand clone = commandsFactory.buildPutDataMapCommand(null, command.getFqn(), command.getData()); setDataVersion(clone, command.getFqn()); return null; } @Override public Object visitPutKeyValueCommand(InvocationContext ctx, PutKeyValueCommand command) throws Throwable { VersionedDataCommand clone = commandsFactory.buildPutKeyValueCommand(null, command.getFqn(), command.getKey(), command.getValue()); setDataVersion(clone, command.getFqn()); return null; } @Override public Object visitPutForExternalReadCommand(InvocationContext ctx, PutForExternalReadCommand command) throws Throwable { VersionedDataCommand clone = commandsFactory.buildPutForExternalReadCommand(null, command.getFqn(), command.getKey(), command.getValue()); setDataVersion(clone, command.getFqn()); return null; } @Override public Object visitRemoveNodeCommand(InvocationContext ctx, RemoveNodeCommand command) throws Throwable { VersionedDataCommand clone = commandsFactory.buildRemoveNodeCommand(command.getGlobalTransaction(), command.getFqn()); setDataVersion(clone, command.getFqn()); return null; } @Override public Object visitRemoveKeyCommand(InvocationContext ctx, RemoveKeyCommand command) throws Throwable { VersionedDataCommand clone = commandsFactory.buildRemoveKeyCommand(null, command.getFqn(), command.getKey()); setDataVersion(clone, command.getFqn()); return null; } @Override public Object visitClearDataCommand(InvocationContext ctx, ClearDataCommand command) throws Throwable { VersionedDataCommand clone = commandsFactory.buildClearDataCommand(command.getGlobalTransaction(), command.getFqn() ); setDataVersion(clone, command.getFqn()); return null; } @Override public Object visitCreateNodeCommand(InvocationContext ctx, CreateNodeCommand command) throws Throwable { versionedCommands.add(command); return command; } @Override public Object visitMoveCommand(InvocationContext ctx, MoveCommand command) throws Throwable { versionedCommands.add(command); return command; } @Override public Object handleDefault(InvocationContext ctx, VisitableCommand command) throws Throwable { throw new CacheException("Not handling " + command + "!"); } /** * Digs out the DataVersion for a given Fqn. If the versioning is explicit, it is passed as-is. If implicit, it is * cloned and then incremented, and the clone is returned. */ private DataVersion getVersionToBroadcast(TransactionWorkspace w, Fqn f) { WorkspaceNode n = w.getNode(f); if (n == null) { if (trace) log.trace("Fqn " + f + " not found in workspace; not using a data version."); return null; } if (n.isVersioningImplicit()) { DefaultDataVersion v = (DefaultDataVersion) n.getVersion(); if (trace) log.trace("Fqn " + f + " has implicit versioning. Broadcasting an incremented version."); // potential bug here - need to check if we *need* to increment at all, because of Configuration.isLockParentForChildInsertRemove() return v.increment(); } else { if (trace) log.trace("Fqn " + f + " has explicit versioning. Broadcasting the version as-is."); return n.getVersion(); } } } protected TransactionWorkspace getTransactionWorkspace(InvocationContext ctx) throws CacheException { OptimisticTransactionEntry transactionEntry = (OptimisticTransactionEntry) ctx.getTransactionEntry(); if (transactionEntry == null) { throw new CacheException("unable to map global transaction " + ctx + " to transaction entry"); } // try and get the workspace from the transaction return transactionEntry.getTransactionWorkSpace(); } }jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/interceptors/CacheLoaderInterceptor.java0000755000175000017500000004507411074652365032364 0ustar twernertwernerpackage org.jboss.cache.interceptors; import org.jboss.cache.CacheException; import org.jboss.cache.DataContainer; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.NodeSPI; import org.jboss.cache.commands.ReversibleCommand; import org.jboss.cache.commands.read.GetChildrenNamesCommand; import org.jboss.cache.commands.read.GetDataMapCommand; import org.jboss.cache.commands.read.GetKeyValueCommand; import org.jboss.cache.commands.read.GetKeysCommand; import org.jboss.cache.commands.read.GetNodeCommand; import org.jboss.cache.commands.tx.RollbackCommand; import org.jboss.cache.commands.write.ClearDataCommand; import org.jboss.cache.commands.write.MoveCommand; import org.jboss.cache.commands.write.PutDataMapCommand; import org.jboss.cache.commands.write.PutForExternalReadCommand; import org.jboss.cache.commands.write.PutKeyValueCommand; import org.jboss.cache.commands.write.RemoveKeyCommand; import org.jboss.cache.commands.write.RemoveNodeCommand; import org.jboss.cache.config.Configuration; import static org.jboss.cache.config.Configuration.CacheMode; import org.jboss.cache.factories.annotations.Inject; import org.jboss.cache.factories.annotations.Start; import org.jboss.cache.interceptors.base.CommandInterceptor; import org.jboss.cache.loader.CacheLoader; import org.jboss.cache.loader.CacheLoaderManager; import org.jboss.cache.lock.LockManager; import org.jboss.cache.lock.LockType; import org.jboss.cache.lock.TimeoutException; import org.jboss.cache.notifications.Notifier; import org.jboss.cache.transaction.TransactionEntry; import org.jboss.cache.transaction.TransactionTable; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.ListIterator; import java.util.Map; import java.util.Set; /** * Loads nodes that don't exist at the time of the call into memory from the CacheLoader * * @author Bela Ban * @version $Id: CacheLoaderInterceptor.java 6909 2008-10-13 14:02:29Z bstansberry@jboss.com $ */ public class CacheLoaderInterceptor extends CommandInterceptor implements CacheLoaderInterceptorMBean { private long cacheLoads = 0; private long cacheMisses = 0; private CacheLoaderManager clm; private LockManager lockManager; protected TransactionTable txTable = null; protected CacheLoader loader; protected DataContainer dataContainer; protected Notifier notifier; protected boolean isActivation = false; protected boolean usingOptimisticInvalidation = false; /** * True if CacheStoreInterceptor is in place. * This allows us to skip loading keys for remove(Fqn, key) and put(Fqn, key). * It also affects removal of node data and listing children. */ protected boolean useCacheStore = true; @Inject protected void injectDependencies(TransactionTable txTable, CacheLoaderManager clm, Configuration configuration, DataContainer dataContainer, LockManager lockManager, Notifier notifier) { this.txTable = txTable; this.clm = clm; CacheMode mode = configuration.getCacheMode(); usingOptimisticInvalidation = configuration.isNodeLockingOptimistic() && mode.isInvalidation(); this.dataContainer = dataContainer; this.lockManager = lockManager; this.notifier = notifier; } @Start protected void startInterceptor() { loader = clm.getCacheLoader(); } @Override public Object visitPutDataMapCommand(InvocationContext ctx, PutDataMapCommand command) throws Throwable { if (command.getFqn() != null) { loadIfNeeded(ctx, command.getFqn(), null, true, true, false, ctx.getTransactionEntry(), false, false, false); } return invokeNextInterceptor(ctx, command); } @Override public Object visitPutKeyValueCommand(InvocationContext ctx, PutKeyValueCommand command) throws Throwable { if (command.getFqn() != null) { loadIfNeeded(ctx, command.getFqn(), command.getKey(), false, useCacheStore, !useCacheStore, ctx.getTransactionEntry(), false, false, false); } return invokeNextInterceptor(ctx, command); } @Override public Object visitPutForExternalReadCommand(InvocationContext ctx, PutForExternalReadCommand command) throws Throwable { return visitPutKeyValueCommand(ctx, command); } @Override public Object visitMoveCommand(InvocationContext ctx, MoveCommand command) throws Throwable { if (command.getFqn() != null) { if (command.getTo() != null) { loadIfNeeded(ctx, command.getTo(), null, false, false, true, ctx.getTransactionEntry(), false, true, false); } loadIfNeeded(ctx, command.getFqn(), null, false, false, true, ctx.getTransactionEntry(), true, true, false); } return invokeNextInterceptor(ctx, command); } @Override public Object visitGetKeyValueCommand(InvocationContext ctx, GetKeyValueCommand command) throws Throwable { if (command.getFqn() != null) { loadIfNeeded(ctx, command.getFqn(), command.getKey(), false, false, true, ctx.getTransactionEntry(), false, false, false); } return invokeNextInterceptor(ctx, command); } @Override public Object visitGetNodeCommand(InvocationContext ctx, GetNodeCommand command) throws Throwable { if (command.getFqn() != null) { loadIfNeeded(ctx, command.getFqn(), null, false, false, true, ctx.getTransactionEntry(), false, false, !usingOptimisticInvalidation); } return invokeNextInterceptor(ctx, command); } @Override public Object visitGetChildrenNamesCommand(InvocationContext ctx, GetChildrenNamesCommand command) throws Throwable { Fqn fqn = command.getFqn(); if (fqn != null) { loadIfNeeded(ctx, fqn, null, false, false, false, ctx.getTransactionEntry(), false, false, true); NodeSPI n = dataContainer.peek(fqn, true, true); loadChildren(fqn, n, false, false, ctx); } return invokeNextInterceptor(ctx, command); } @Override public Object visitGetKeysCommand(InvocationContext ctx, GetKeysCommand command) throws Throwable { if (command.getFqn() != null) { loadIfNeeded(ctx, command.getFqn(), null, true, false, true, ctx.getTransactionEntry(), false, false, false); } return invokeNextInterceptor(ctx, command); } @Override public Object visitGetDataMapCommand(InvocationContext ctx, GetDataMapCommand command) throws Throwable { if (command.getFqn() != null) { loadIfNeeded(ctx, command.getFqn(), null, true, false, true, ctx.getTransactionEntry(), false, false, false); } return invokeNextInterceptor(ctx, command); } @Override public Object visitRollbackCommand(InvocationContext ctx, RollbackCommand command) throws Throwable { // clean up nodesCreated map if (trace) log.trace("Removing temporarily created nodes from treecache"); // this needs to be done in reverse order. List list = ctx.getTransactionEntry().getDummyNodesCreatedByCacheLoader(); if (list != null && list.size() > 0) { ListIterator i = list.listIterator(list.size()); while (i.hasPrevious()) { Fqn fqn = (Fqn) i.previous(); try { dataContainer.evict(fqn, false); } catch (CacheException e) { if (trace) log.trace("Unable to evict node " + fqn, e); } } } return invokeNextInterceptor(ctx, command); } @Override public Object visitRemoveNodeCommand(InvocationContext ctx, RemoveNodeCommand command) throws Throwable { if (configuration.isNodeLockingOptimistic() && command.getFqn() != null) { loadIfNeeded(ctx, command.getFqn(), null, false, false, false, ctx.getTransactionEntry(), false, false, false); } return invokeNextInterceptor(ctx, command); } @Override public Object visitRemoveKeyCommand(InvocationContext ctx, RemoveKeyCommand command) throws Throwable { if (command.getFqn() != null && !useCacheStore) { loadIfNeeded(ctx, command.getFqn(), command.getKey(), false, false, false, ctx.getTransactionEntry(), false, false, false); } return invokeNextInterceptor(ctx, command); } @Override public Object visitClearDataCommand(InvocationContext ctx, ClearDataCommand command) throws Throwable { Fqn fqn = command.getFqn(); if (fqn != null && !useCacheStore) { loadIfNeeded(ctx, fqn, null, false, true, false, ctx.getTransactionEntry(), false, false, false); } return invokeNextInterceptor(ctx, command); } private void loadIfNeeded(InvocationContext ctx, Fqn fqn, Object key, boolean allKeys, boolean initNode, boolean acquireLock, TransactionEntry entry, boolean recursive, boolean isMove, boolean bypassLoadingData) throws Throwable { NodeSPI n = dataContainer.peek(fqn, true, true); Object lockOwner = lockManager.getLockOwner(ctx); boolean needLock = n != null && !lockManager.ownsLock(fqn, lockOwner); boolean mustLoad = false; try { if (needLock && !lockManager.lock(n, LockType.READ, lockOwner)) { throw new TimeoutException("Unable to acquire lock on " + fqn + ". Lock info: " + lockManager.printLockInfo(n)); } mustLoad = mustLoad(n, key, allKeys || isMove); } finally { if (needLock) lockManager.unlock(n, lockOwner); } if (trace) { log.trace("load element " + fqn + " mustLoad=" + mustLoad); } if (mustLoad) { if (initNode) { n = createTempNode(fqn, entry); } // Only attempt to acquire this lock if we need to - i.e., if // the lock hasn't already been acquired by the Lock // interceptor. CRUD methods (put, remove) would have acquired // this lock - even if the node is not in memory and needs to be // loaded. Non-CRUD methods (put) would NOT have acquired this // lock so if we are to load the node from cache loader, we need // to acquire a write lock here. as a 'catch-all', DO NOT // attempt to acquire a lock here *anyway*, even for CRUD // methods - this leads to a deadlock when you have threads // simultaneously trying to create a node. See // org.jboss.cache.loader.deadlock.ConcurrentCreationDeadlockTest // - Manik Surtani (21 March 2006) if (acquireLock) { lock(fqn, LockType.WRITE, false, ctx);// non-recursive for now } // if (!initNode && !wasRemovedInTx(fqn, ctx.getGlobalTransaction())) if (!wasRemovedInTx(fqn, ctx)) { if (bypassLoadingData) { if (n == null && loader.exists(fqn)) { // just create a dummy node in memory n = createTempNode(fqn, entry); } } else { n = loadNode(ctx, fqn, n, entry); } } } // The complete list of children aren't known without loading them if (recursive) { loadChildren(fqn, n, recursive, isMove, ctx); } } /** * Load the children. * * @param node may be null if the node was not found. * @param ctxt */ private void loadChildren(Fqn fqn, NodeSPI node, boolean recursive, boolean isMove, InvocationContext ctxt) throws Throwable { if (node != null && node.isChildrenLoaded()) { if (trace) log.trace("Children already loaded!"); return; } Set childrenNames; try { childrenNames = loader.getChildrenNames(fqn); } catch (Exception e) { if (log.isInfoEnabled()) log.info("Cache loader was unable to load state", e); // return! return; } if (trace) { log.trace("load children " + fqn + " children=" + childrenNames); } // For getChildrenNames null means no children if (childrenNames == null) { if (node != null) { if (useCacheStore) { node.removeChildrenDirect();//getChildrenMapDirect().clear(); } node.setChildrenLoaded(true); } return; } // Create if node had not been created already if (node == null) { node = createNodes(fqn, null);// dont care about local transactions } // Create one DataNode per child, mark as UNINITIALIZED for (Object name : childrenNames) { Fqn childFqn = Fqn.fromElements(name);// this is a RELATIVE Fqn!! // create child if it didn't exist NodeSPI child = node.addChildDirect(childFqn); if ((isMove || isActivation) && recursive) { // load data for children as well! child.setInternalState(loader.get(child.getFqn())); child.setDataLoaded(true); } if (recursive) { loadChildren(child.getFqn(), child, true, isMove, ctxt); } } lock(fqn, recursive ? LockType.WRITE : LockType.READ, true, ctxt);// recursive=true: lock entire subtree node.setChildrenLoaded(true); } private boolean mustLoad(NodeSPI n, Object key, boolean allKeys) { if (n == null) { if (trace) log.trace("must load, node null"); return true; } // check this first!!! if (!n.isValid() && configuration.isNodeLockingOptimistic()) { // attempt to load again; this only happens if we have tombstones lying around, or we are using invalidation. if (trace) log.trace("loading again from cache loader since in-memory node is marked as invalid"); return true; } // JBCACHE-1172 Skip single-key optimization if request needs all keys if (!allKeys) { // if we are not looking for a specific key don't bother loading! if (key == null) { if (trace) log.trace("don't load, key requested is null"); return false; } if (n.getKeysDirect().contains(key)) { if (trace) log.trace("don't load, already have necessary key in memory"); return false; } } if (!n.isDataLoaded()) { if (trace) log.trace("must Load, uninitialized"); return true; } return false; } public long getCacheLoaderLoads() { return cacheLoads; } public long getCacheLoaderMisses() { return cacheMisses; } @Override public void resetStatistics() { cacheLoads = 0; cacheMisses = 0; } @Override public Map dumpStatistics() { Map retval = new HashMap(); retval.put("CacheLoaderLoads", cacheLoads); retval.put("CacheLoaderMisses", cacheMisses); return retval; } protected void lock(Fqn fqn, LockType lockType, boolean recursive, InvocationContext ctx) throws Throwable { if (configuration.isNodeLockingOptimistic()) return; if (recursive) lockManager.lockAllAndRecord(fqn, lockType, ctx); else lockManager.lockAndRecord(fqn, lockType, ctx); } /** * Returns true if the FQN or parent was removed during the current * transaction. * This is O(N) WRT to the number of modifications so far. */ private boolean wasRemovedInTx(Fqn fqn, InvocationContext ctx) { TransactionEntry entry = ctx.getTransactionEntry(); if (entry == null) return false; for (ReversibleCommand txCacheCommand : entry.getModifications()) { if (txCacheCommand instanceof RemoveNodeCommand && fqn.isChildOrEquals(txCacheCommand.getFqn())) return true; } return false; } /** * Loads a node from disk; if it exists creates parent TreeNodes. * If it doesn't exist on disk but in memory, clears the * uninitialized flag, otherwise returns null. */ private NodeSPI loadNode(InvocationContext ctx, Fqn fqn, NodeSPI n, TransactionEntry entry) throws Exception { if (trace) log.trace("loadNode " + fqn); Map nodeData = loadData(fqn); if (nodeData != null) { if (trace) log.trace("Node data is not null, loading"); notifier.notifyNodeLoaded(fqn, true, Collections.emptyMap(), ctx); if (isActivation) { notifier.notifyNodeActivated(fqn, true, Collections.emptyMap(), ctx); } n = createNodes(fqn, entry); // n.clearDataDirect(); n.setInternalState(nodeData); // set this node as valid? if (usingOptimisticInvalidation) n.setValid(true, false); notifier.notifyNodeLoaded(fqn, false, nodeData, ctx); if (isActivation) { notifier.notifyNodeActivated(fqn, false, nodeData, ctx); } } if (n != null && !n.isDataLoaded()) { if (trace) log.trace("Setting dataLoaded to true"); n.setDataLoaded(true); } return n; } /** * Creates a new memory node in preparation for storage. */ private NodeSPI createTempNode(Fqn fqn, TransactionEntry entry) throws Exception { NodeSPI n = createNodes(fqn, entry); n.setDataLoaded(false); if (trace) { log.trace("createTempNode n " + n); } return n; } @SuppressWarnings("unchecked") private NodeSPI createNodes(Fqn fqn, TransactionEntry entry) throws Exception { Object[] results = dataContainer.createNodes(fqn); List createdNodes = (List) results[0]; NodeSPI lastCreated = null; for (NodeSPI node : createdNodes) { node.setDataLoaded(false); if (entry != null) { entry.loadUninitialisedNode(node.getFqn()); } lastCreated = node; } // mark the leaf node as data loaded since that is what we are doing in this interceptor. if (lastCreated != null) lastCreated.setDataLoaded(true); // regardless of whether the last node was created, return it. return (NodeSPI) results[1]; } private Map loadData(Fqn fqn) throws Exception { Map nodeData = loader.get(fqn); boolean nodeExists = (nodeData != null); if (trace) log.trace("nodeExists " + nodeExists); if (getStatisticsEnabled()) { if (nodeExists) { cacheLoads++; } else { cacheMisses++; } } return nodeData; } }jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/AbstractNode.java0000644000175000017500000001116711041076602025620 0ustar twernertwerner/** * */ package org.jboss.cache; import static org.jboss.cache.AbstractNode.NodeFlags.DELETED; import static org.jboss.cache.AbstractNode.NodeFlags.RESIDENT; import java.util.Map; /** * Base class for {@link UnversionedNode}. * * @author manik */ public abstract class AbstractNode// implements Node { protected Map> children; protected Fqn fqn; /** * Flags placed on the node. Replaces older 'boolean' flags. */ // NOTE: this is a lot more efficient than an EnumSet, expecially when initialising and copying. protected short flags = 0; /** * These flags were originally stored as booleans on the UnversionedNode class. They have been replaced with an enum * and an EnumSet, which is much more space-efficient for very little cost in lookups. */ public static enum NodeFlags { /** * All children are loaded from the cache loader if this flag is present. */ CHILDREN_LOADED(0x1), /** * Data is loaded from the cache loader if this flag is present. */ DATA_LOADED(0x2), /** * Node is write-locked when children are added or removed if this flag is enabled. */ LOCK_FOR_CHILD_INSERT_REMOVE(0x4), /** * Node is valid if this flag is present. */ VALID(0x8), /** * Node has been deleted. */ DELETED(0x10), /** * NOde is resident and excluded from evictions */ RESIDENT(0x20), /** * Specific to Optimistic Locking Workspace nodes - set if a node has been modified in a workspace. */ MODIFIED_IN_WORKSPACE(0x40), /** * Specific to Optimistic Locking Workspace nodes - set if a node has been created in a workspace. */ CREATED_IN_WORKSPACE(0x80), /** * Specific to Optimistic Locking Workspace nodes - set if a node has added or removed children in a workspace. */ CHILDREN_MODIFIED_IN_WORKSPACE(0x100), /** * Specific to Optimistic Locking Workspace nodes - set if an implicit version is associated with this node. */ VERSIONING_IMPLICIT(0x200), /** * Specific to Optimistic Locking Workspace nodes - set if a node has been resurrected in a workspace. */ RESURRECTED_IN_WORKSPACE(0x400); protected final short mask; NodeFlags(int mask) { this.mask = (short) mask; } } /** * Tests whether a flag is set. * * @param flag flag to test * @return true if set, false otherwise. */ protected final boolean isFlagSet(NodeFlags flag) { return (flags & flag.mask) != 0; } /** * Utility method for setting or unsetting a flag. If status is true, the NodeFlag specified is added to the {@link #flags} * encoded short. If status is false, the NodeFlag is removed from the encoded short. * * @param flag flag to set or unset * @param value true to set the flag, false to unset the flag. */ protected final void setFlag(NodeFlags flag, boolean value) { if (value) setFlag(flag); else unsetFlag(flag); } /** * Unility method that sets the value of the given flag to true. * * @param flag flag to set */ protected final void setFlag(NodeFlags flag) { flags |= flag.mask; } /** * Utility method that sets the value of the flag to false. * * @param flag flag to unset */ protected final void unsetFlag(NodeFlags flag) { flags &= ~flag.mask; } public boolean isDeleted() { return isFlagSet(DELETED); } public void markAsDeleted(boolean marker) { markAsDeleted(marker, false); } public void markAsDeleted(boolean marker, boolean recursive) { setFlag(DELETED, marker); if (recursive && children != null) { synchronized (this) { for (Node child : children.values()) { ((NodeSPI) child).markAsDeleted(marker, true); } } } } public void setResident(boolean resident) { setFlag(RESIDENT, resident); } public boolean isResident() { return isFlagSet(RESIDENT); } @Override public boolean equals(Object another) { if (another instanceof AbstractNode) { AbstractNode anotherNode = (AbstractNode) another; return fqn == null && anotherNode.fqn == null || !(fqn == null || anotherNode.fqn == null) && fqn.equals(anotherNode.fqn); } return false; } @Override public int hashCode() { return fqn.hashCode(); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/CacheException.java0000644000175000017500000000210610660427364026135 0ustar twernertwerner// $Id: CacheException.java 4252 2007-08-14 22:38:44Z jason.greene@jboss.com $ /* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache; /** * Thrown when operations on {@link org.jboss.cache.Cache} or {@link org.jboss.cache.Node} fail unexpectedly. *

* Specific subclasses such as {@link org.jboss.cache.lock.TimeoutException}, {@link org.jboss.cache.config.ConfigurationException} and {@link org.jboss.cache.lock.LockingException} * have more specific uses. * * @author Bela Ban * @author Manik Surtani */ public class CacheException extends RuntimeException { private static final long serialVersionUID = -4386393072593859164L; public CacheException() { super(); } public CacheException(Throwable cause) { super(cause); } public CacheException(String msg) { super(msg); } public CacheException(String msg, Throwable cause) { super(msg, cause); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/util/0000755000175000017500000000000011376174035023365 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/util/CachePrinter.java0000644000175000017500000000377411031017377026603 0ustar twernertwernerpackage org.jboss.cache.util; import org.jboss.cache.Cache; import org.jboss.cache.CacheSPI; import org.jboss.cache.DataContainerImpl; import org.jboss.cache.interceptors.base.CommandInterceptor; import org.jboss.cache.invocation.CacheInvocationDelegate; /** * Helper that prints the contents of a {@link org.jboss.cache.Cache} to a string. * * @author Manik Surtani * @since 2.0.0 */ public class CachePrinter { /** * Prints the contents of the cache (nodes + data) to a string * * @param c cache to print * @return a String representation of the cache */ public static String printCacheDetails(Cache c) { // internal cast DataContainerImpl ci = (DataContainerImpl) ((CacheInvocationDelegate) c).getDataContainer(); return ci.printDetails(); } /** * Prints the status of locks in the cache (nodes + locks) to a string * * @param c cache to print * @return a String representation of the cache */ public static String printCacheLockingInfo(Cache c) { // internal cast DataContainerImpl cd = (DataContainerImpl) ((CacheInvocationDelegate) c).getDataContainer(); return cd.printLockInfo(); } public static String printCacheInterceptors(CacheSPI cache) { StringBuilder b = new StringBuilder(); int index = 0; b.append("\n"); for (CommandInterceptor i : cache.getInterceptorChain()) { b.append("# "); b.append(index); b.append(" : "); b.append(i); b.append("\n"); index++; } return b.toString(); } public static String printInterceptorChain(CommandInterceptor i) { StringBuilder sb = new StringBuilder(); if (i != null) { if (i.getNext() != null) { sb.append(printInterceptorChain(i.getNext())).append("\n"); } sb.append("\t>> "); sb.append(i.getClass().getName()); } return sb.toString(); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/util/BeanUtils.java0000644000175000017500000000610611006101071026075 0ustar twernertwernerpackage org.jboss.cache.util; import java.lang.reflect.Method; import java.util.Locale; /** * Simple JavaBean manipulation helper methods * * @author Manik Surtani (manik@jboss.org) * @since 2.1.0 */ public class BeanUtils { /** * Retrieves a setter name based on a field name passed in * * @param fieldName field name to find setter for * @return name of setter method */ public static String setterName(String fieldName) { StringBuilder sb = new StringBuilder("set"); if (fieldName != null && fieldName.length() > 0) { sb.append(fieldName.substring(0, 1).toUpperCase(Locale.ENGLISH)); if (fieldName.length() > 1) { sb.append(fieldName.substring(1)); } } return sb.toString(); } /** * Returns a getter for a given class * * @param componentClass class to find getter for * @return name of getter method */ public static String getterName(Class componentClass) { if (componentClass == null) return null; StringBuilder sb = new StringBuilder("get"); sb.append(componentClass.getSimpleName()); return sb.toString(); } /** * Returns a setter for a given class * * @param componentClass class to find setter for * @return name of getter method */ public static String setterName(Class componentClass) { if (componentClass == null) return null; StringBuilder sb = new StringBuilder("set"); sb.append(componentClass.getSimpleName()); return sb.toString(); } /** * Returns a Method object corresponding to a getter that retrieves an instance of componentClass from target. * * @param target class that the getter should exist on * @param componentClass component to get * @return Method object, or null of one does not exist */ public static Method getterMethod(Class target, Class componentClass) { try { return target.getMethod(getterName(componentClass)); } catch (NoSuchMethodException e) { //if (log.isTraceEnabled()) log.trace("Unable to find method " + getterName(componentClass) + " in class " + target); return null; } catch (NullPointerException e) { return null; } } /** * Returns a Method object corresponding to a setter that sets an instance of componentClass from target. * * @param target class that the setter should exist on * @param componentClass component to set * @return Method object, or null of one does not exist */ public static Method setterMethod(Class target, Class componentClass) { try { return target.getMethod(setterName(componentClass), componentClass); } catch (NoSuchMethodException e) { //if (log.isTraceEnabled()) log.trace("Unable to find method " + setterName(componentClass) + " in class " + target); return null; } catch (NullPointerException e) { return null; } } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/util/BitEncodedIntegerSet.java0000644000175000017500000000451511021001464030205 0ustar twernertwernerpackage org.jboss.cache.util; import net.jcip.annotations.NotThreadSafe; /** * A Set that encodes integers as bits in a long. Does not implement java.util.Set since autoboxing is unnecessarily * expensive for the ints stored, but follows very similar semantics to Set: no nulls, no duplicates, and order not guaranteed, * and adds one more: this can only store ints from 0 to 63, inclusive. *

* Integers in this set are packed into a single long, setting bit values accordingly and hence the strict range on allowable * integers. The upshot is a guaranteed limit on how much memory is consumed, as well as very efficient operations on the set. *

* * @author Manik Surtani (manik@jboss.org) * @since 2.1.0 */ @NotThreadSafe public class BitEncodedIntegerSet { private long encoded = 0; /** * Adds an integer to the set. * * @param i integer to add */ public void add(int i) { encoded |= ((long) 1 << i); } /** * Removes an integer from the set * * @param i integer to remove */ public void remove(int i) { encoded &= ~((long) 1 << i); } /** * Tests whether the set contains an integer * * @param i integer to check for * @return true if contained; false otherwise */ public boolean contains(int i) { return (encoded & ((long) 1 << i)) != 0; } @Override public boolean equals(Object o) { if (o == this) return true; if (o == null || getClass() != o.getClass()) return false; BitEncodedIntegerSet that = (BitEncodedIntegerSet) o; return encoded == that.encoded; } @Override public int hashCode() { return (int) (encoded ^ (encoded >>> 32)); } /** * Clears the set */ public void clear() { encoded = 0; } /** * Tests if the set is empty * * @return true if empty */ public boolean isEmpty() { return encoded == 0; } @Override public String toString() { return "BitEncodedSet (encoded as: " + Long.toBinaryString(encoded) + ")"; } /** * Adds all elements of another BitEncodedIntegerSet to the current set. * * @param otherSet other set to add */ public void addAll(BitEncodedIntegerSet otherSet) { encoded |= otherSet.encoded; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/util/concurrent/0000755000175000017500000000000011376174034025546 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/util/concurrent/ConcurrentHashSet.java0000644000175000017500000000617011033367610032011 0ustar twernertwernerpackage org.jboss.cache.util.concurrent; import java.util.AbstractSet; import java.util.Collection; import java.util.Iterator; import java.util.concurrent.ConcurrentHashMap; /** * A simple Set implementation backed by a {@link java.util.concurrent.ConcurrentHashMap} to deal with the fact that the * JDK does not have a proper concurrent Set implementation that uses efficient lock striping. *

* Note that values are stored as keys in the underlying Map, with a static dummy object as value. * * @author Manik Surtani * @since 2.0.0 */ public class ConcurrentHashSet extends AbstractSet { protected ConcurrentHashMap map; private static final Object DUMMY = new Object(); public ConcurrentHashSet() { map = new ConcurrentHashMap(); } /** * @param concurrency concurrency level passed in to the CHM constructor. See {@link ConcurrentHashMap#ConcurrentHashMap(int, float, int)} javadocs for details. */ public ConcurrentHashSet(int concurrency) { map = new ConcurrentHashMap(16, 0.75f, concurrency); } /** * Params passed in to the CHM constructor. See {@link ConcurrentHashMap#ConcurrentHashMap(int, float, int)} javadocs for details. */ public ConcurrentHashSet(int initSize, float loadFactor, int concurrency) { map = new ConcurrentHashMap(initSize, loadFactor, concurrency); } @Override public int size() { return map.size(); } @Override public boolean isEmpty() { return map.isEmpty(); } @Override public boolean contains(Object o) { return map.containsKey(o); } @Override public Iterator iterator() { return map.keySet().iterator(); } @Override public Object[] toArray() { return map.keySet().toArray(); } @Override public T[] toArray(T[] a) { return map.keySet().toArray(a); } @Override public boolean add(E o) { Object v = map.put(o, DUMMY); return v == null; } @Override public boolean remove(Object o) { Object v = map.remove(o); return v != null; } @Override public boolean containsAll(Collection c) { return map.keySet().containsAll(c); } @Override public boolean addAll(Collection c) { throw new UnsupportedOperationException("Not supported in this implementation since additional locking is required and cannot directly be delegated to multiple calls to ConcurrentHashMap"); } @Override public boolean retainAll(Collection c) { throw new UnsupportedOperationException("Not supported in this implementation since additional locking is required and cannot directly be delegated to multiple calls to ConcurrentHashMap"); } @Override public boolean removeAll(Collection c) { throw new UnsupportedOperationException("Not supported in this implementation since additional locking is required and cannot directly be delegated to multiple calls to ConcurrentHashMap"); } @Override public void clear() { map.clear(); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/util/reflect/0000755000175000017500000000000011376174034025010 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/util/reflect/ReflectionUtil.java0000644000175000017500000001021211011102214030550 0ustar twernertwernerpackage org.jboss.cache.util.reflect; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.CacheException; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Arrays; import java.util.Collection; import java.util.LinkedList; import java.util.List; /** * Basic reflection utilities to enhance what the JDK provides. * * @author Manik Surtani (manik@jboss.org) * @since 2.1.0 */ public class ReflectionUtil { private static final Log log = LogFactory.getLog(ReflectionUtil.class); /** * Returns a set of Methods that contain the given method annotation. This includes all public, protected, package and private * methods, as well as those of superclasses. Note that this does *not* include overridden methods. * * @param c class to inspect * @param annotationType the type of annotation to look for * @return List of Method objects that require injection. */ public static List getAllMethods(Class c, Class annotationType) { List annotated = new LinkedList(); inspectRecursively(c, annotated, annotationType); return annotated; } /** * Inspects a class and it's superclasses (all the way to {@link Object} for method instances that contain a given annotation. * This even identifies private, package and protected methods, not just public ones. * * @param c * @param s * @param annotationType */ private static void inspectRecursively(Class c, List s, Class annotationType) { // Superclass first if (!c.equals(Object.class)) inspectRecursively(c.getSuperclass(), s, annotationType); for (Method m : c.getDeclaredMethods()) { // don't bother if this method has already been overridden by a subclass if (!alreadyFound(m, s) && m.isAnnotationPresent(annotationType)) { s.add(m); } } } /** * Tests whether a method has already been found, i.e., overridden. * * @param m method to inspect * @param s collection of methods found * @return true a method with the same signature already exists. */ private static boolean alreadyFound(Method m, Collection s) { for (Method found : s) { if (m.getName().equals(found.getName()) && Arrays.equals(m.getParameterTypes(), found.getParameterTypes())) return true; } return false; } public static void setValue(Object instance, String fieldName, Object value) { try { Field f = findFieldRecursively(instance.getClass(), fieldName); if (f == null) throw new NoSuchMethodException("Cannot find field " + fieldName + " on " + instance.getClass() + " or superclasses"); f.setAccessible(true); f.set(instance, value); } catch (Exception e) { log.error("Unable to set value!", e); } } private static Field findFieldRecursively(Class c, String fieldName) { Field f = null; try { f = c.getDeclaredField(fieldName); } catch (NoSuchFieldException e) { if (!c.equals(Object.class)) f = findFieldRecursively(c.getSuperclass(), fieldName); } return f; } /** * Invokes a method using reflection, in an accessible manner (by using {@link Method#setAccessible(boolean)} * * @param instance instance on which to execute the method * @param method method to execute * @param parameters parameters */ public static void invokeAccessibly(Object instance, Method method, Object[] parameters) { try { method.setAccessible(true); method.invoke(instance, parameters); } catch (Exception e) { throw new CacheException("Unable to invoke method " + method + " on object instance " + instance + (parameters != null ? " with parameters " + Arrays.asList(parameters) : ""), e); } } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/util/ImmutableSetCopy.java0000644000175000017500000000672011044030206027442 0ustar twernertwernerpackage org.jboss.cache.util; import net.jcip.annotations.Immutable; import java.io.Externalizable; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.util.AbstractSet; import java.util.Collection; import java.util.Iterator; import java.util.NoSuchElementException; /** * This is based on an ImmutableListCopy, with the assumption that the set passed in would ensure uniqueness of elements. *

* The constructor takes in a collection so the onus is on the caller to ensure that the collection passed in adheres to * Set semantics. *

* Typically used in place of the common idiom: * * return Collections.unmodifiableSet(new HashSet( myInternalSet )); * * * @author Manik Surtani (manik@jboss.org) * @see org.jboss.cache.util.ImmutableListCopy * @since 3.0 */ @Immutable public class ImmutableSetCopy extends AbstractSet implements Externalizable { private static final long serialVersionUID = 11929568968766L; private E[] elements; private int size; @SuppressWarnings("unchecked") public ImmutableSetCopy(Collection set) { size = set.size(); E[] tempElements = (E[]) new Object[size]; // no room for growth elements = set.toArray(tempElements); } /** * Assumes that the array passed in is "safe", i.e., is not referenced from elsewhere. Also assumes the array contains * elements such that the uniqueness required by a set is adhered to. Use with care! * * @param array to reference */ public ImmutableSetCopy(E[] array) { elements = array; size = elements.length; } @Override public boolean remove(Object o) { throw new UnsupportedOperationException(); } @Override public boolean addAll(Collection c) { throw new UnsupportedOperationException(); } @Override public boolean retainAll(Collection c) { throw new UnsupportedOperationException(); } @Override public boolean removeAll(Collection c) { throw new UnsupportedOperationException(); } @Override public void clear() { throw new UnsupportedOperationException(); } public Iterator iterator() { return new Iterator() { int cursor = 0; public boolean hasNext() { return cursor < size; } public E next() { if (cursor >= size) throw new NoSuchElementException(); return elements[cursor++]; } public void remove() { throw new UnsupportedOperationException(); } }; } public int size() { return size; } /** * Format: * - entry array size (int) * - elements (Object) * * @param out stream to write to * @throws IOException */ public void writeExternal(ObjectOutput out) throws IOException { out.writeInt(size); for (E e : elements) out.writeObject(e); } /** * See {@link #writeExternal(java.io.ObjectOutput)} for serialization format * * @param in stream * @throws IOException * @throws ClassNotFoundException */ @SuppressWarnings("unchecked") public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { size = in.readInt(); elements = (E[]) new Object[size]; for (int i = 0; i < size; i++) elements[i] = (E) in.readObject(); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/util/DeltaMap.java0000644000175000017500000001731310707457507025731 0ustar twernertwernerpackage org.jboss.cache.util; import java.util.AbstractMap; import java.util.AbstractSet; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.NoSuchElementException; import java.util.Set; /** * Wraps an existing map, which is not modified, reflecting modifications * in an internal modification set. * * This is to minimize the amount of data copying, for instance in the * case few changes are applied. * * Example usage:

HashMap<String, String> hm = new HashMap<String, String>();
hm.put("a", "apple");
DeltaMap dm = DeltaMap.create(hm);
dm.remove("a");
assert hm.containsKey("a");
assert !dm.containsKey("a");
dm.commit();
assert !hm.containsKey("a");
* * @author Elias Ross * @param key type * @param value type */ public class DeltaMap extends AbstractMap { /** * Wrapped instance. */ private Map original; /** * Keys excluded. */ private Set exclude; /** * Keys changed. * This may contain new entries or entries modified. */ private Map changed = new HashMap(); /** * Constructs a new DeltaMap. */ private DeltaMap(Map original, Set exclude) { if (original == null) throw new NullPointerException("original"); if (exclude == null) throw new NullPointerException("exclude"); this.original = original; this.exclude = exclude; } /** * Creates and returns a DeltaMap for an original map. * * @param original will not be modified, except by {@link #commit()} * @return a new instance */ public static DeltaMap create(Map original) { return new DeltaMap(original, new HashSet()); } /** * Creates and returns a DeltaMap for an empty map. * @return a new instance */ public static DeltaMap create() { return create(new HashMap(0)); } /** * Creates and returns a DeltaMap for an original map, excluding some key mappings. * * @param original will not be modified, except by {@link #commit()} * @param excluded entries not to include * @return a new instance */ public static DeltaMap excludeKeys(Map original, Set exclude) { return new DeltaMap(original, exclude); } /** * Creates and returns a DeltaMap for an original map, excluding some key mappings. */ public static DeltaMap excludeKeys(Map original, K... exclude) { return excludeKeys(original, new HashSet(Arrays.asList(exclude))); } @Override public Set> entrySet() { return new AbstractSet>() { @Override public Iterator> iterator() { return new WrappedIterator(); } @Override public int size() { return DeltaMap.this.size(); } }; } @Override public int size() { int size = original.size(); for (Object o : changed.keySet()) { if (!original.containsKey(o)) size++; } for (Object o : exclude) { if (original.containsKey(o)) size--; } return size; } @Override public boolean containsKey(Object key) { if (exclude.contains(key)) return false; if (changed.containsKey(key)) return true; return original.containsKey(key); } @Override public V get(Object key) { if (exclude.contains(key)) return null; if (changed.containsKey(key)) return changed.get(key); return original.get(key); } @Override public V put(K key, V value) { V old; if (changed.containsKey(key)) old = changed.get(key); else old = original.get(key); changed.put(key, value); if (exclude.contains(key)) { exclude.remove(key); return null; } return old; } @SuppressWarnings("unchecked") @Override public V remove(Object key) { if (changed.containsKey(key)) { if (original.containsKey(key)) exclude.add((K) key); return changed.remove(key); } if (exclude.contains(key)) { return null; } if (original.containsKey(key)) { exclude.add((K) key); return original.get(key); } return null; } /** * Commits the changes to the original map. * Clears the list of removed keys. */ public void commit() { original.keySet().removeAll(exclude); original.putAll(changed); exclude.clear(); changed.clear(); } /** * Returns true if the internal map was modified. */ public boolean isModified() { return !changed.isEmpty() || !exclude.isEmpty(); } /** * Iterator that skips over removed entries. * @author Elias Ross */ private class WrappedIterator implements Iterator> { private boolean orig = true; private boolean nextSet = false; private Entry next; private Iterator> i = original.entrySet().iterator(); private boolean redef(Entry e) { K key = e.getKey(); return exclude.contains(key) || changed.containsKey(key); } public boolean hasNext() { if (nextSet) return true; if (orig) { while (true) { if (!i.hasNext()) { orig = false; i = changed.entrySet().iterator(); return hasNext(); } next = i.next(); if (!redef(next)) { nextSet = true; return true; } } } if (!i.hasNext()) return false; next = i.next(); nextSet = true; return true; } public java.util.Map.Entry next() { if (!hasNext()) throw new NoSuchElementException(); try { return next; } finally { nextSet = false; } } public void remove() { DeltaMap.this.remove(next.getKey()); } } /** * Returns a debug string. */ public String toDebugString() { return "DeltaMap original=" + original + " exclude=" + exclude + " changed=" + changed; } @Override public void clear() { exclude.addAll(original.keySet()); changed.clear(); } /** * Returns the original wrapped Map. */ public Map getOriginal() { return original; } /** * Sets the original values of this delta map. */ public void setOriginal(Map original) { if (original == null) throw new NullPointerException("original"); this.original = original; } /** * Returns a Map of the entries changed, not including those removed. */ public Map getChanged() { return changed; } /** * Returns the entries removed, including entries excluded by the constructor. */ public Set getRemoved() { return exclude; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/util/MapCopy.java0000755000175000017500000000505711004125045025573 0ustar twernertwernerpackage org.jboss.cache.util; import net.jcip.annotations.Immutable; import java.io.IOException; import java.io.Serializable; import java.util.AbstractMap; import java.util.AbstractSet; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; /** * Contains a fixed array of read-only map entries, from a copy of an existing map. * This class is more lightweight for places where the copied map will just be iterated over. *

* This map is strictly read-only, and map modification methods (as well as modifications over iterators) will throw * {@link UnsupportedOperationException}s. * * @author Elias Ross */ @Immutable public class MapCopy extends AbstractMap implements Serializable { private static final long serialVersionUID = -958813082188242956L; private final List> data; private transient Set> entrySet; /** * Copies the supplied map to an internal array. * * @param m map to copy */ public MapCopy(Map m) { data = new ArrayList>(m.size()); for (Map.Entry me : m.entrySet()) { if (me == null) throw new NullPointerException(); data.add(new SimpleImmutableEntry(me)); } init(); } public MapCopy() { this(new HashMap(0)); } /** * Returns a copy of the given map. */ public static Map copy(Map m) { return new MapCopy(m); } private void init() { this.entrySet = new AbstractSet>() { @Override public int size() { return data.size(); } @Override public Iterator> iterator() { return new EntryIterator(); } }; } private class EntryIterator implements Iterator> { private int index; public boolean hasNext() { return index < data.size(); } public Entry next() { return data.get(index++); } public void remove() { throw new UnsupportedOperationException(); } } @Override public Set> entrySet() { return entrySet; } @Override public int size() { return data.size(); } private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); init(); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/util/Util.java0000644000175000017500000001275111006024704025136 0ustar twernertwerner/* * JBoss, Home of Professional Open Source. * Copyright 2006, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.cache.util; import java.util.HashMap; import java.util.Map; /** * General utility methods used throughout the JBC code base. * * @author Brian Stansberry * @version $Revision: 5768 $ */ public final class Util { /** * Loads the specified class using this class's classloader, or, if it is null * (i.e. this class was loaded by the bootstrap classloader), the system classloader. *

* If loadtime instrumentation via GenerateInstrumentedClassLoader is used, this * class may be loaded by the bootstrap classloader. *

* * @param classname name of the class to load * @return the class * @throws ClassNotFoundException */ public static Class loadClass(String classname) throws ClassNotFoundException { ClassLoader cl = Thread.currentThread().getContextClassLoader(); if (cl == null) cl = ClassLoader.getSystemClassLoader(); return cl.loadClass(classname); } /** * Prevent instantiation */ private Util() { } /** * Calculates the diffs between data maps passed in to {@link org.jboss.cache.notifications.event.NodeModifiedEvent#getData()} * before and after modification. This only makes sense if the modification type is {@link org.jboss.cache.notifications.event.NodeModifiedEvent.ModificationType#PUT_MAP}. * Refer to {@link org.jboss.cache.notifications.event.NodeModifiedEvent} and {@link org.jboss.cache.notifications.annotation.NodeModified}. * * @param pre map of data before the node was modified * @param post Map of data after the node was modified * @return MapModifications containing the differences. */ public static MapModifications diffNodeData(Map pre, Map post) { MapModifications mods = new MapModifications(); // let's start with what's been added and modified. for (Map.Entry me : post.entrySet()) { Object key = me.getKey(); Object value = me.getValue(); if (pre.containsKey(key)) { if (!value.equals(pre.get(key))) { mods.modifiedEntries.put(key, value); } } else { mods.addedEntries.put(key, value); } } // now the removed entries. for (Map.Entry me : pre.entrySet()) { Object key = me.getKey(); if (!post.containsKey(key)) { mods.removedEntries.put(key, me.getValue()); } } return mods; } /** * Null-safe equality test. * * @param a first object to compare * @param b second object to compare * @return true if the objects are equals or both null, false otherwise. */ public static boolean safeEquals(Object a, Object b) { return (a == b) || (a != null && a.equals(b)); } /** * Static inner class that holds 3 maps - for data added, removed and modified. */ public static class MapModifications { public final Map addedEntries = new HashMap(); public final Map removedEntries = new HashMap(); public final Map modifiedEntries = new HashMap(); @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; MapModifications that = (MapModifications) o; if (addedEntries != null ? !addedEntries.equals(that.addedEntries) : that.addedEntries != null) return false; if (modifiedEntries != null ? !modifiedEntries.equals(that.modifiedEntries) : that.modifiedEntries != null) return false; if (removedEntries != null ? !removedEntries.equals(that.removedEntries) : that.removedEntries != null) return false; return true; } @Override public int hashCode() { int result; result = (addedEntries != null ? addedEntries.hashCode() : 0); result = 31 * result + (removedEntries != null ? removedEntries.hashCode() : 0); result = 31 * result + (modifiedEntries != null ? modifiedEntries.hashCode() : 0); return result; } @Override public String toString() { return "Added Entries " + addedEntries + " Removeed Entries " + removedEntries + " Modified Entries " + modifiedEntries; } } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/util/SimpleImmutableEntry.java0000644000175000017500000000247711004116445030342 0ustar twernertwernerpackage org.jboss.cache.util; import java.io.Serializable; import java.util.Map; import java.util.Map.Entry; /** * Where is Java 1.6? */ class SimpleImmutableEntry implements Map.Entry, Serializable { private static final long serialVersionUID = -6092752114794052323L; private final K key; private final V value; public SimpleImmutableEntry(Entry me) { key = me.getKey(); value = me.getValue(); } public SimpleImmutableEntry(K key, V value) { this.key = key; this.value = value; } public K getKey() { return key; } public V getValue() { return value; } public V setValue(V arg0) { throw new UnsupportedOperationException(); } @Override public boolean equals(Object o) { if (!(o instanceof Map.Entry)) return false; Map.Entry e2 = (Map.Entry) o; return (getKey() == null ? e2.getKey() == null : getKey().equals(e2.getKey())) && (getValue() == null ? e2.getValue() == null : getValue().equals(e2.getValue())); } @Override public int hashCode() { return (getKey() == null ? 0 : getKey().hashCode()) ^ (getValue() == null ? 0 : getValue().hashCode()); } @Override public String toString() { return key + "=" + value; } }jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/util/ThreadGate.java0000644000175000017500000000553610676272670026257 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * Copyright 2005, JBoss Inc., and individual contributors as indicated * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.cache.util; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; /** * A reclosable gate with timeout support. * * @author Jason T. Greene */ public class ThreadGate { private final ReentrantLock lock = new ReentrantLock(); private final Condition condition = lock.newCondition(); private boolean open; private int sequence; /** * Open the gate. */ public void open() { lock.lock(); try { open = true; sequence++; condition.signalAll(); } finally { lock.unlock(); } } /** * Close the gate. */ public void close() { lock.lock(); try { open = false; } finally { lock.unlock(); } } /** * Waits for the gate to open. * * @throws InterruptedException if this thread is interrupted */ public void await() throws InterruptedException { lock.lock(); try { int snapshot = sequence; while (!open && snapshot == sequence) condition.await(); } finally { lock.unlock(); } } /** * Waits for the gate to open or the specified time to elapse. * * @param time the maximum time in milliseconds to wait. * @return false if gate timeout occurred * @throws InterruptedException if this thread is interrupted */ public boolean await(long time) throws InterruptedException { lock.lock(); try { int snapshot = sequence; boolean success = true; while (!open && snapshot == sequence && success) success = condition.await(time, TimeUnit.MILLISECONDS); return success; } finally { lock.unlock(); } } }jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/util/ImmutableListCopy.java0000644000175000017500000002507211044030206027623 0ustar twernertwernerpackage org.jboss.cache.util; import net.jcip.annotations.Immutable; import java.io.Externalizable; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.lang.reflect.Array; import java.util.AbstractList; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.ListIterator; import java.util.NoSuchElementException; /** * A lightweight, read-only copy of a List. Typically used in place of the common idiom: * * return Collections.unmodifiableList(new ArrayList( myInternalList )); * *

* a it is far more efficient than making a defensive copy and then wrapping the defensive copy in a read-only wrapper. *

* Also used whenever a read-only reference List is needed (such as in Fqns). *

* * @author Manik Surtani (manik@jboss.org) * @since 3.0 */ @Immutable public class ImmutableListCopy extends AbstractList implements Externalizable { private static final long serialVersionUID = 10929568968966L; private E[] elements; private int size; /** * Only one copy constructor since the list is immutable. * * @param c collection to copy from */ @SuppressWarnings("unchecked") public ImmutableListCopy(Collection c) { size = c.size(); Object[] el = new Object[size]; // no room for growth; el = c.toArray(el); elements = (E[]) el; } /** * Assumes that the array passed in is "safe", i.e., is not referenced from elsewhere. Use with care! * * @param array to reference */ public ImmutableListCopy(E[] array) { size = array.length; elements = array; } /** * Utility constructors to allow combining collections * * @param collection1 collection to copy from * @param collection2 collection to copy from */ @SuppressWarnings("unchecked") public ImmutableListCopy(Collection collection1, Collection collection2) { size = collection1.size() + collection2.size(); elements = (E[]) new Object[size]; // no room for growth; Object[] c1 = new Object[collection1.size()]; Object[] c2 = new Object[collection2.size()]; c1 = collection1.toArray(c1); c2 = collection2.toArray(c2); System.arraycopy(c1, 0, elements, 0, c1.length); System.arraycopy(c2, 0, elements, c1.length, c2.length); } @Override public final int size() { return size; } @Override public final boolean isEmpty() { return size == 0; } @Override public final boolean contains(Object o) { return indexOf(o) >= 0; } @Override public final Iterator iterator() { return new ImmutableIterator(); } @Override public final Object[] toArray() { Object[] result = new Object[size]; System.arraycopy(elements, 0, result, 0, size); return result; } @Override @SuppressWarnings("unchecked") public final T[] toArray(T[] a) { if (a.length < size) { a = (T[]) Array.newInstance(a.getClass().getComponentType(), size); } System.arraycopy(elements, 0, a, 0, size); if (a.length > size) a[size] = null; return a; } @Override public final boolean add(E o) { throw new UnsupportedOperationException(); } @Override public final boolean remove(Object o) { throw new UnsupportedOperationException(); } @Override public final boolean addAll(Collection c) { throw new UnsupportedOperationException(); } @Override public final boolean addAll(int index, Collection c) { throw new UnsupportedOperationException(); } @Override public final boolean removeAll(Collection c) { throw new UnsupportedOperationException(); } @Override public final boolean retainAll(Collection c) { throw new UnsupportedOperationException(); } public final E get(int index) { if (index >= size || index < 0) throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size); return elements[index]; } @Override public final int indexOf(Object o) { if (o == null) { for (int i = 0; i < size; i++) { if (elements[i] == null) return i; } } else { for (int i = 0; i < size; i++) { if (o.equals(elements[i])) return i; } } return -1; } @Override public final int lastIndexOf(Object o) { if (o == null) { for (int i = size - 1; i >= 0; i--) { if (elements[i] == null) return i; } } else { for (int i = size - 1; i >= 0; i--) { if (o.equals(elements[i])) return i; } } return -1; } @Override public final ListIterator listIterator() { return new ImmutableIterator(); } @Override public final ListIterator listIterator(int index) { return new ImmutableIterator(index); } @Override public final List subList(int fromIndex, int toIndex) { return new ImmutableSubList(fromIndex, toIndex); } /** * Format: * - entry array size (int) * - elements (Object) * * @param out stream to write to * @throws IOException */ public void writeExternal(ObjectOutput out) throws IOException { out.writeInt(size); for (E e : elements) out.writeObject(e); } /** * See {@link #writeExternal(java.io.ObjectOutput)} for serialization format * * @param in stream * @throws IOException * @throws ClassNotFoundException */ @SuppressWarnings("unchecked") public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { size = in.readInt(); elements = (E[]) new Object[size]; for (int i = 0; i < size; i++) elements[i] = (E) in.readObject(); } private class ImmutableIterator implements ListIterator { int cursor = 0; ImmutableIterator(int index) { if (index < 0 || index > size()) throw new IndexOutOfBoundsException("Index: " + index); cursor = index; } ImmutableIterator() { } public boolean hasNext() { return cursor != size; } public E next() { try { return get(cursor++); } catch (IndexOutOfBoundsException e) { throw new NoSuchElementException(); } } public void remove() { throw new UnsupportedOperationException(); } public boolean hasPrevious() { return cursor != 0; } public E previous() { try { return get(--cursor); } catch (IndexOutOfBoundsException e) { throw new NoSuchElementException(); } } public int nextIndex() { return cursor; } public int previousIndex() { return cursor - 1; } public void set(E o) { throw new UnsupportedOperationException(); } public void add(E o) { throw new UnsupportedOperationException(); } } private class ImmutableSubList extends AbstractList { private int offset; private int size; ImmutableSubList(int fromIndex, int toIndex) { if (fromIndex < 0 || toIndex > ImmutableListCopy.this.size || fromIndex > toIndex) throw new IllegalArgumentException("fromIndex(" + fromIndex + "), toIndex(" + toIndex + "), size (" + ImmutableListCopy.this.size + "), List=" + ImmutableListCopy.this.toString()); offset = fromIndex; size = toIndex - fromIndex; } @SuppressWarnings("unchecked") public final E get(int index) { if (index < 0 || index >= size) throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size); return (E) ImmutableListCopy.this.get(index + offset); } public final int size() { return size; } @Override protected final void removeRange(int fromIndex, int toIndex) { throw new UnsupportedOperationException(); } @Override public final boolean addAll(Collection c) { throw new UnsupportedOperationException(); } @Override public final boolean addAll(int index, Collection c) { throw new UnsupportedOperationException(); } @Override public final Iterator iterator() { return listIterator(); } @Override public final ListIterator listIterator(final int index) { if (index < 0 || (index != 0 && index >= size)) throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size); return new ListIterator() { private ListIterator i = ImmutableListCopy.this.listIterator(index + offset); public boolean hasNext() { return nextIndex() < size; } @SuppressWarnings("unchecked") public E next() { if (hasNext()) return (E) i.next(); else throw new NoSuchElementException(); } public boolean hasPrevious() { return previousIndex() >= 0; } @SuppressWarnings("unchecked") public E previous() { if (hasPrevious()) return (E) i.previous(); else throw new NoSuchElementException(); } public int nextIndex() { return i.nextIndex() - offset; } public int previousIndex() { return i.previousIndex() - offset; } public void remove() { throw new UnsupportedOperationException(); } public void set(E o) { throw new UnsupportedOperationException(); } public void add(E o) { throw new UnsupportedOperationException(); } }; } @Override public final List subList(int fromIndex, int toIndex) { return new ImmutableSubList(offset + fromIndex, offset + toIndex); } } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/util/MinMapUtil.java0000644000175000017500000000430510707457507026256 0ustar twernertwernerpackage org.jboss.cache.util; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; /** * Minimizes Map memory usage by changing the map * instance based on the number of stored entries. * * @author Elias Ross */ public class MinMapUtil { private static Class singleton = Collections.singletonMap(null, null).getClass(); private static Class empty = Collections.emptyMap().getClass(); private MinMapUtil() { } /** * Puts a mapping into a map, returns a map with the mapping. * @param map destination */ public static Map put(Map map, K key, V value) { int size = map.size(); if (size == 0) return Collections.singletonMap(key, value); if (size == 1) { HashMap map2 = new HashMap(map); map2.put(key, value); return map2; } else { map.put(key, value); return map; } } /** * Puts a number of entries into a map, returns a map. * @param dest destination map * @param src source map */ public static Map putAll(Map dest, Map src) { if (src == null) { return dest; } int srcSize = src.size(); if (srcSize == 0) { return dest; } if (srcSize == 1) { Entry next = src.entrySet().iterator().next(); return Collections.singletonMap(next.getKey(), next.getValue()); } Class c = dest.getClass(); if (c == empty || c == singleton) return new HashMap(src); dest.putAll(src); return dest; } /** * Removes a mapping by key from a map, returns the map. */ public static Map remove(Map map, K key) { int size = map.size(); if (size == 0) { return map; } if (size == 1) { if (map.containsKey(key)) return Collections.emptyMap(); else return map; } map.remove(key); return map; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/annotations/0000755000175000017500000000000011376174010024736 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/annotations/Experimental.java0000644000175000017500000000145211006366210030233 0ustar twernertwernerpackage org.jboss.cache.annotations; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * A marker annotation to mark that a class, method or field is experimental and as such, should be used with extreme * care as it may not be completely implemented, may not work at all, and/or may be prone to incompatible changes in * future releases. *

* * @author Manik Surtani (manik@jboss.org) * @since 2.2.0 */ @Retention(RetentionPolicy.SOURCE) @Target({ElementType.TYPE, ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.LOCAL_VARIABLE, ElementType.METHOD, ElementType.PACKAGE, ElementType.PARAMETER}) public @interface Experimental { } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/NodeNotExistsException.java0000644000175000017500000000151110660427364027677 0ustar twernertwerner// $Id: NodeNotExistsException.java 4252 2007-08-14 22:38:44Z jason.greene@jboss.com $ /* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache; /** * Thrown when an operation is attempted on a non-existing node in the cache * * @author Bela Ban. * @version $Id: NodeNotExistsException.java 4252 2007-08-14 22:38:44Z jason.greene@jboss.com $ */ public class NodeNotExistsException extends CacheException { private static final long serialVersionUID = 779376138690777440L; public NodeNotExistsException() { super(); } public NodeNotExistsException(String msg) { super(msg); } public NodeNotExistsException(String msg, Throwable cause) { super(msg, cause); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/Fqn.java0000644000175000017500000004772411043317625024010 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache; import net.jcip.annotations.Immutable; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.util.ImmutableListCopy; import java.io.Externalizable; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; /** * A Fully Qualified Name (Fqn) is a list of names (typically Strings but can be any Object), * which represent a path to a particular {@link Node} or sometimes a {@link Region} in a {@link Cache}. *

* This name can be absolute (i.e., relative from the root node - {@link #ROOT}), or relative to any node in the cache. Reading the * documentation on each API call that makes use of {@link org.jboss.cache.Fqn}s will tell you whether the API expects a * relative or absolute Fqn. *

* For instance, using this class to fetch a particular node might look like * this. (Here data on "Joe" is kept under the "Smith" surname node, under * the "people" tree.) *

 * Fqn abc = Fqn.fromString("/people/Smith/Joe/");
 * Node joesmith = Cache.getRoot().getChild(abc);
 * 
* Alternatively, the same Fqn could be constructed using an array: *
 * Fqn abc = Fqn.fromElements("people", "Smith", "Joe");
 * 
* This is a bit more efficient to construct. *

* Note that
*

* Fqn f = new Fqn("/a/b/c"); *

* is not the same as *

* Fqn f = Fqn.fromString("/a/b/c"); *

* The former will result in a single Fqn, called "/a/b/c" which hangs directly under Fqn.ROOT. *

* The latter will result in 3 Fqns, called "a", "b" and "c", where "c" is a child of "b", "b" is a child of "a", and "a" hangs off Fqn.ROOT. *

* Another way to look at it is that the "/" separarator is only parsed when it forms * part of a String passed in to Fqn.fromString() and not otherwise. *

* Best practices: Always creating Fqns - even when using some factory methods - can be expensive in the long run, * and as far as possible we recommend that client code holds on to their Fqn references and reuse them. E.g.: * * // BAD!! * for (int i=0; i * instead, do: * * // Much better * Fqn f = Fqn.fromString("/a/b/c"); * for (int i=0; i *

* NOTE public constructors of this class are deprectaed and will be removed in JBoss Cache 3.0.0. The * factory methods provided are the correct way get an instance of an Fqn, since these allow for numerous optimisations. *

* * @version $Revision: 6413 $ */ @Immutable public class Fqn implements Cloneable, Externalizable, Comparable> { /** * Separator between FQN elements. */ public static final String SEPARATOR = "/"; private static final long serialVersionUID = -5351930616956603651L; private static final Log log = LogFactory.getLog(Fqn.class); protected List elements; private transient int hash_code = 0; protected int size = 0; /** * Immutable root FQN. */ @SuppressWarnings("unchecked") public static final Fqn ROOT = new Fqn(true); /** * A cached string representation of this Fqn, used by toString to it isn't calculated again every time. */ protected String stringRepresentation; // ----------------- START: Private constructors for use by factory methods only. ---------------------- // TODO: 3.0.0: Remove the unnecessary internalMarker boolean parameters to these methods once the deprecated public constructors are removed in 3.0.0. protected Fqn(boolean internalMarker) { elements = Collections.emptyList(); size = 0; } @SuppressWarnings("unchecked") protected Fqn(boolean internalMarker, List names, boolean safe) { if (names != null) { // if not safe make a defensive copy elements = safe ? names : new ImmutableListCopy(names); size = elements.size(); } else { elements = Collections.emptyList(); size = 0; } } @SuppressWarnings("unchecked") protected Fqn(boolean internalMarker, Fqn base, List relative) { elements = new ImmutableListCopy(base.elements, relative); size = elements.size(); } // ----------------- END: Private constructors for use by factory methods only. ---------------------- /** * Constructs a root Fqn * * @deprecated use {@link #ROOT} instead. This constructor will be removed in 3.0.0. */ @Deprecated public Fqn() { this(true); } /** * Constructs a FQN from a list of names. * * @param names List of names * @deprecated use {@link #fromList(java.util.List)} instead. This constructor will be removed in 3.0.0. */ @Deprecated public Fqn(List names) { // the list is unsafe - may be referenced externally this(true, names, false); } /** * Retrieves an Fqn that represents the list of elements passed in. * * @param names list of elements that comprise the Fqn * @return an Fqn * @since 2.2.0 */ public static Fqn fromList(List names) { return new Fqn(true, names, false); } /** * Retrieves an Fqn that represents the list of elements passed in. * * @param names list of elements that comprise the Fqn * @param safe if true, the list passed in is not defensively copied but used directly. Use with care. Make sure * you know what you are doing before you pass in a true value to safe, as it can have adverse effects on * performance or correctness. The defensive copy of list elements is not just for safety but also for performance as * an appropriare List implementation is used, which works well with Fqn operations. * @return an Fqn */ public static Fqn fromList(List elements, boolean safe) { return new Fqn(true, elements, safe); } /** * If safe is false, Collections.unmodifiableList() is used to wrap the list passed in. This is an optimisation so * Fqn.fromString(), probably the most frequently used factory method, doesn't end up needing to use the unmodifiableList() * since it creates the list internally. * * @param names List of names * @param safe whether this list is referenced externally (safe = false) or not (safe = true). * @deprecated use {@link #fromList(java.util.List)} instead. The boolean "safety" hint is calculated internally. This constructor will be removed in 3.0.0. */ @Deprecated public Fqn(List names, boolean safe) { this(true, names, safe); } /** * Constructs a Fqn from an array of names. * * @param names Names that comprose this Fqn * @deprecated use {@link #fromElements(Object[])} instead. This constructor will be removed in 3.0.0. */ @Deprecated public Fqn(E... names) { // safe - the list is created here. this(true, Arrays.asList(names), true); } /** * Retrieves an Fqn that represents the array of elements passed in. * * @param elements array of elements that comprise the Fqn * @return an Fqn * @since 2.2.0 */ public static Fqn fromElements(E... elements) { return new Fqn(true, Arrays.asList(elements), true); } /** * Constructs a Fqn from a base and relative Fqn. * * @param base parent Fqn * @param relative Sub-Fqn relative to the parent * @deprecated use {@link #fromRelativeFqn(Fqn, Fqn)} instead. This constructor will be removed in 3.0.0. */ @Deprecated public Fqn(Fqn base, Fqn relative) { this(true, base, relative.elements); } /** * Retrieves an Fqn that represents the absolute Fqn of the relative Fqn passed in. * * @param base base Fqn * @param relative relative Fqn * @return an Fqn * @since 2.2.0 */ public static Fqn fromRelativeFqn(Fqn base, Fqn relative) { return new Fqn(true, base, relative.elements); } /** * Constructs a Fqn from a base and a list of relative names. * * @param base parent Fqn * @param relative List of elements that identify the child Fqn, relative to the parent * @deprecated use {@link #fromRelativeList(Fqn, java.util.List)} instead. This constructor will be removed in 3.0.0. */ @Deprecated public Fqn(Fqn base, List relative) { this(true, base, relative); } /** * Retrieves an Fqn that represents the List of elements passed in, relative to the base Fqn. * * @param base base Fqn * @param relativeElements relative List of elements * @return an Fqn * @since 2.2.0 */ public static Fqn fromRelativeList(Fqn base, List relativeElements) { return new Fqn(true, base, relativeElements); } /** * Constructs a Fqn from a base and two relative names. * * @param base parent Fqn * @param childNames elements that denote the path to the Fqn, under the parent * @deprecated use {@link #fromRelativeElements(Fqn, Object[])} instead. This constructor will be removed in 3.0.0. */ @Deprecated public Fqn(Fqn base, E... childNames) { this(true, base, Arrays.asList(childNames)); } /** * Retrieves an Fqn that represents the array of elements passed in, relative to the base Fqn. * * @param base base Fqn * @param relativeElements relative elements * @return an Fqn * @since 2.2.0 */ public static Fqn fromRelativeElements(Fqn base, Object... relativeElements) { return new Fqn(true, base, Arrays.asList(relativeElements)); } /** * Returns a new Fqn from a string, where the elements are deliminated by * one or more separator ({@link #SEPARATOR}) characters.

* Example use:
*
    * Fqn.fromString("/a/b/c/");
    * 

* is equivalent to:
*
    * new Fqn("a", "b", "c");
    * 

* but not
*
    * new Fqn("/a/b/c");
    * 
* * @param stringRepresentation String representation of the Fqn * @return an Fqn constructed from the string representation passed in * @see #Fqn(Object[]) */ @SuppressWarnings("unchecked") public static Fqn fromString(String stringRepresentation) { if (stringRepresentation == null || stringRepresentation.equals(SEPARATOR) || stringRepresentation.equals("")) return root(); String toMatch = stringRepresentation.startsWith(SEPARATOR) ? stringRepresentation.substring(1) : stringRepresentation; Object[] el = toMatch.split("/"); return new Fqn(new ImmutableListCopy(el), true); } /** * Retrieves an Fqn read from an object input stream, typically written to using {@link #writeExternal(java.io.ObjectOutput)}. * * @param in input stream * @return an Fqn * @throws IOException in the event of a problem reading the stream * @throws ClassNotFoundException in the event of classes that comprise the element list of this Fqn not being found * @since 2.2.0 */ public static Fqn fromExternalStream(ObjectInput in) throws IOException, ClassNotFoundException { Fqn f = new Fqn(true); f.readExternal(in); return f; } /** * Obtains an ancestor of the current Fqn. Literally performs elements.subList(0, generation) * such that if * * generation == Fqn.size() * * then the return value is the Fqn itself (current generation), and if * * generation == Fqn.size() - 1 * * then the return value is the same as * * Fqn.getParent() * * i.e., just one generation behind the current generation. * * generation == 0 * * would return Fqn.ROOT. * * @param generation the generation of the ancestor to retrieve * @return an ancestor of the current Fqn */ public Fqn getAncestor(int generation) { if (generation == 0) return root(); return getSubFqn(0, generation); } /** * Obtains a sub-Fqn from the given Fqn. Literally performs elements.subList(startIndex, endIndex) */ public Fqn getSubFqn(int startIndex, int endIndex) { List el = elements.subList(startIndex, endIndex); return new Fqn(true, el, true); } /** * @return the number of elements in the Fqn. The root node contains zero. */ public int size() { return size; } /** * @param n index of the element to return * @return Returns the nth element in the Fqn. */ public E get(int n) { return elements.get(n); } /** * @return the last element in the Fqn. * @see #getLastElementAsString */ public E getLastElement() { if (isRoot()) return null; return elements.get(size - 1); } /** * @param element element to find * @return true if the Fqn contains this element, false otherwise. */ public boolean hasElement(E element) { return elements.indexOf(element) != -1; } /** * Clones the Fqn. */ @Override @SuppressWarnings("unchecked") public Fqn clone() throws CloneNotSupportedException { try { return (Fqn) super.clone(); } catch (CloneNotSupportedException e) { log.error("Unable to clone Fqn " + this, e); return null; } } /** * Returns true if obj is a Fqn with the same elements. */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!(obj instanceof Fqn)) { return false; } Fqn other = (Fqn) obj; return size == other.size() && elements.equals(other.elements); } /** * Returns a hash code with Fqn elements. */ @Override public int hashCode() { if (hash_code == 0) { hash_code = calculateHashCode(); } return hash_code; } /** * Returns this Fqn as a string, prefixing the first element with a {@link Fqn#SEPARATOR} and * joining each subsequent element with a {@link Fqn#SEPARATOR}. * If this is the root Fqn, returns {@link Fqn#SEPARATOR}. * Example: *
    * new Fqn(new Object[] { "a", "b", "c" }).toString(); // "/a/b/c"
    * Fqn.ROOT.toString(); // "/"
    * 
*/ @Override public String toString() { if (stringRepresentation == null) { stringRepresentation = getStringRepresentation(elements); } return stringRepresentation; } public void writeExternal(ObjectOutput out) throws IOException { out.writeShort(size); for (E element : elements) { out.writeObject(element); } } @SuppressWarnings("unchecked") public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { size = in.readShort(); this.elements = new ArrayList(size); for (int i = 0; i < size; i++) { E e = (E) in.readObject(); elements.add(e); } } /** * Returns true if this Fqn is child of parentFqn. * Example usage: *
    * Fqn f1 = Fqn.fromString("/a/b");
    * Fqn f2 = Fqn.fromString("/a/b/c");
    * assertTrue(f1.isChildOf(f2));
    * assertFalse(f1.isChildOf(f1));
    * assertFalse(f2.isChildOf(f1));
    * 
* * @param parentFqn candidate parent to test against * @return true if the target is a child of parentFqn */ public boolean isChildOf(Fqn parentFqn) { return parentFqn.size() != size && isChildOrEquals(parentFqn); } /** * Returns true if this Fqn is a direct child of a given Fqn. * * @param parentFqn parentFqn to compare with * @return true if this is a direct child, false otherwise. */ public boolean isDirectChildOf(Fqn parentFqn) { return size == parentFqn.size() + 1 && isChildOf(parentFqn); } /** * Returns true if this Fqn is equals or the child of parentFqn. * Example usage: *
    * Fqn f1 = Fqn.fromString("/a/b");
    * Fqn f2 = Fqn.fromString("/a/b/c");
    * assertTrue(f1.isChildOrEquals(f2));
    * assertTrue(f1.isChildOrEquals(f1));
    * assertFalse(f2.isChildOrEquals(f1));
    * 
* * @param parentFqn candidate parent to test against * @return true if this Fqn is equals or the child of parentFqn. */ public boolean isChildOrEquals(Fqn parentFqn) { List parentList = parentFqn.elements; if (parentList.size() > size) { return false; } for (int i = parentList.size() - 1; i >= 0; i--) { if (!parentList.get(i).equals(elements.get(i))) { return false; } } return true; } /** * Calculates a hash code by summing the hash code of all elements. * * @return a cached hashcode */ protected int calculateHashCode() { int hashCode = 0; int count = 1; Object o; for (E element : elements) { o = element; hashCode += (o == null) ? 0 : o.hashCode() * count++; } if (hashCode == 0)// fix degenerate case { hashCode = 0xFEED; } return hashCode; } protected String getStringRepresentation(List elements) { StringBuilder builder = new StringBuilder(); for (Object e : elements) { // incase user element 'e' does not implement equals() properly, don't rely on their implementation. if (!SEPARATOR.equals(e) && !"".equals(e)) { builder.append(SEPARATOR); builder.append(e); } } return builder.length() == 0 ? SEPARATOR : builder.toString(); } /** * Returns the parent of this Fqn. * The parent of the root node is {@link #ROOT}. * Examples: *
    * Fqn f1 = Fqn.fromString("/a");
    * Fqn f2 = Fqn.fromString("/a/b");
    * assertEquals(f1, f2.getParent());
    * assertEquals(Fqn.ROOT, f1.getParent().getParent());
    * assertEquals(Fqn.ROOT, Fqn.ROOT.getParent());
    * 
* * @return the parent Fqn */ public Fqn getParent() { switch (size) { case 0: case 1: return root(); default: return new Fqn(true, elements.subList(0, size - 1), true); } } @SuppressWarnings("unchecked") public static Fqn root() { return ROOT; } /** * Returns true if this is a root Fqn. * * @return true if the Fqn is Fqn.ROOT. */ public boolean isRoot() { return size == 0; } /** * If this is the root, returns {@link Fqn#SEPARATOR}. * * @return a String representation of the last element that makes up this Fqn. */ public String getLastElementAsString() { if (isRoot()) { return SEPARATOR; } else { Object last = getLastElement(); if (last instanceof String) return (String) last; else return String.valueOf(getLastElement()); } } /** * Peeks into the elements that build up this Fqn. The list returned is * read-only, to maintain the immutable nature of Fqn. * * @return an unmodifiable list */ public List peekElements() { return elements; } /** * Compares this Fqn to another using {@link FqnComparator}. */ public int compareTo(Fqn Fqn) { return FqnComparator.INSTANCE.compare(this, Fqn); } }jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/StringFqn.java0000644000175000017500000000610111012441070025143 0ustar twernertwernerpackage org.jboss.cache; import org.jboss.cache.annotations.Experimental; import java.util.Arrays; import java.util.List; /** * An optimisation of Fqn that does more efficient equals() and hashcode() computations. This is returned by default when * the factory method {@link Fqn#fromString(String)} is used, or when any of the other factory methods on {@link Fqn} are * passed only String elements. *

* Note that the "/" character is illegal in any Fqn String element and if encountered may be used to split Fqn elements. * Expect indeterminate behaviour until proper String escaping is in place. *

* * @author Manik Surtani (manik@jboss.org) * @since 2.2.0 */ // TODO: 3.0.0: Implement proper String escaping. @Experimental public final class StringFqn extends Fqn { // Needs to be public because of NodeData serialization. // TODO: Remove in 3.0.0 once we refactor NodeData to go through a cache marshaller instead of it's current serialization. public StringFqn() { super(true); stringRepresentation = SEPARATOR; } protected StringFqn(StringFqn base, List elements) { super(true, base, elements); String elementStringRep = getStringRepresentation(elements); stringRepresentation = base.isRoot() ? elementStringRep : base.stringRepresentation + elementStringRep; } protected StringFqn(StringFqn base, StringFqn relative) { super(true, base, relative.elements); if (base.isRoot()) { if (relative.isRoot()) stringRepresentation = SEPARATOR; else stringRepresentation = relative.stringRepresentation; } else { if (relative.isRoot()) stringRepresentation = base.stringRepresentation; else stringRepresentation = base.stringRepresentation + relative.stringRepresentation; } } protected StringFqn(List stringElements) { super(true, stringElements, false); stringRepresentation = getStringRepresentation(elements); } protected StringFqn(String stringRep) { this(Arrays.asList(stringRep.split("/"))); } @Override public boolean equals(Object other) { if (other == this) return true; if (other == null) return false; if (other.getClass().equals(StringFqn.class)) { return stringRepresentation.equals(((StringFqn) other).stringRepresentation); } else { return super.equals(other); } } @Override protected int calculateHashCode() { return stringRepresentation.hashCode(); } @Override public boolean isChildOrEquals(Fqn parentFqn) { if (parentFqn.getClass().equals(StringFqn.class)) { StringFqn stringParentFqn = (StringFqn) parentFqn; return stringRepresentation.startsWith(stringParentFqn.stringRepresentation); } else { return super.isChildOrEquals(parentFqn); } } public String getStringRepresentation() { return stringRepresentation; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/cluster/0000755000175000017500000000000011376174005024066 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/cluster/ReplicationQueue.java0000644000175000017500000001075611032145203030203 0ustar twernertwernerpackage org.jboss.cache.cluster; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.RPCManager; import org.jboss.cache.commands.ReplicableCommand; import org.jboss.cache.commands.remote.ReplicateCommand; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.CommandsFactory; import org.jboss.cache.factories.annotations.Inject; import org.jboss.cache.factories.annotations.Start; import org.jboss.cache.factories.annotations.Stop; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Timer; import java.util.TimerTask; /** * Periodically (or when certain size is exceeded) takes elements and replicates them. * * @author Bela Ban May 24, 2003 * @version $Revision: 5197 $ */ public class ReplicationQueue { private static final Log log = LogFactory.getLog(ReplicationQueue.class); /** * We flush every 5 seconds. Inactive if -1 or 0 */ private long interval = 5000; /** * Max elements before we flush */ private long max_elements = 500; /** * Holds the replication jobs: LinkedList */ final List elements = new LinkedList(); /** * For periodical replication */ private Timer timer = null; /** * The timer task, only calls flush() when executed by Timer */ private ReplicationQueue.MyTask task = null; private RPCManager rpcManager; private Configuration configuration; private boolean enabled; private CommandsFactory commandsFactory; public boolean isEnabled() { return enabled; } @Inject private void injectDependencies(RPCManager rpcManager, Configuration configuration, CommandsFactory commandsFactory) { this.rpcManager = rpcManager; this.configuration = configuration; this.commandsFactory = commandsFactory; // this is checked again in Start enabled = configuration.isUseReplQueue() && (configuration.getBuddyReplicationConfig() == null || !configuration.getBuddyReplicationConfig().isEnabled()); } /** * Starts the asynchronous flush queue. */ @Start public synchronized void start() { this.interval = configuration.getReplQueueInterval(); this.max_elements = configuration.getReplQueueMaxElements(); // check again enabled = configuration.isUseReplQueue() && (configuration.getBuddyReplicationConfig() == null || !configuration.getBuddyReplicationConfig().isEnabled()); if (enabled && interval > 0) { if (task == null) task = new ReplicationQueue.MyTask(); if (timer == null) { timer = new Timer(true); timer.schedule(task, 500, // delay before initial flush interval); // interval between flushes } } } /** * Stops the asynchronous flush queue. */ @Stop public synchronized void stop() { if (task != null) { task.cancel(); task = null; } if (timer != null) { timer.cancel(); timer = null; } } /** * Adds a new method call. */ public void add(ReplicateCommand job) { if (job == null) throw new NullPointerException("job is null"); synchronized (elements) { elements.add(job); if (elements.size() >= max_elements) flush(); } } /** * Flushes existing method calls. */ public void flush() { List toReplicate; synchronized (elements) { if (log.isTraceEnabled()) log.trace("flush(): flushing repl queue (num elements=" + elements.size() + ")"); toReplicate = new ArrayList(elements); elements.clear(); } if (toReplicate.size() > 0) { try { ReplicateCommand replicateCommand = commandsFactory.buildReplicateCommand(toReplicate); // send to all live nodes in the cluster rpcManager.callRemoteMethods(null, replicateCommand, false, configuration.getSyncReplTimeout(), false); } catch (Throwable t) { log.error("failed replicating " + toReplicate.size() + " elements in replication queue", t); } } } class MyTask extends TimerTask { @Override public void run() { flush(); } } }jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/CacheManagerImpl.java0000755000175000017500000001734111010550166026370 0ustar twernertwerner/* * Copyright (c) 2007, Red Hat Middleware, LLC. All rights reserved. * * This copyrighted material is made available to anyone wishing to use, modify, * copy, or redistribute it subject to the terms and conditions of the GNU * Lesser General Public License, v. 2.1. This program is distributed in the * hope that it will be useful, but WITHOUT A WARRANTY; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. You should have received a * copy of the GNU Lesser General Public License, v.2.1 along with this * distribution; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * Red Hat Author(s): Brian Stansberry */ package org.jboss.cache; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.ConfigurationRegistry; import org.jboss.cache.config.XmlParsingConfigurationRegistry; import org.jgroups.ChannelFactory; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; /** * Basic implementation of {@link CacheManager}. * * @author Brian Stansberry * @version $Revision: 1 $ */ public class CacheManagerImpl implements CacheManager { private ConfigurationRegistry configRegistry; private boolean configRegistryInjected; private final Map> caches = new HashMap>(); private final Map checkouts = new HashMap(); private ChannelFactory channelFactory; private boolean started; /** * Create a new CacheRegistryImpl. */ public CacheManagerImpl() { } /** * Create a new CacheRegistryImpl using the provided ConfigurationRegistry * and ChannelFactory. */ public CacheManagerImpl(ConfigurationRegistry configRegistry, ChannelFactory factory) { this.configRegistry = configRegistry; this.configRegistryInjected = true; this.channelFactory = factory; } /** * Create a new CacheRegistryImpl using the provided ChannelFactory and * using the provided file name to create an * {@link XmlParsingConfigurationRegistry}. */ public CacheManagerImpl(String configFileName, ChannelFactory factory) { configRegistry = new XmlParsingConfigurationRegistry(configFileName); this.channelFactory = factory; } // ---------------------------------------------------------- CacheRegistry public ChannelFactory getChannelFactory() { return channelFactory; } @SuppressWarnings("unchecked") public Set getConfigurationNames() { synchronized (caches) { Set configNames = configRegistry == null ? new HashSet() : configRegistry.getConfigurationNames(); configNames.addAll(getCacheNames()); return configNames; } } public Set getCacheNames() { synchronized (caches) { return new HashSet(caches.keySet()); } } public Cache getCache(String configName, boolean create) throws Exception { Cache cache; synchronized (caches) { cache = caches.get(configName); if (cache == null && create) { Configuration config = configRegistry.getConfiguration(configName); if (channelFactory != null && config.getMultiplexerStack() != null) { config.getRuntimeConfig().setMuxChannelFactory(channelFactory); } cache = createCache(config); registerCache(cache, configName); } else if (cache != null) { incrementCheckout(configName); } } return cache; } /** * Extension point for subclasses, where we actually use a * {@link CacheFactory} to create a cache. This default implementation * uses {@link DefaultCacheFactory}. * * @param config the Configuration for the cache * @return the Cache */ @SuppressWarnings("unchecked") protected Cache createCache(Configuration config) { return new DefaultCacheFactory().createCache(config, false); } public void releaseCache(String configName) { synchronized (caches) { if (!caches.containsKey(configName)) throw new IllegalStateException(configName + " not registered"); if (decrementCheckout(configName) == 0) { Cache cache = caches.remove(configName); destroyCache(cache); } } } // ----------------------------------------------------------------- Public public ConfigurationRegistry getConfigurationRegistry() { return configRegistry; } public void setConfigurationRegistry(ConfigurationRegistry configRegistry) { this.configRegistry = configRegistry; this.configRegistryInjected = true; } public void setChannelFactory(ChannelFactory channelFactory) { this.channelFactory = channelFactory; } public void registerCache(Cache cache, String configName) { synchronized (caches) { if (caches.containsKey(configName)) throw new IllegalStateException(configName + " already registered"); caches.put(configName, cache); incrementCheckout(configName); } } public void start() throws Exception { if (!started) { if (configRegistry == null) throw new IllegalStateException("Must configure a ConfigurationRegistry before calling start()"); if (channelFactory == null) throw new IllegalStateException("Must provide a ChannelFactory before calling start()"); if (!configRegistryInjected) { ((XmlParsingConfigurationRegistry) configRegistry).start(); } started = true; } } public void stop() { if (started) { synchronized (caches) { for (Iterator>> it = caches.entrySet().iterator(); it.hasNext();) { Map.Entry> entry = it.next(); destroyCache(entry.getValue()); it.remove(); } caches.clear(); checkouts.clear(); } if (!configRegistryInjected) { ((XmlParsingConfigurationRegistry) configRegistry).stop(); } started = false; } } // ---------------------------------------------------------------- Private private int incrementCheckout(String configName) { synchronized (checkouts) { Integer count = checkouts.get(configName); if (count == null) count = 0; Integer newVal = count + 1; checkouts.put(configName, newVal); return newVal; } } private int decrementCheckout(String configName) { synchronized (checkouts) { Integer count = checkouts.get(configName); if (count == null || count < 1) throw new IllegalStateException("invalid count of " + count + " for " + configName); Integer newVal = count - 1; checkouts.put(configName, newVal); return newVal; } } private void destroyCache(Cache cache) { if (cache.getCacheStatus() == CacheStatus.STARTED) { cache.stop(); } if (cache.getCacheStatus() != CacheStatus.DESTROYED && cache.getCacheStatus() != CacheStatus.INSTANTIATED) { cache.destroy(); } } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/DataContainer.java0000644000175000017500000001762611026046072025772 0ustar twernertwernerpackage org.jboss.cache; import org.jboss.cache.marshall.NodeData; import org.jboss.cache.optimistic.DataVersion; import org.jboss.cache.transaction.GlobalTransaction; import java.util.List; import java.util.Set; /** * This class defines the functionality needed for node manipulation. * * @author Mircea.Markus@jboss.com * @see DataContainerImpl * @since 2.2 */ public interface DataContainer { /** * Retrieves the root node. * * @return the root node */ NodeSPI getRoot(); /** * Adds the specified Fqn to the list of Fqns to be considered "internal". * * @param fqn fqn to add to list */ void registerInternalFqn(Fqn fqn); /** * Finds a node given a fully qualified name, directly off the interceptor chain. In the event of an exception, * returns null. Does not include invalid or deleted nodes. * * @param fqn Fully qualified name for the corresponding node. * @return Node referenced by the given Fqn, or null if the node cannot be found or if there is an exception. */ NodeSPI peek(Fqn fqn); /** * Finds a node given a fully qualified name and DataVersion. Does not include invalid or deleted nodes. If the data * version passed in is null, then data version checking is skipped. Data version checking is also skipped if optimistic * locking is not used. * * @param fqn fqn to find * @param version version of the node to find * @return a node, if found, or null if not. */ NodeSPI peekVersioned(Fqn fqn, DataVersion version); /** * Similar to {@link #peekVersioned(Fqn, org.jboss.cache.optimistic.DataVersion)} except that it throws a {@link org.jboss.cache.NodeNotExistsException} * if the node cannot be found. * * @param gtx global transaction * @param fqn fqn to find * @param includeInvalid if true, invalid nodes are considered as well. * @return the node */ NodeSPI peekStrict(GlobalTransaction gtx, Fqn fqn, boolean includeInvalid); /** * Searches for a specific node, with a specific data version and, optionally, invalid nodes as well, but not * deleted nodes. If the data version passed in is null, then data version checking is skipped. Data version * checking is also skipped if optimistic locking is not used. * * @param fqn Fqn to find * @param version version of the node to find * @param includeInvalidNodes if true, invalid nodes are considered * @return the node, if found, or null otherwise. */ NodeSPI peekVersioned(Fqn fqn, DataVersion version, boolean includeInvalidNodes); /** * Same as calling peek(fqn, includeDeletedNodes, false). * * @param fqn Fqn to find * @param includeDeletedNodes if true, deleted nodes are considered * @return the node, if found, or null otherwise. */ NodeSPI peek(Fqn fqn, boolean includeDeletedNodes); /** * Peeks for a specified node. This involves a direct walk of the tree, starting at the root, until the required node * is found. If the node is not found, a null is returned. * * @param fqn Fqn of the node to find * @param includeDeletedNodes if true, deleted nodes are also considered * @param includeInvalidNodes if true, invalid nodes are also considered * @return the node, if found, or null otherwise. */ NodeSPI peek(Fqn fqn, boolean includeDeletedNodes, boolean includeInvalidNodes); /** * Tests if an Fqn exists and is valid and not deleted. * * @param fqn the fqn representing the node to test * @return true if the node exists, false otherwise. */ boolean exists(Fqn fqn); /** * Returns true if the Fqn exists, is valid and is not deleted, and the node has children. * * @param fqn the fqn to test * @return true if the Fqn exists, is valid and is not deleted, and the node has children. */ boolean hasChildren(Fqn fqn); /** * Prepares a list of {@link NodeData} objects for a specified node and all its children. * * @param list List of NodeData objects, which will be added to. * @param node node to recursively add to the list * @return the same list passed in */ List buildNodeData(List list, NodeSPI node); /** * Generates a list of nodes for eviction. This filters out nodes that cannot be evicted, such as those which are * marked as resident. See {@link NodeSPI#setResident(boolean)}. * * @param fqn the node to consider for eviction * @param recursive if recursive, child nodes are also considered * @return a list of Fqns that can be considered for eviction */ List getNodesForEviction(Fqn fqn, boolean recursive); /** * Returns a Set of Fqns of the topmost node of internal regions that * should not included in standard state transfers. Will include * {@link org.jboss.cache.buddyreplication.BuddyManager#BUDDY_BACKUP_SUBTREE} if buddy replication is * enabled. * * @return an unmodifiable Set. Will not return null. */ Set getInternalFqns(); /** * Returns the number of read or write locks held across the entire cache. */ int getNumberOfLocksHeld(); /** * Returns an approximation of the total number of nodes in the * cache. Since this method doesn't acquire any locks, the number might be * incorrect, or the method might even throw a * ConcurrentModificationException */ int getNumberOfNodes(); /** * Returns an approximation of the total number of attributes in * this sub cache. */ int getNumberOfAttributes(Fqn fqn); /** * Returns an approximation of the total number of attributes in * the cache. Since this method doesn't acquire any locks, the number might * be incorrect, or the method might even throw a * ConcurrentModificationException * * @return number of attribs */ int getNumberOfAttributes(); /** * Removes the actual node from the tree data structure. * * @param f the Fqn of the node to remove * @param skipMarkerCheck if true, skips checking the boolean {@link org.jboss.cache.NodeSPI#isDeleted()} flag and deletes the node anyway. * @return Returns true if the node was found and removed, false if not. */ boolean removeFromDataStructure(Fqn f, boolean skipMarkerCheck); /** * Evicts the given node. If recursive is set to true then all child nodes are recusively evicted. */ void evict(Fqn fqn, boolean recursive); /** *

    * Following scenarios define how eviction works.
    * 1. If the given node is a leaf then it is entirely removed from the data structure. The node is marked as invalid.
    * 2. If the given node is a leaf then only the data map is cleared.
    * 3. If the given node is the root node then the cildren nodes are evicted. For each child node 1. or 2. applies
    * 
* * @return true if the FQN is leaf and was removed; false if is an intermediate FQN and only contained data * is droped. */ boolean evict(Fqn fqn); /** * Traverses the tree to the given Fqn, creating nodes if needed. Returns a list of nodes created, as well as a reference to the last node. *

* E.g., * * Object[] results = createNode(myFqn); * results[0] // this is a List<NodeSPI> of nodes created in getting to the target node. * results[1] // is a NodeSPI reference to the target node, regardless of whether it was created or just found. * * * @param fqn fqn to find * @return see above. */ Object[] createNodes(Fqn fqn); } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/CacheStatus.java0000644000175000017500000001402311075463443025462 0ustar twernertwerner/* * JBoss, Home of Professional Open Source. * Copyright 2006, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.cache; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * Various states that an object that has a four stage lifecycle * (i.e. create(), start(), stop() * and destroy()) might be in. * * @author Brian Stansberry * @version $Revision: 6966 $ */ public enum CacheStatus { /** * Object has been instantiated, but create() has not been called. */ INSTANTIATED, /** * The create() method has been called but not yet completed. */ CREATING, /** * The create() method has been completed but * start() has not been called. */ CREATED, /** * The start() method has been called but has not yet completed. */ STARTING, /** * The start() method has completed. */ STARTED, /** * The stop() method has been called but has not yet completed. */ STOPPING, /** * The stop() method has completed but destroy() * has not yet been called. Conceptually equivalent to {@link #CREATED}. */ STOPPED, /** * The destroy() method has been called but has not yet completed. */ DESTROYING, /** * The destroy() method has completed. * Conceptually equivalent to {@link #INSTANTIATED}. */ DESTROYED, /** * A failure occurred during the execution of create(), * start(), stop() or destroy(). * The next logical transition is to call destroy(). */ FAILED; private static final Log log = LogFactory.getLog(CacheStatus.class); public boolean createAllowed() { switch (this) { case CREATING: case CREATED: case STARTING: case STARTED: case STOPPED: log.debug("Ignoring call to create() as current state is " + this); // fall through case FAILED: return false; case STOPPING: case DESTROYING: log.warn("Ignoring call to create() while cache is " + this); return false; case INSTANTIATED: case DESTROYED: default: return true; } } public boolean needToDestroyFailedCache() { if (this == CacheStatus.FAILED) { log.debug("need to call destroy() since current state is " + this); return true; } return false; } public boolean startAllowed() { switch (this) { case INSTANTIATED: case DESTROYED: case STARTING: case STARTED: log.debug("Ignoring call to start() as current state is " + this); // fall through case FAILED: return false; case STOPPING: case DESTROYING: log.warn("Ignoring call to start() as current state is " + this); return false; case CREATED: case STOPPED: default: return true; } } public boolean needCreateBeforeStart() { switch (this) { case INSTANTIATED: case DESTROYED: log.debug("start() called while current state is " + this + " -- call create() first"); return true; default: return false; } } public boolean stopAllowed() { switch (this) { case INSTANTIATED: case CREATED: case STOPPING: case STOPPED: case DESTROYED: log.debug("Ignoring call to stop() as current state is " + this); return false; case CREATING: case STARTING: case DESTROYING: log.warn("Ignoring call to stop() as current state is " + this); return false; case FAILED: case STARTED: default: return true; } } public boolean destroyAllowed() { switch (this) { case INSTANTIATED: case DESTROYING: case DESTROYED: log.debug("Ignoring call to destroy() as current state is " + this); return false; case CREATING: case STARTING: case STOPPING: log.warn("Ignoring call to destroy() as current state is " + this); return false; case STARTED: // stop first return false; case CREATED: case STOPPED: case FAILED: default: return true; } } public boolean needStopBeforeDestroy() { if (this == CacheStatus.STARTED) { log.warn("destroy() called while current state is " + this + " -- call stop() first"); return true; } return false; } public boolean allowInvocations() { return (this == CacheStatus.STARTED); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/VersionedNode.java0000644000175000017500000000615011012615735026013 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache; import org.apache.commons.logging.LogFactory; import org.jboss.cache.lock.IdentityLock; import org.jboss.cache.optimistic.DataVersion; import org.jboss.cache.optimistic.DefaultDataVersion; import java.util.Map; /** * VersionedNode extends the {@link org.jboss.cache.UnversionedNode} by adding a {@link org.jboss.cache.optimistic.DataVersion} property. *

* Unlike {@link org.jboss.cache.UnversionedNode}, this node supports {@link #getVersion} and {@link #setVersion(org.jboss.cache.optimistic.DataVersion)} * defined in {@link org.jboss.cache.NodeSPI} *

* Typically used when the cache mode configured is {@link org.jboss.cache.config.Configuration.NodeLockingScheme#OPTIMISTIC} * * @author Manik Surtani (manik@jboss.org) * @since 2.0.0 */ public class VersionedNode extends UnversionedNode { private static final String DATA_VERSION_INTERNAL_KEY = "_JBOSS_INTERNAL_OPTIMISTIC_DATA_VERSION"; private DataVersion version; // make sure this is NOT initialized to anything, even a null! Since the UnversionedNode constructor may set this value based on a data version passed along in the data map. /** * Although this object has a reference to the CacheImpl, the optimistic * node is actually disconnected from the CacheImpl itself. * The parent could be looked up from the TransactionWorkspace. */ private NodeSPI parent; protected VersionedNode(Fqn fqn, NodeSPI parent, Map data, CacheSPI cache) { super(fqn.getLastElement(), fqn, data, false, cache); if (version == null) version = DefaultDataVersion.ZERO; if (parent == null && !fqn.isRoot()) throw new NullPointerException("parent"); this.parent = parent; log = LogFactory.getLog(VersionedNode.class); } /** * Returns the version id of this node. * * @return the version */ @Override public DataVersion getVersion() { return version; } /** * Returns the parent. */ @Override public NodeSPI getParent() { return parent; } /** * Sets the version id of this node. * * @param version */ @Override public void setVersion(DataVersion version) { this.version = version; } /** * Optimistically locked nodes (VersionedNodes) will always use repeatable read. */ @Override protected synchronized void initLock() { if (lock == null) lock = new IdentityLock(lockStrategyFactory, delegate); } @Override public Map getInternalState(boolean onlyInternalState) { Map state = super.getInternalState(onlyInternalState); state.put(DATA_VERSION_INTERNAL_KEY, version); return state; } @Override public void setInternalState(Map state) { if (state != null) { DataVersion dv = (DataVersion) state.remove(DATA_VERSION_INTERNAL_KEY); if (dv != null) version = dv; } super.setInternalState(state); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/FqnComparator.java0000644000175000017500000000455011006372120026014 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache; import net.jcip.annotations.Immutable; import java.util.Comparator; /** * Compares the order of two FQN. * Sorts by name, then by depth, e.g. *

 * aaa/bbb
 * xxx
 * xxx/ccc
 * 
* * @author Manik Surtani (manik@jboss.org) * @author Steve Woodcock (stevew@jofti.com) */ @Immutable public class FqnComparator implements Comparator { public static final FqnComparator INSTANCE = new FqnComparator(); /** * Returns -1 if the first comes before; 0 if they are the same; 1 if the * second Fqn comes before. null always comes first. */ public int compare(Fqn fqn1, Fqn fqn2) { int s1 = fqn1.size(); int s2 = fqn2.size(); if (s1 == 0) { return (s2 == 0) ? 0 : -1; } if (s2 == 0) { return 1; } // if (fqn1.getClass().equals(StringFqn.class) && fqn2.getClass().equals(StringFqn.class)) // { // StringFqn sfqn1 = (StringFqn) fqn1; // StringFqn sfqn2 = (StringFqn) fqn2; // return sfqn1.stringRepresentation.compareTo(sfqn2.stringRepresentation); // } int size = Math.min(s1, s2); for (int i = 0; i < size; i++) { Object e1 = fqn1.get(i); Object e2 = fqn2.get(i); if (e1 == e2) { continue; } if (e1 == null) { return 0; } if (e2 == null) { return 1; } if (!e1.equals(e2)) { int c = compareElements(e1, e2); if (c != 0) { return c; } } } return s1 - s2; } /** * Compares two Fqn elements. * If e1 and e2 are the same class and e1 implements Comparable, * returns e1.compareTo(e2). * Otherwise, returns e1.toString().compareTo(e2.toString()). */ private int compareElements(Object e1, Object e2) { if (e1.getClass() == e2.getClass() && e1 instanceof Comparable) { return ((Comparable) e1).compareTo(e2); } else { return e1.toString().compareTo(e2.toString()); } } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/NodeNotValidException.java0000644000175000017500000000114210707226542027454 0ustar twernertwernerpackage org.jboss.cache; /** * Thrown whenever operations are attempted on a node that is no longer valid. See {@link org.jboss.cache.Node#isValid()} * for details. * * @author Manik Surtani * @since 2.1.0 */ public class NodeNotValidException extends CacheException { public NodeNotValidException() { } public NodeNotValidException(Throwable cause) { super(cause); } public NodeNotValidException(String msg) { super(msg); } public NodeNotValidException(String msg, Throwable cause) { super(msg, cause); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/remoting/0000755000175000017500000000000011376174006024232 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/remoting/jgroups/0000755000175000017500000000000011376174006025723 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/remoting/jgroups/ChannelMessageListener.java0000644000175000017500000002426511075062420033152 0ustar twernertwernerpackage org.jboss.cache.remoting.jgroups; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.CacheException; import org.jboss.cache.Fqn; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.annotations.Inject; import org.jboss.cache.factories.annotations.NonVolatile; import org.jboss.cache.io.ExposedByteArrayOutputStream; import org.jboss.cache.statetransfer.StateTransferManager; import org.jboss.util.stream.MarshalledValueInputStream; import org.jboss.util.stream.MarshalledValueOutputStream; import org.jgroups.ExtendedMessageListener; import org.jgroups.Message; import org.jgroups.util.Util; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.io.OutputStream; /** * JGroups MessageListener * * @author Manik Surtani (manik@jboss.org) * @since 2.1.0 */ @NonVolatile public class ChannelMessageListener implements ExtendedMessageListener { /** * Reference to an exception that was raised during * state installation on this node. */ protected volatile Exception setStateException; private final Object stateLock = new Object(); private Log log = LogFactory.getLog(ChannelMessageListener.class); private StateTransferManager stateTransferManager; private Configuration configuration; /** * True if state was initialized during start-up. */ private volatile boolean isStateSet = false; @Inject private void injectDependencies(StateTransferManager stateTransferManager, Configuration configuration) { this.stateTransferManager = stateTransferManager; this.configuration = configuration; } public boolean isStateSet() { return isStateSet; } public void setStateSet(boolean stateSet) { isStateSet = stateSet; } public void waitForState() throws Exception { synchronized (stateLock) { while (!isStateSet) { if (setStateException != null) { throw setStateException; } try { stateLock.wait(); } catch (InterruptedException iex) { } } } } protected void stateReceivedSuccess() { isStateSet = true; setStateException = null; } protected void stateReceivingFailed(Throwable t) { if (t instanceof CacheException) { log.debug(t); } else { log.error("failed setting state", t); } if (t instanceof Exception) { setStateException = (Exception) t; } else { setStateException = new Exception(t); } } protected void stateProducingFailed(Throwable t) { if (t instanceof CacheException) { log.debug(t); } else { log.error("Caught " + t.getClass().getName() + " while responding to state transfer request", t); } } /** * Callback, does nothing. */ public void receive(Message msg) { } public byte[] getState() { MarshalledValueOutputStream out = null; byte[] result; ExposedByteArrayOutputStream baos = new ExposedByteArrayOutputStream(16 * 1024); try { out = new MarshalledValueOutputStream(baos); stateTransferManager.getState(out, Fqn.ROOT, configuration.getStateRetrievalTimeout(), true, true); } catch (Throwable t) { stateProducingFailed(t); } finally { result = baos.getRawBuffer(); Util.close(out); } return result; } public void setState(byte[] new_state) { if (new_state == null) { log.debug("transferred state is null (may be first member in cluster)"); return; } ByteArrayInputStream bais = new ByteArrayInputStream(new_state); MarshalledValueInputStream in = null; try { in = new MarshalledValueInputStream(bais); stateTransferManager.setState(in, Fqn.ROOT); stateReceivedSuccess(); } catch (Throwable t) { stateReceivingFailed(t); } finally { Util.close(in); synchronized (stateLock) { // Notify wait that state has been set. stateLock.notifyAll(); } } } public byte[] getState(String state_id) { MarshalledValueOutputStream out = null; String sourceRoot = state_id; byte[] result; boolean hasDifferentSourceAndIntegrationRoots = state_id.indexOf(StateTransferManager.PARTIAL_STATE_DELIMITER) > 0; if (hasDifferentSourceAndIntegrationRoots) { sourceRoot = state_id.split(StateTransferManager.PARTIAL_STATE_DELIMITER)[0]; } ExposedByteArrayOutputStream baos = new ExposedByteArrayOutputStream(16 * 1024); try { out = new MarshalledValueOutputStream(baos); stateTransferManager.getState(out, Fqn.fromString(sourceRoot), configuration.getStateRetrievalTimeout(), true, true); } catch (Throwable t) { stateProducingFailed(t); } finally { result = baos.getRawBuffer(); Util.close(out); } return result; } public void getState(OutputStream ostream) { MarshalledValueOutputStream out = null; try { out = new MarshalledValueOutputStream(ostream); stateTransferManager.getState(out, Fqn.ROOT, configuration.getStateRetrievalTimeout(), true, true); } catch (Throwable t) { stateProducingFailed(t); } finally { Util.close(out); } } public void getState(String state_id, OutputStream ostream) { String sourceRoot = state_id; MarshalledValueOutputStream out = null; boolean hasDifferentSourceAndIntegrationRoots = state_id.indexOf(StateTransferManager.PARTIAL_STATE_DELIMITER) > 0; if (hasDifferentSourceAndIntegrationRoots) { sourceRoot = state_id.split(StateTransferManager.PARTIAL_STATE_DELIMITER)[0]; } try { out = new MarshalledValueOutputStream(ostream); stateTransferManager.getState(out, Fqn.fromString(sourceRoot), configuration.getStateRetrievalTimeout(), true, true); } catch (Throwable t) { stateProducingFailed(t); } finally { Util.close(out); } } public void setState(InputStream istream) { if (istream == null) { log.debug("stream is null (may be first member in cluster)"); return; } MarshalledValueInputStream in = null; try { in = new MarshalledValueInputStream(istream); stateTransferManager.setState(in, Fqn.ROOT); stateReceivedSuccess(); } catch (Throwable t) { stateReceivingFailed(t); } finally { Util.close(in); synchronized (stateLock) { // Notify wait that state has been set. stateLock.notifyAll(); } } } public void setState(String state_id, byte[] state) { if (state == null) { log.debug("partial transferred state is null"); return; } MarshalledValueInputStream in = null; String targetRoot = state_id; boolean hasDifferentSourceAndIntegrationRoots = state_id.indexOf(StateTransferManager.PARTIAL_STATE_DELIMITER) > 0; if (hasDifferentSourceAndIntegrationRoots) { targetRoot = state_id.split(StateTransferManager.PARTIAL_STATE_DELIMITER)[1]; } try { log.debug("Setting received partial state for subroot " + state_id); Fqn subroot = Fqn.fromString(targetRoot); // Region region = regionManager.getRegion(subroot, false); // ClassLoader cl = null; // if (region != null) // { // // If a classloader is registered for the node's region, use it // cl = region.getClassLoader(); // } ByteArrayInputStream bais = new ByteArrayInputStream(state); in = new MarshalledValueInputStream(bais); //getStateTransferManager().setState(in, subroot, cl); stateTransferManager.setState(in, subroot); stateReceivedSuccess(); } catch (Throwable t) { stateReceivingFailed(t); } finally { Util.close(in); synchronized (stateLock) { // Notify wait that state has been set. stateLock.notifyAll(); } } } public void setState(String stateId, InputStream istream) { if (log.isTraceEnabled()) log.trace("**** Receiving state for " + stateId); String targetRoot = stateId; MarshalledValueInputStream in = null; boolean hasDifferentSourceAndIntegrationRoots = stateId.indexOf(StateTransferManager.PARTIAL_STATE_DELIMITER) > 0; if (hasDifferentSourceAndIntegrationRoots) { targetRoot = stateId.split(StateTransferManager.PARTIAL_STATE_DELIMITER)[1]; } if (istream == null) { log.debug("stream is null (may be first member in cluster). State is not set"); return; } try { log.debug("Setting received partial state for subroot " + stateId); in = new MarshalledValueInputStream(istream); Fqn subroot = Fqn.fromString(targetRoot); // Region region = regionManager.getRegion(subroot, false); // ClassLoader cl = null; // if (region != null) // { // // If a classloader is registered for the node's region, use it // cl = region.getClassLoader(); // } //getStateTransferManager().setState(in, subroot, cl); stateTransferManager.setState(in, subroot); stateReceivedSuccess(); } catch (Throwable t) { stateReceivingFailed(t); } finally { Util.close(in); synchronized (stateLock) { // Notify wait that state has been set. stateLock.notifyAll(); } } } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/UnversionedNode.java0000644000175000017500000004572211042316612026361 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import static org.jboss.cache.AbstractNode.NodeFlags.*; import org.jboss.cache.commands.write.CreateNodeCommand; import org.jboss.cache.factories.CommandsFactory; import org.jboss.cache.lock.IdentityLock; import org.jboss.cache.lock.LockStrategyFactory; import org.jboss.cache.marshall.MarshalledValue; import org.jboss.cache.optimistic.DataVersion; import org.jboss.cache.transaction.GlobalTransaction; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; /** * Basic data node class. Throws {@link UnsupportedOperationException} for version-specific methods like {@link #getVersion()} and * {@link #setVersion(org.jboss.cache.optimistic.DataVersion)}, defined in {@link org.jboss.cache.NodeSPI}. * * @author Manik Surtani (manik@jboss.org) * @since 2.0.0 */ @SuppressWarnings("unchecked") public class UnversionedNode extends AbstractNode { /** * Debug log. */ protected static Log log = LogFactory.getLog(UnversionedNode.class); protected static final boolean trace = log.isTraceEnabled(); /** * Lock manager that manages locks to be acquired when accessing the node inside a transaction. Lazy set just in case * locking is not needed. */ protected transient IdentityLock lock = null; /** * A reference of the CacheImpl instance. */ private transient CacheSPI cache; /** * Map of general data keys to values. */ private final Map data = new HashMap(); protected NodeSPI delegate; private CommandsFactory commandsFactory; protected LockStrategyFactory lockStrategyFactory; /** * Constructs a new node with an FQN of Root. */ public UnversionedNode() { this.fqn = Fqn.ROOT; initFlags(); } /** * Constructs a new node with a name, etc. * * @param mapSafe true if param data can safely be directly assigned to this object's * {@link #data} field; false if param data's contents should be copied into * this object's {@link #data} field. */ protected UnversionedNode(Object child_name, Fqn fqn, Map data, boolean mapSafe, CacheSPI cache) { if (cache == null) { throw new IllegalArgumentException("no cache init for " + fqn); } if (!fqn.isRoot() && !child_name.equals(fqn.getLastElement())) { throw new IllegalArgumentException("Child " + child_name + " must be last part of " + fqn); } initFlags(); this.cache = cache; this.fqn = fqn; init(); setInternalState(data); } /** * This method initialises flags on the node, by setting DATA_LOADED to true and VALID to true and all other flags to false. * The flags are defined in the {@link NodeFlags} enum. */ protected void initFlags() { setFlag(DATA_LOADED); setFlag(VALID); } public NodeSPI getDelegate() { return delegate; } public void setDelegate(NodeSPI delegate) { this.delegate = delegate; } public void injectDependencies(CacheSPI spi, CommandsFactory commandsFactory, LockStrategyFactory lockStrategyFactory) { this.cache = spi; this.commandsFactory = commandsFactory; this.lockStrategyFactory = lockStrategyFactory; init(); } /** * Initializes with a name and FQN and cache. */ private void init() { if (cache != null && cache.getConfiguration() != null) setLockForChildInsertRemove(cache.getConfiguration().isLockParentForChildInsertRemove()); } /** * Returns a parent by checking the TreeMap by name. */ public NodeSPI getParent() { if (fqn.isRoot()) { return null; } return cache.peek(fqn.getParent(), true); } protected synchronized void initLock() { if (lock == null) { lock = new IdentityLock(lockStrategyFactory, delegate); } } private synchronized Map> children() { if (children == null) { if (getFqn().isRoot()) { children = new ConcurrentHashMap>(64, .5f, 16); } else { // Less segments to save memory children = new ConcurrentHashMap>(4, .75f, 4); } } return children; } public CacheSPI getCache() { return cache; } public boolean isChildrenLoaded() { return isFlagSet(CHILDREN_LOADED); } public void setChildrenLoaded(boolean childrenLoaded) { setFlag(CHILDREN_LOADED, childrenLoaded); } private void assertValid() { if (!isValid()) throw new NodeNotValidException("Node " + getFqn() + " is not valid. Perhaps it has been moved or removed."); } public Object get(Object key) { assertValid(); return cache.get(getFqn(), key); } public Object getDirect(Object key) { return data.get(key); } private boolean isReadLocked() { return lock != null && lock.isReadLocked(); } private boolean isWriteLocked() { return lock != null && lock.isWriteLocked(); } public IdentityLock getLock() { initLock(); return lock; } public Map getDataDirect() { if (data == null) return Collections.emptyMap(); // return Collections.unmodifiableMap(data); return data; } public Object put(Object key, Object value) { assertValid(); return cache.put(getFqn(), key, value); } public Object putDirect(Object key, Object value) { return data.put(key, value); } public NodeSPI getOrCreateChild(Object child_name, GlobalTransaction gtx, boolean notify) { return getOrCreateChild(child_name, gtx, true, notify); } private NodeSPI getOrCreateChild(Object child_name, GlobalTransaction gtx, boolean createIfNotExists, boolean notify) { NodeSPI child; if (child_name == null) { throw new IllegalArgumentException("null child name"); } child = (NodeSPI) children().get(child_name); InvocationContext ctx = cache.getInvocationContext(); if (createIfNotExists && child == null) { // construct the new child outside the synchronized block to avoid // spending any more time than necessary in the synchronized section Fqn child_fqn = Fqn.fromRelativeElements(this.fqn, child_name); NodeSPI newChild = (NodeSPI) cache.getConfiguration().getRuntimeConfig().getNodeFactory().createNode(child_name, delegate, null); if (newChild == null) { throw new IllegalStateException(); } synchronized (this) { // check again to see if the child exists // after acquiring exclusive lock child = (NodeSPI) children().get(child_name); if (child == null) { if (notify) cache.getNotifier().notifyNodeCreated(child_fqn, true, ctx); child = newChild; children.put(child_name, child); if (gtx != null) { CreateNodeCommand createNodeCommand = commandsFactory.buildCreateNodeCommand(child_fqn); ctx.getTransactionEntry().addModification(createNodeCommand); } } } // notify if we actually created a new child if (newChild == child) { if (trace) { log.trace("created child: fqn=" + child_fqn); } if (notify) cache.getNotifier().notifyNodeCreated(child_fqn, false, ctx); } } return child; } public Object remove(Object key) { assertValid(); return cache.remove(getFqn(), key); } public Object removeDirect(Object key) { if (data == null) return null; return data.remove(key); } public void printDetails(StringBuilder sb, int indent) { printDetailsInMap(sb, indent); } /** * Returns a debug string. */ @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(getClass().getSimpleName()); if (!isValid()) sb.append(" (INVALID!) "); if (isDeleted()) { sb.append(" (deleted) [ ").append(fqn); } else { sb.append("[ ").append(fqn); } if (data != null) { synchronized (data) { if (trace) { sb.append(" data=").append(data.keySet()); } else { sb.append(" data=["); Set keys = data.keySet(); int i = 0; for (Object o : keys) { i++; sb.append(o); if (i == 5) { int more = keys.size() - 5; if (more > 1) { sb.append(", and "); sb.append(more); sb.append(" more"); break; } } else { sb.append(", "); } } sb.append("]"); } } } if (children != null && !children.isEmpty()) { if (trace) { sb.append(" children=").append(getChildrenNamesDirect()); } else { sb.append(" children=["); Set names = getChildrenNamesDirect(); int i = 0; for (Object o : names) { i++; sb.append(o); if (i == 5) { int more = names.size() - 5; if (more > 1) { sb.append(", and "); sb.append(more); sb.append(" more"); break; } } else { sb.append(", "); } } sb.append("]"); } } if (lock != null) { if (isReadLocked()) { sb.append(" RL"); } if (isWriteLocked()) { sb.append(" WL"); } } sb.append("]"); return sb.toString(); } public void addChildDirect(NodeSPI child) { Fqn childFqn = child.getFqn(); if (childFqn.isDirectChildOf(fqn)) { synchronized (this) { children().put(childFqn.getLastElement(), child); } } else throw new CacheException("Attempting to add a child [" + child.getFqn() + "] to [" + getFqn() + "]. Can only add direct children."); } public NodeSPI addChildDirect(Fqn f) { return addChildDirect(f, true); } public NodeSPI addChildDirect(Fqn f, boolean notify) { if (f.size() == 1) { GlobalTransaction gtx = cache.getInvocationContext().getGlobalTransaction(); return getOrCreateChild(f.getLastElement(), gtx, true, notify); } else { throw new UnsupportedOperationException("Cannot directly create children which aren't directly under the current node."); } } public NodeSPI addChildDirect(Object childName, boolean notify) { GlobalTransaction gtx = cache.getInvocationContext().getGlobalTransaction(); return getOrCreateChild(childName, gtx, true, notify); } public void clearDataDirect() { if (data != null) data.clear(); } public NodeSPI getChildDirect(Fqn fqn) { if (fqn.size() == 1) { return getChildDirect(fqn.getLastElement()); } else { NodeSPI currentNode = delegate; for (int i = 0; i < fqn.size(); i++) { Object nextChildName = fqn.get(i); currentNode = currentNode.getChildDirect(nextChildName); if (currentNode == null) return null; } return currentNode; } } public Set getChildrenNamesDirect() { return children == null ? Collections.emptySet() : new HashSet(children.keySet()); } public Set getKeysDirect() { if (data == null) { return Collections.emptySet(); } return Collections.unmodifiableSet(new HashSet(data.keySet())); } public boolean removeChildDirect(Object childName) { return children != null && children.remove(childName) != null; } public boolean removeChildDirect(Fqn f) { if (f.size() == 1) { return removeChildDirect(f.getLastElement()); } else { NodeSPI child = getChildDirect(f); return child != null && child.getParent().removeChildDirect(f.getLastElement()); } } public Map> getChildrenMapDirect() { return children; } public void setChildrenMapDirect(Map> children) { this.children().clear(); this.children.putAll(children); } public void putAll(Map data) { assertValid(); cache.put(fqn, data); } public void putAllDirect(Map data) { if (data == null) return; this.data.putAll(data); } public void removeChildrenDirect() { if (children != null) { children.clear(); } children = null; } // versioning public void setVersion(DataVersion version) { throw new UnsupportedOperationException("Versioning not supported"); } public DataVersion getVersion() { throw new UnsupportedOperationException("Versioning not supported"); } private void printIndent(StringBuilder sb, int indent) { if (sb != null) { for (int i = 0; i < indent; i++) { sb.append(" "); } } } public void addChild(Object child_name, Node n) { if (child_name != null) { children().put(child_name, n); } } /** * Returns the name of this node. */ private Object getName() { return fqn.getLastElement(); } /** * Returns the name of this node. */ public Fqn getFqn() { return fqn; } public void setFqn(Fqn fqn) { if (trace) { log.trace(getFqn() + " set FQN " + fqn); } this.fqn = fqn; if (children == null) { return; } // invoke children for (Map.Entry me : children.entrySet()) { NodeSPI n = (NodeSPI) me.getValue(); Fqn cfqn = Fqn.fromRelativeElements(fqn, me.getKey()); n.setFqn(cfqn); } } public NodeSPI getChildDirect(Object childName) { if (childName == null) return null; return (NodeSPI) (children == null ? null : children.get(childName)); } public Set getChildrenDirect() { // strip out deleted child nodes... if (children == null || children.size() == 0) return Collections.emptySet(); Set exclDeleted = new HashSet(); for (Node n : children.values()) { NodeSPI spi = (NodeSPI) n; if (!spi.isDeleted()) exclDeleted.add(spi); } return Collections.unmodifiableSet(exclDeleted); } public boolean hasChildrenDirect() { return children != null && children.size() != 0; } public Set getChildrenDirect(boolean includeMarkedForRemoval) { if (includeMarkedForRemoval) { if (children != null && !children.isEmpty()) { return Collections.unmodifiableSet(new HashSet((Collection) children.values())); } else { return Collections.emptySet(); } } else { return getChildrenDirect(); } } /** * Adds details of the node into a map as strings. */ private void printDetailsInMap(StringBuilder sb, int indent) { printIndent(sb, indent); indent += 2;// increse it if (!(getFqn()).isRoot()) { sb.append(Fqn.SEPARATOR); } sb.append(getName()); sb.append(" "); sb.append(data); if (children != null) { for (Node n : children.values()) { sb.append("\n"); ((NodeSPI) n).printDetails(sb, indent); } } } /** * Returns true if the data was loaded from the cache loader. */ public boolean isDataLoaded() { return isFlagSet(DATA_LOADED); } /** * Sets if the data was loaded from the cache loader. */ public void setDataLoaded(boolean dataLoaded) { setFlag(DATA_LOADED, dataLoaded); } public boolean isValid() { return isFlagSet(VALID); } public void setValid(boolean valid, boolean recursive) { setFlag(VALID, valid); if (trace) log.trace("Marking node " + getFqn() + " as " + (valid ? "" : "in") + "valid"); if (recursive) { for (Node child : children().values()) { ((NodeSPI) child).setValid(valid, recursive); } } } public boolean isLockForChildInsertRemove() { return isFlagSet(LOCK_FOR_CHILD_INSERT_REMOVE); } public void setLockForChildInsertRemove(boolean lockForChildInsertRemove) { setFlag(LOCK_FOR_CHILD_INSERT_REMOVE, lockForChildInsertRemove); } public void setInternalState(Map state) { // don't bother doing anything here putAllDirect(state); } public Map getInternalState(boolean onlyInternalState) { if (onlyInternalState) return new HashMap(0); // don't bother doing anything here if (data == null) return new HashMap(0); return new HashMap(data); } public void releaseObjectReferences(boolean recursive) { if (recursive && children != null) { for (Node child : children.values()) { child.releaseObjectReferences(recursive); } } if (data != null) { for (Object key : data.keySet()) { // get the key first, before attempting to serialize stuff since data.get() may deserialize the key if doing // a hashcode() or equals(). Object value = data.get(key); if (key instanceof MarshalledValue) { ((MarshalledValue) key).compact(true, true); } if (value instanceof MarshalledValue) { ((MarshalledValue) value).compact(true, true); } } } } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/registry/0000755000175000017500000000000011376174033024256 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/factories/0000755000175000017500000000000011376174001024360 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/factories/BuddyManagerFactory.java0000644000175000017500000000146511010537324031117 0ustar twernertwernerpackage org.jboss.cache.factories; import org.jboss.cache.buddyreplication.BuddyManager; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.annotations.DefaultFactoryFor; /** * Buddy manager factory * * @author Manik Surtani (manik@jboss.org) * @since 2.1.0 */ @DefaultFactoryFor(classes = {BuddyManager.class}) public class BuddyManagerFactory extends EmptyConstructorFactory { @Override public T construct(Class componentType) { if (configuration.getBuddyReplicationConfig() != null && configuration.getBuddyReplicationConfig().isEnabled() && configuration.getCacheMode() != Configuration.CacheMode.LOCAL) { return super.construct(componentType); } else { return null; } } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/factories/LockManagerFactory.java0000644000175000017500000000153711015575710030746 0ustar twernertwernerpackage org.jboss.cache.factories; import org.jboss.cache.factories.annotations.DefaultFactoryFor; import org.jboss.cache.lock.LockManager; import org.jboss.cache.lock.NodeBasedLockManager; import org.jboss.cache.lock.PessimisticNodeBasedLockManager; /** * Creates lock managers * * @author Manik Surtani (manik@jboss.org) * @since 2.2.0 */ @DefaultFactoryFor(classes = LockManager.class) public class LockManagerFactory extends EmptyConstructorFactory { @Override @SuppressWarnings({"unchecked", "deprecation"}) protected T construct(Class componentType) { if (configuration.isNodeLockingOptimistic()) { return (T) super.construct(NodeBasedLockManager.class); } else { return (T) super.construct(PessimisticNodeBasedLockManager.class); } } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/factories/ComponentFactory.java0000644000175000017500000000422411012441070030505 0ustar twernertwernerpackage org.jboss.cache.factories; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.ConfigurationException; import org.jboss.cache.factories.annotations.Inject; /** * Factory that creates components used internally within JBoss Cache, and also wires dependencies into the components. *

* The {@link org.jboss.cache.DefaultCacheFactory} is a special subclass of this, which bootstraps the construction of * other components. When this class is loaded, it maintains a static list of known default factories for known * components, which it then delegates to, when actually performing the construction. *

* * @author Manik Surtani (manik@jboss.org) * @see Inject * @see ComponentRegistry * @since 2.1.0 */ public abstract class ComponentFactory { protected final Log log = LogFactory.getLog(getClass()); protected ComponentRegistry componentRegistry; protected Configuration configuration; /** * Constructs a new ComponentFactory. */ public ComponentFactory(ComponentRegistry componentRegistry, Configuration configuration) { this.componentRegistry = componentRegistry; this.configuration = configuration; } /** * Constructs a new ComponentFactory. */ public ComponentFactory() { } @Inject private void injectDependencies(Configuration configuration, ComponentRegistry componentRegistry) { this.configuration = configuration; this.componentRegistry = componentRegistry; } /** * Constructs a component. * * @param componentType type of component * @return a component */ protected abstract T construct(Class componentType); protected void assertTypeConstructable(Class requestedType, Class... ableToConstruct) { boolean canConstruct = false; for (Class c : ableToConstruct) { canConstruct = canConstruct || requestedType.isAssignableFrom(c); } if (!canConstruct) throw new ConfigurationException("Don't know how to construct " + requestedType); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/factories/EmptyConstructorFactory.java0000644000175000017500000000445511026046072032125 0ustar twernertwernerpackage org.jboss.cache.factories; import org.jboss.cache.DataContainer; import org.jboss.cache.RegionManager; import org.jboss.cache.buddyreplication.BuddyFqnTransformer; import org.jboss.cache.config.ConfigurationException; import org.jboss.cache.factories.annotations.DefaultFactoryFor; import org.jboss.cache.invocation.CacheInvocationDelegate; import org.jboss.cache.invocation.InvocationContextContainer; import org.jboss.cache.loader.CacheLoaderManager; import org.jboss.cache.lock.LockStrategyFactory; import org.jboss.cache.marshall.Marshaller; import org.jboss.cache.marshall.VersionAwareMarshaller; import org.jboss.cache.notifications.Notifier; import org.jboss.cache.remoting.jgroups.ChannelMessageListener; import org.jboss.cache.statetransfer.StateTransferManager; import org.jboss.cache.transaction.TransactionTable; /** * Simple factory that just uses reflection and an empty constructor of the component type. * * @author Manik Surtani (manik@jboss.org) * @since 2.1.0 */ @DefaultFactoryFor(classes = {StateTransferManager.class, RegionManager.class, Notifier.class, ChannelMessageListener.class, CacheLoaderManager.class, Marshaller.class, InvocationContextContainer.class, CacheInvocationDelegate.class, TransactionTable.class, DataContainer.class, CommandsFactory.class, LockStrategyFactory.class, BuddyFqnTransformer.class}) public class EmptyConstructorFactory extends ComponentFactory { @Override @SuppressWarnings("unchecked") protected T construct(Class componentType) { try { if (componentType.isInterface()) { Class componentImpl; if (componentType.equals(Marshaller.class)) { componentImpl = VersionAwareMarshaller.class; } else { // add an "Impl" to the end of the class name and try again componentImpl = getClass().getClassLoader().loadClass(componentType.getName() + "Impl"); } return (T) componentImpl.newInstance(); } else { return componentType.newInstance(); } } catch (Exception e) { throw new ConfigurationException("Unable to create component " + componentType, e); } } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/factories/BootstrapFactory.java0000644000175000017500000000230411012441070030515 0ustar twernertwernerpackage org.jboss.cache.factories; import org.jboss.cache.CacheException; import org.jboss.cache.CacheSPI; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.annotations.DefaultFactoryFor; import org.jboss.cache.factories.annotations.NonVolatile; /** * // TODO: MANIK: Document this * * @author Manik Surtani (manik@jboss.org) * @since 2.2.0 */ @DefaultFactoryFor(classes = {CacheSPI.class, Configuration.class, ComponentRegistry.class}) @NonVolatile public class BootstrapFactory extends ComponentFactory { CacheSPI cacheSPI; public BootstrapFactory(CacheSPI cacheSPI, Configuration configuration, ComponentRegistry componentRegistry) { super(componentRegistry, configuration); this.cacheSPI = cacheSPI; } @Override protected T construct(Class componentType) { if (componentType.isAssignableFrom(CacheSPI.class) || componentType.isAssignableFrom(Configuration.class) || componentType.isAssignableFrom(ComponentRegistry.class)) { return componentType.cast(cacheSPI); } throw new CacheException("Don't know how to handle type " + componentType); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/factories/XmlConfigurationParser.java0000644000175000017500000006322211032227627031677 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.factories; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.buddyreplication.NextMemberBuddyLocator; import org.jboss.cache.config.BuddyReplicationConfig; import org.jboss.cache.config.BuddyReplicationConfig.BuddyLocatorConfig; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig.SingletonStoreConfig; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.ConfigurationException; import org.jboss.cache.config.EvictionConfig; import org.jboss.cache.config.EvictionPolicyConfig; import org.jboss.cache.config.EvictionRegionConfig; import org.jboss.cache.config.MissingPolicyException; import org.jboss.cache.eviction.EvictionPolicy; import org.jboss.cache.util.BeanUtils; import org.jboss.cache.util.Util; import org.jboss.cache.xml.XmlHelper; import org.w3c.dom.Attr; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import java.beans.PropertyEditor; import java.beans.PropertyEditorManager; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Properties; /** * Reads in XMLconfiguration files and spits out a {@link org.jboss.cache.config.Configuration} object. When deployed as a * JBoss MBean, this role is performed by the JBoss Microcontainer. This class is only used internally in unit tests * or within {@link org.jboss.cache.CacheFactory} implementations for standalone JBoss Cache usage. * * @author Manik Surtani (manik@jboss.org) * @author Galder Zamarreno * @since 2.00. */ public class XmlConfigurationParser { private static final Log log = LogFactory.getLog(XmlConfigurationParser.class); public static final String ATTR = "attribute"; public static final String NAME = "name"; /** * Parses an XML file and returns a new configuration. This method attempts to look for the file name passed in on * the classpath. If not found, it will search for the file on the file system instead, treating the name as an * absolute path. * * @param filename the name of the XML file to parse. * @return a configured Configuration object representing the configuration in the file */ public Configuration parseFile(String filename) { InputStream is = getAsInputStreamFromClassLoader(filename); if (is == null) { if (log.isDebugEnabled()) log.debug("Unable to find configuration file " + filename + " in classpath; searching for this file on the filesystem instead."); try { is = new FileInputStream(filename); } catch (FileNotFoundException e) { throw new ConfigurationException("Unable to find config file " + filename + " either in classpath or on the filesystem!", e); } } return parseStream(is); } /** * Parses an input stream containing XML text and returns a new configuration. * * @param stream input stream to parse. SHould not be null. * @return a configured Configuration object representing the configuration in the stream * @since 2.1.0 */ public Configuration parseStream(InputStream stream) { // loop through all elements in XML. Element root = XmlHelper.getDocumentRoot(stream); Element mbeanElement = getMBeanElement(root); return parseConfiguration(mbeanElement); } public Configuration parseConfiguration(Element configurationRoot) { ParsedAttributes attributes = extractAttributes(configurationRoot); // Deal with legacy attributes we no longer support handleRemovedAttributes(attributes); // Deal with legacy attributes that we renamed or otherwise altered handleRenamedAttributes(attributes); Configuration c = new Configuration(); setValues(c, attributes.stringAttribs, false); // Special handling for XML elements -- we hard code the parsing setXmlValues(c, attributes.xmlAttribs); return c; } /** * Check for and remove any attributes that were supported in the * 1.x releases and no longer are. Log a WARN or throw a * {@link ConfigurationException} if any are found. Which is done depends * on the attribute: *

*

    *
  • MultiplexerService -- throws an Exception
  • *
  • ServiceName -- logs a WARN
  • *
* * @param attributes */ protected void handleRemovedAttributes(ParsedAttributes attributes) { String evictionPolicy = attributes.stringAttribs.remove("EvictionPolicyClass"); if (evictionPolicy != null) { throw new ConfigurationException("XmlConfigurationParser does not " + "support the JBC 1.x attribute EvictionPolicyClass. Set the default " + "eviction policy via the policyClass element in the EvictionConfig section"); } String multiplexerService = attributes.stringAttribs.remove("MultiplexerService"); if (multiplexerService != null) { throw new ConfigurationException("XmlConfigurationParser does not " + "support the JBC 1.x attribute MultiplexerService. Inject the " + "multiplexer directly using Configuration.getRuntimeConfig().setMuxChannelFactory()"); } String serviceName = attributes.stringAttribs.remove("ServiceName"); if (serviceName != null) { log.warn("XmlConfigurationParser does not support the deprecated " + "attribute ServiceName. If JMX registration is needed, " + "register a CacheJmxWrapper or PojoCacheJmxWrapper in " + "JMX with the desired name"); } } /** * Check for any attributes that were supported in the * 1.x releases but whose name has changed. Log a WARN if any are found, but * convert the attribute to the new name. *

*

    *
  • UseMbean becomes ExposeManagementStatistics
  • *
* * @param attributes */ private void handleRenamedAttributes(ParsedAttributes attributes) { String keepStats = attributes.stringAttribs.remove("UseInterceptorMbeans"); if (keepStats != null && attributes.stringAttribs.get("ExposeManagementStatistics") == null) { log.warn("Found non-existent JBC 1.x attribute 'UseInterceptorMbeans' and replaced " + "with 'ExposeManagementStatistics'. Please update your config " + "to use the new attribute name"); attributes.stringAttribs.put("ExposeManagementStatistics", keepStats); } Element clc = attributes.xmlAttribs.remove("CacheLoaderConfiguration"); if (clc != null && attributes.xmlAttribs.get("CacheLoaderConfig") == null) { log.warn("Found non-existent JBC 1.x attribute 'CacheLoaderConfiguration' and replaced " + "with 'CacheLoaderConfig'. Please update your config " + "to use the new attribute name"); attributes.xmlAttribs.put("CacheLoaderConfig", clc); } } protected InputStream getAsInputStreamFromClassLoader(String filename) { ClassLoader cl = Thread.currentThread().getContextClassLoader(); InputStream is = cl == null ? null : cl.getResourceAsStream(filename); if (is == null) { // check system class loader is = getClass().getClassLoader().getResourceAsStream(filename); } return is; } protected Element getMBeanElement(Element root) { // This is following JBoss convention. NodeList list = root.getElementsByTagName(XmlHelper.ROOT); if (list == null) throw new ConfigurationException("Can't find " + XmlHelper.ROOT + " tag"); if (list.getLength() > 1) throw new ConfigurationException("Has multiple " + XmlHelper.ROOT + " tag"); Node node = list.item(0); Element element; if (node.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE) { element = (Element) node; } else { throw new ConfigurationException("Can't find " + XmlHelper.ROOT + " element"); } return element; } protected static void setValues(Object target, Map attribs, boolean isXmlAttribs) { Class objectClass = target.getClass(); // go thru simple string setters first. for (Entry entry : attribs.entrySet()) { String propName = (String) entry.getKey(); String setter = BeanUtils.setterName(propName); Method method; try { if (isXmlAttribs) { method = objectClass.getMethod(setter, Element.class); method.invoke(target, entry.getValue()); } else { method = objectClass.getMethod(setter, String.class); method.invoke(target, entry.getValue()); } continue; } catch (NoSuchMethodException me) { // this is ok, but certainly log this as a warning if (log.isDebugEnabled()) log.debug("Unrecognised attribute " + propName + ". Please check your configuration. Ignoring!!"); } catch (Exception e) { throw new ConfigurationException("Unable to invoke setter " + setter + " on " + objectClass, e); } // if we get here, we could not find a String or Element setter. for (Method m : objectClass.getMethods()) { if (setter.equals(m.getName())) { Class paramTypes[] = m.getParameterTypes(); if (paramTypes.length != 1) { throw new ConfigurationException("Setter " + setter + " does not contain the expected number of params. Has " + paramTypes.length + " instead of just 1."); } Class parameterType = paramTypes[0]; PropertyEditor editor = PropertyEditorManager.findEditor(parameterType); if (editor == null) { throw new ConfigurationException("Couldn't find a property editor for parameter type " + parameterType); } editor.setAsText((String) attribs.get(propName)); Object parameter = editor.getValue(); //if (log.isDebugEnabled()) log.debug("Invoking setter method: " + setter + " with parameter \"" + parameter + "\" of type " + parameter.getClass()); try { m.invoke(target, parameter); } catch (Exception e) { throw new ConfigurationException("Unable to invoke setter " + setter + " on " + objectClass, e); } } } } } protected void setXmlValues(Configuration conf, Map attribs) { for (Entry entry : attribs.entrySet()) { String propname = entry.getKey(); if ("BuddyReplicationConfiguration".equals(propname) || "BuddyReplicationConfig".equals(propname)) { BuddyReplicationConfig brc = parseBuddyReplicationConfig(entry.getValue()); conf.setBuddyReplicationConfig(brc); } else if ("CacheLoaderConfiguration".equals(propname) || "CacheLoaderConfig".equals(propname)) { CacheLoaderConfig clc = parseCacheLoaderConfig(entry.getValue()); conf.setCacheLoaderConfig(clc); } else if ("EvictionPolicyConfiguration".equals(propname) || "EvictionPolicyConfig".equals(propname)) { EvictionConfig ec = parseEvictionConfig(entry.getValue()); conf.setEvictionConfig(ec); } else if ("ClusterConfig".equals(propname)) { String jgc = parseClusterConfigXml(entry.getValue()); conf.setClusterConfig(jgc); } else { throw new ConfigurationException("Unknown configuration element " + propname); } } } public static BuddyReplicationConfig parseBuddyReplicationConfig(Element element) { BuddyReplicationConfig brc = new BuddyReplicationConfig(); brc.setEnabled(XmlHelper.readBooleanContents(element, "buddyReplicationEnabled")); brc.setDataGravitationRemoveOnFind(XmlHelper.readBooleanContents(element, "dataGravitationRemoveOnFind", true)); brc.setDataGravitationSearchBackupTrees(XmlHelper.readBooleanContents(element, "dataGravitationSearchBackupTrees", true)); brc.setAutoDataGravitation(brc.isEnabled() && XmlHelper.readBooleanContents(element, "autoDataGravitation", false)); String strBuddyCommunicationTimeout = XmlHelper.readStringContents(element, "buddyCommunicationTimeout"); try { brc.setBuddyCommunicationTimeout(Integer.parseInt(strBuddyCommunicationTimeout)); } catch (Exception e) { if (strBuddyCommunicationTimeout != null && strBuddyCommunicationTimeout.trim().length() != 0) { throw new ConfigurationException("Bad buddyCommunicationTimeout [" + strBuddyCommunicationTimeout + "]"); } } finally { if (log.isDebugEnabled()) { log.debug("Using buddy communication timeout of " + brc.getBuddyCommunicationTimeout() + " millis"); } } String buddyPoolName = XmlHelper.readStringContents(element, "buddyPoolName"); if ("".equals(buddyPoolName)) { buddyPoolName = null; } brc.setBuddyPoolName(buddyPoolName); // now read the buddy locator details String buddyLocatorClass = XmlHelper.readStringContents(element, "buddyLocatorClass"); if (buddyLocatorClass == null || buddyLocatorClass.length() == 0) { buddyLocatorClass = NextMemberBuddyLocator.class.getName(); } Properties props = null; try { props = XmlHelper.readPropertiesContents(element, "buddyLocatorProperties"); } catch (IOException e) { log.warn("Caught exception reading buddyLocatorProperties", e); log.error("Unable to read buddyLocatorProperties specified! Using defaults for [" + buddyLocatorClass + "]"); } BuddyLocatorConfig blc = new BuddyLocatorConfig(); blc.setBuddyLocatorClass(buddyLocatorClass); blc.setBuddyLocatorProperties(props); brc.setBuddyLocatorConfig(blc); return brc; } public static CacheLoaderConfig parseCacheLoaderConfig(Element element) { CacheLoaderConfig clc = new CacheLoaderConfig(); clc.setPassivation(XmlHelper.readBooleanContents(element, "passivation")); clc.setPreload(XmlHelper.readStringContents(element, "preload")); clc.setShared(XmlHelper.readBooleanContents(element, "shared")); NodeList cacheLoaderNodes = element.getElementsByTagName("cacheloader"); for (int i = 0; i < cacheLoaderNodes.getLength(); i++) { Node node = cacheLoaderNodes.item(i); if (node.getNodeType() == Node.ELEMENT_NODE) { Element indivElement = (Element) node; CacheLoaderConfig.IndividualCacheLoaderConfig iclc = new CacheLoaderConfig.IndividualCacheLoaderConfig(); iclc.setAsync(XmlHelper.readBooleanContents(indivElement, "async", false)); iclc.setIgnoreModifications(XmlHelper.readBooleanContents(indivElement, "ignoreModifications", false)); iclc.setFetchPersistentState(XmlHelper.readBooleanContents(indivElement, "fetchPersistentState", false)); iclc.setPurgeOnStartup(XmlHelper.readBooleanContents(indivElement, "purgeOnStartup", false)); iclc.setClassName(XmlHelper.readStringContents(indivElement, "class")); try { iclc.setProperties(XmlHelper.readPropertiesContents(indivElement, "properties")); } catch (IOException e) { throw new ConfigurationException("Problem loader cache loader properties", e); } SingletonStoreConfig ssc = parseSingletonStoreConfig(indivElement); if (ssc != null) { iclc.setSingletonStoreConfig(ssc); } clc.addIndividualCacheLoaderConfig(iclc); } } return clc; } private static SingletonStoreConfig parseSingletonStoreConfig(Element cacheLoaderelement) { /* singletonStore element can only appear once in a cacheloader, so we just take the first one ignoring any subsequent definitions in cacheloader element*/ Node singletonStoreNode = cacheLoaderelement.getElementsByTagName("singletonStore").item(0); if (singletonStoreNode != null && singletonStoreNode.getNodeType() == Node.ELEMENT_NODE) { Element singletonStoreElement = (Element) singletonStoreNode; boolean singletonStoreEnabled = XmlHelper.readBooleanContents(singletonStoreElement, "enabled"); String singletonStoreClass = XmlHelper.readStringContents(singletonStoreElement, "class"); Properties singletonStoreproperties; try { singletonStoreproperties = XmlHelper.readPropertiesContents(singletonStoreElement, "properties"); } catch (IOException e) { throw new ConfigurationException("Problem loading singleton store properties", e); } SingletonStoreConfig ssc = new SingletonStoreConfig(); ssc.setSingletonStoreEnabled(singletonStoreEnabled); ssc.setSingletonStoreClass(singletonStoreClass); ssc.setSingletonStoreproperties(singletonStoreproperties); return ssc; } return null; } public static EvictionConfig parseEvictionConfig(Element element) { EvictionConfig ec = new EvictionConfig(); if (element != null) { // If they set the default eviction policy in the element, use that // in preference to the external attribute String temp = XmlHelper.getTagContents(element, EvictionConfig.EVICTION_POLICY_CLASS, ATTR, NAME); if (temp != null && temp.length() > 0) { ec.setDefaultEvictionPolicyClass(temp); } temp = XmlHelper.getTagContents(element, EvictionConfig.WAKEUP_INTERVAL_SECONDS, ATTR, NAME); int wakeupIntervalSeconds = 0; if (temp != null) { wakeupIntervalSeconds = Integer.parseInt(temp); } if (wakeupIntervalSeconds <= 0) { wakeupIntervalSeconds = EvictionConfig.WAKEUP_DEFAULT; } ec.setWakeupIntervalSeconds(wakeupIntervalSeconds); int eventQueueSize = 0; temp = XmlHelper.getTagContents(element, EvictionConfig.EVENT_QUEUE_SIZE, ATTR, NAME); if (temp != null) { eventQueueSize = Integer.parseInt(temp); } if (eventQueueSize <= 0) { eventQueueSize = EvictionConfig.EVENT_QUEUE_SIZE_DEFAULT; } ec.setDefaultEventQueueSize(eventQueueSize); NodeList list = element.getElementsByTagName(EvictionRegionConfig.REGION); if (list != null && list.getLength() > 0) { List regionConfigs = new ArrayList(list.getLength()); for (int i = 0; i < list.getLength(); i++) { org.w3c.dom.Node node = list.item(i); if (node.getNodeType() != org.w3c.dom.Node.ELEMENT_NODE) { continue; } try { regionConfigs.add(parseEvictionRegionConfig((Element) node, ec.getDefaultEvictionPolicyClass(), eventQueueSize)); } catch (MissingPolicyException missingPolicy) { LogFactory.getLog(EvictionConfig.class).warn(missingPolicy.getLocalizedMessage()); throw missingPolicy; } } ec.setEvictionRegionConfigs(regionConfigs); } } return ec; } public static EvictionRegionConfig parseEvictionRegionConfig(Element element, String defaultEvictionClass, int defaultQueueCapacity) { EvictionRegionConfig erc = new EvictionRegionConfig(); erc.setRegionName(element.getAttribute(EvictionRegionConfig.NAME)); String temp = element.getAttribute(EvictionRegionConfig.EVENT_QUEUE_SIZE); if (temp != null && temp.length() > 0) { erc.setEventQueueSize(Integer.parseInt(temp)); } else { erc.setEventQueueSize(defaultQueueCapacity); } String evictionClass = element.getAttribute(EvictionRegionConfig.REGION_POLICY_CLASS); if (evictionClass == null || evictionClass.length() == 0) { evictionClass = defaultEvictionClass; // if it's still null... what do we setCache? if (evictionClass == null || evictionClass.length() == 0) { throw new MissingPolicyException( "There is no Eviction Policy Class specified on the region or for the entire cache!"); } } EvictionPolicy policy; try { policy = (EvictionPolicy) Util.loadClass(evictionClass).newInstance(); } catch (RuntimeException e) { throw e; } catch (Exception e) { throw new RuntimeException("Eviction class is not properly loaded in classloader", e); } EvictionPolicyConfig epc; try { epc = policy.getEvictionConfigurationClass().newInstance(); } catch (RuntimeException e) { throw e; } catch (Exception e) { throw new RuntimeException("Failed to instantiate eviction configuration of class " + policy.getEvictionConfigurationClass(), e); } parseEvictionPolicyConfig(element, epc); erc.setEvictionPolicyConfig(epc); return erc; } public static void parseEvictionPolicyConfig(Element element, EvictionPolicyConfig target) { target.reset(); ParsedAttributes attributes = extractAttributes(element); setValues(target, attributes.stringAttribs, false); setValues(target, attributes.xmlAttribs, true); target.validate(); } /** * Parses the cluster config which is used to start a JGroups channel * * @param config an old-style JGroups protocol config String */ public static String parseClusterConfigXml(Element config) { StringBuilder buffer = new StringBuilder(); NodeList stack = config.getChildNodes(); int length = stack.getLength(); for (int s = 0; s < length; s++) { org.w3c.dom.Node node = stack.item(s); if (node.getNodeType() != org.w3c.dom.Node.ELEMENT_NODE) { continue; } Element tag = (Element) node; String protocol = tag.getTagName(); buffer.append(protocol); NamedNodeMap attrs = tag.getAttributes(); int attrLength = attrs.getLength(); if (attrLength > 0) { buffer.append('('); } for (int a = 0; a < attrLength; a++) { Attr attr = (Attr) attrs.item(a); String name = attr.getName(); String value = attr.getValue(); buffer.append(name); buffer.append('='); buffer.append(value); if (a < attrLength - 1) { buffer.append(';'); } } if (attrLength > 0) { buffer.append(')'); } buffer.append(':'); } // Remove the trailing ':' buffer.setLength(buffer.length() - 1); return buffer.toString(); } protected static ParsedAttributes extractAttributes(Element source) { Map stringAttribs = new HashMap(); Map xmlAttribs = new HashMap(); NodeList list = source.getElementsByTagName(XmlHelper.ATTR); if (log.isDebugEnabled()) log.debug("Attribute size: " + list.getLength()); // loop through attributes for (int loop = 0; loop < list.getLength(); loop++) { Node node = list.item(loop); if (node.getNodeType() != org.w3c.dom.Node.ELEMENT_NODE) continue; // for each element (attribute) ... Element element = (Element) node; String name = element.getAttribute(XmlHelper.NAME); String valueStr = XmlHelper.getElementContent(element, true); Element valueXml = null; if (valueStr.length() == 0) { // This may be an XML element ... valueXml = XmlHelper.getConfigSubElement(element); } // add these to the maps. if (valueStr.length() > 0) stringAttribs.put(name, valueStr); if (valueXml != null) xmlAttribs.put(name, valueXml); } return new ParsedAttributes(stringAttribs, xmlAttribs); } static class ParsedAttributes { final Map stringAttribs; final Map xmlAttribs; ParsedAttributes(Map strings, Map elements) { this.stringAttribs = strings; this.xmlAttribs = elements; } } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/factories/ReplicationQueueFactory.java0000644000175000017500000000150711010537324032030 0ustar twernertwernerpackage org.jboss.cache.factories; import org.jboss.cache.cluster.ReplicationQueue; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.annotations.DefaultFactoryFor; /** * RPCManager factory * * @author Manik Surtani (manik@jboss.org) * @since 2.1.0 */ @DefaultFactoryFor(classes = ReplicationQueue.class) public class ReplicationQueueFactory extends EmptyConstructorFactory { @Override public T construct(Class componentType) { if ((configuration.getCacheMode() == Configuration.CacheMode.REPL_ASYNC || configuration.getCacheMode() == Configuration.CacheMode.INVALIDATION_ASYNC) && configuration.isUseReplQueue()) { return super.construct(componentType); } else { return null; } } }jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/factories/InterceptorChainFactory.java0000644000175000017500000001367211012320512032010 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.factories; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.ConfigurationException; import org.jboss.cache.factories.annotations.DefaultFactoryFor; import org.jboss.cache.interceptors.*; import org.jboss.cache.interceptors.base.CommandInterceptor; /** * Factory class that builds an interceptor chain based on cache configuration. * * @author Manik Surtani (manik@jboss.org) */ @DefaultFactoryFor(classes = InterceptorChain.class) public class InterceptorChainFactory extends ComponentFactory { /** * Note - this method used to return a singleton instance, and since 2.1.0 returns a new instance. The method is * deprecated and you should use the no-arg constructor to create a new instance of this factory. * * @return a NEW instance of this class. */ @Deprecated public static InterceptorChainFactory getInstance() { return new InterceptorChainFactory(); } private CommandInterceptor createInterceptor(Class clazz) throws IllegalAccessException, InstantiationException { CommandInterceptor chainedInterceptor = componentRegistry.getComponent(clazz); if (chainedInterceptor == null) { chainedInterceptor = clazz.newInstance(); componentRegistry.registerComponent(chainedInterceptor, clazz); } else { // wipe next/last chaining!! chainedInterceptor.setNext(null); } chainedInterceptor.setStatisticsEnabled(configuration.getExposeManagementStatistics()); return chainedInterceptor; } public InterceptorChain buildInterceptorChain() throws IllegalAccessException, InstantiationException, ClassNotFoundException { boolean optimistic = configuration.isNodeLockingOptimistic(); // load the icInterceptor first CommandInterceptor first = createInterceptor(InvocationContextInterceptor.class); InterceptorChain interceptorChain = new InterceptorChain(first); // add the interceptor chain to the registry first, since some interceptors may ask for it. componentRegistry.registerComponent(interceptorChain, InterceptorChain.class); // load the cache management interceptor next if (configuration.getExposeManagementStatistics()) interceptorChain.appendIntereceptor(createInterceptor(CacheMgmtInterceptor.class)); // load the tx interceptor interceptorChain.appendIntereceptor(createInterceptor(optimistic ? OptimisticTxInterceptor.class : TxInterceptor.class)); if (configuration.isUseLazyDeserialization()) interceptorChain.appendIntereceptor(createInterceptor(MarshalledValueInterceptor.class)); interceptorChain.appendIntereceptor(createInterceptor(NotificationInterceptor.class)); switch (configuration.getCacheMode()) { case REPL_SYNC: case REPL_ASYNC: interceptorChain.appendIntereceptor(optimistic ? createInterceptor(OptimisticReplicationInterceptor.class) : createInterceptor(ReplicationInterceptor.class)); break; case INVALIDATION_SYNC: case INVALIDATION_ASYNC: interceptorChain.appendIntereceptor(createInterceptor(InvalidationInterceptor.class)); break; case LOCAL: //Nothing... } if (!optimistic) { interceptorChain.appendIntereceptor(createInterceptor(PessimisticLockInterceptor.class)); } if (configuration.isUsingCacheLoaders()) { if (configuration.getCacheLoaderConfig().isPassivation()) { interceptorChain.appendIntereceptor(createInterceptor(ActivationInterceptor.class)); interceptorChain.appendIntereceptor(createInterceptor(PassivationInterceptor.class)); } else { interceptorChain.appendIntereceptor(createInterceptor(CacheLoaderInterceptor.class)); interceptorChain.appendIntereceptor(createInterceptor(CacheStoreInterceptor.class)); } } if (configuration.isUsingBuddyReplication()) interceptorChain.appendIntereceptor(createInterceptor(DataGravitatorInterceptor.class)); if (optimistic) { interceptorChain.appendIntereceptor(createInterceptor(OptimisticLockingInterceptor.class)); interceptorChain.appendIntereceptor(createInterceptor(OptimisticValidatorInterceptor.class)); interceptorChain.appendIntereceptor(createInterceptor(OptimisticCreateIfNotExistsInterceptor.class)); } // eviction interceptor to come before the optimistic node interceptor if (configuration.getEvictionConfig() != null && configuration.getEvictionConfig().isValidConfig()) interceptorChain.appendIntereceptor(createInterceptor(configuration.isUsingBuddyReplication() ? BuddyRegionAwareEvictionInterceptor.class : EvictionInterceptor.class)); if (optimistic) interceptorChain.appendIntereceptor(createInterceptor(OptimisticNodeInterceptor.class)); CommandInterceptor callInterceptor = createInterceptor(CallInterceptor.class); interceptorChain.appendIntereceptor(callInterceptor); if (log.isTraceEnabled()) log.trace("Finished building interceptor chain."); return interceptorChain; } @Override @SuppressWarnings("unchecked") protected T construct(Class componentType) { try { return (T) buildInterceptorChain(); } catch (Exception e) { throw new ConfigurationException("Unable to build interceptor chain", e); } } public static InterceptorChainFactory getInstance(ComponentRegistry componentRegistry, Configuration configuration) { InterceptorChainFactory icf = new InterceptorChainFactory(); icf.componentRegistry = componentRegistry; icf.configuration = configuration; return icf; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/factories/annotations/0000755000175000017500000000000011376174002026716 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/factories/annotations/Stop.java0000644000175000017500000000150111010537324030476 0ustar twernertwernerpackage org.jboss.cache.factories.annotations; import static java.lang.annotation.ElementType.METHOD; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Method level annotation that indicates a (no-param) method to be called on a component registered in the ComponentRegistry * when the cache stops. *

* * @author Manik Surtani (manik@jboss.org) * @since 2.1.0 */ @Target(METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Stop { /** * Optional parameter which defines the order in which this method will be called when the ComponentRegistry moves * to the STARTED state. Defaults to 10. * * @return execution priority * @since 2.2.0 */ int priority() default 10; }jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/factories/annotations/Destroy.java0000644000175000017500000000151311010537324031205 0ustar twernertwernerpackage org.jboss.cache.factories.annotations; import static java.lang.annotation.ElementType.METHOD; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Method level annotation that indicates a (no-param) method to be called on a component registered in the ComponentRegistry * when the cache is destroyed. *

* * @author Manik Surtani (manik@jboss.org) * @since 2.1.0 */ @Target(METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Destroy { /** * Optional parameter which defines the order in which this method will be called when the ComponentRegistry moves * to the STARTED state. Defaults to 10. * * @return execution priority * @since 2.2.0 */ int priority() default 10; }jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/factories/annotations/Inject.java0000644000175000017500000000341510727543765031017 0ustar twernertwernerpackage org.jboss.cache.factories.annotations; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Used to annotate a method as one that is used to inject a registered component into another component. The component * to be constructed must be built using the {@link org.jboss.cache.factories.ComponentFactory#construct(Class)} method, or if your object that needs * components injected into it already exists, it can be built using the {@link org.jboss.cache.factories.ComponentFactory#wireComponents(Object)} * method. *

* Usage example: *

 *       public class MyClass
 *       {
 *          private TransactionManager tm;
 *          private BuddyManager bm;
 *          private Notifier n;
 * 

* &Inject * public void setTransactionManager(TransactionManager tm) * { * this.tm = tm; * } *

* &Inject * public void injectMoreStuff(BuddyManager bm, Notifier n) * { * this.bm = bm; * this.n = n; * } * } *

*

* and an instance of this class can be constructed and wired using *
 *       MyClass myClass = componentFactory.construct(MyClass.class); // instance will have dependencies injected.
 * 
* If you wish to use named components, you can use the optional {@link org.jboss.cache.factories.annotations.ComponentName} * annotation on each parameter. * * @author Manik Surtani * @see ComponentName * @since 2.1.0 */ // ensure this annotation is available at runtime. @Retention(RetentionPolicy.RUNTIME) // only applies to fields. @Target(ElementType.METHOD) public @interface Inject { } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/factories/annotations/DefaultFactoryFor.java0000644000175000017500000000147211010537324033143 0ustar twernertwernerpackage org.jboss.cache.factories.annotations; import static java.lang.annotation.ElementType.TYPE; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * An annotation that is used internally, for defining a DEFAULT factory to be used when constructing components. This * annotation allows you to define which components can be constructed by the annotated factory. * * @author Manik Surtani (manik@jboss.org) * @since 2.1.0 */ @Target(TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface DefaultFactoryFor { /** * Components that may be constructed by a factory annotated with this annotation. * * @return classes that can be constructed by this factory */ Class[] classes(); } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/factories/annotations/NonVolatile.java0000644000175000017500000000144011010541323031777 0ustar twernertwernerpackage org.jboss.cache.factories.annotations; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * This annotation is used for components that will be registered in the {@link org.jboss.cache.factories.ComponentRegistry}, * that are resilient to changes in configuration. Examples are the {@link org.jboss.cache.CacheSPI} implementation used, which does * not change regardless of the configuration. * * @author Manik Surtani (manik@jboss.org) * @since 2.2.0 */ // ensure this annotation is available at runtime. @Retention(RetentionPolicy.RUNTIME) // only applies to classes. @Target(ElementType.TYPE) public @interface NonVolatile { } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/factories/annotations/Start.java0000644000175000017500000000150411010537324030651 0ustar twernertwernerpackage org.jboss.cache.factories.annotations; import static java.lang.annotation.ElementType.METHOD; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Method level annotation that indicates a (no-param) method to be called on a component registered in the ComponentRegistry * when the cache starts. *

* * @author Manik Surtani (manik@jboss.org) * @since 2.1.0 */ @Target(METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Start { /** * Optional parameter which defines the order in which this method will be called when the ComponentRegistry moves * to the STARTED state. Defaults to 10. * * @return execution priority * @since 2.2.0 */ int priority() default 10; } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/factories/jgroups/0000755000175000017500000000000011376174001026051 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/factories/CacheConfigsXmlParser.java0000755000175000017500000001221610713760467031415 0ustar twernertwerner/* * JBoss, Home of Professional Open Source. * Copyright 2006, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.cache.factories; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.InputStream; import java.util.HashMap; import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.ConfigurationException; import org.jboss.cache.xml.XmlHelper; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; /** * Parser able to parse a series of cache configurations stored in an * XML document with the following structure: *

 * 
 *    
 *     ....
 *    
 *    
 *     ....
 *    
 * 
 * 
*

* The '....' represents the normal content of the mbean element in a * JBC -service.xml config file. * * @author Brian Stansberry * @version $Revision: 1.1 $ */ public class CacheConfigsXmlParser { /** Name of the root element in a cache configs XML document*/ public static final String DOCUMENT_ROOT = "cache-configs"; /** * Name of the element that represents an individual cache configuration * in a cache configs XML document. */ public static final String CONFIG_ROOT = "cache-config"; /** * Name of the attribute in a {@link #CONFIG_ROOT cache-config} element that specifies * the name of the configuration. */ public static final String CONFIG_NAME = "name"; private static final Log log = LogFactory.getLog(CacheConfigsXmlParser.class); private final XmlConfigurationParser parser = new XmlConfigurationParser(); public Map parseConfigs(String fileName) throws CloneNotSupportedException { InputStream is = getAsInputStreamFromClassLoader(fileName); if (is == null) { if (log.isDebugEnabled()) log.debug("Unable to find configuration file " + fileName + " in classpath; searching for this file on the filesystem instead."); try { is = new FileInputStream(fileName); } catch (FileNotFoundException e) { throw new ConfigurationException("Unable to find config file " + fileName + " either in classpath or on the filesystem!", e); } } return parseConfigs(is); } public Map parseConfigs(InputStream stream) throws CloneNotSupportedException { // loop through all elements in XML. Element root = XmlHelper.getDocumentRoot(stream); NodeList list = root.getElementsByTagName(CONFIG_ROOT); if (list == null || list.getLength() == 0) throw new ConfigurationException("Can't find " + CONFIG_ROOT + " tag"); Map result = new HashMap(); for (int i = 0; i < list.getLength(); i++) { Node node = list.item(i); if (node.getNodeType() != Node.ELEMENT_NODE) { continue; } Element element = (Element) node; String name = element.getAttribute(CONFIG_NAME); if (name == null || name.trim().length() == 0) throw new ConfigurationException("Element " + element + " has no name attribute"); Configuration c = parser.parseConfiguration(element); // Prove that we can successfully clone it c = c.clone(); result.put(name.trim(), c); } return result; } protected InputStream getAsInputStreamFromClassLoader(String filename) { InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(filename); if (is == null) { // check system class loader is = getClass().getClassLoader().getResourceAsStream(filename); } return is; } }jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/factories/TransactionManagerFactory.java0000644000175000017500000000376211010537324032337 0ustar twernertwernerpackage org.jboss.cache.factories; import org.jboss.cache.config.ConfigurationException; import org.jboss.cache.factories.annotations.DefaultFactoryFor; import org.jboss.cache.transaction.TransactionManagerLookup; import javax.transaction.TransactionManager; /** * Uses a number of mechanisms to retrieve a transaction manager. * * @author Manik Surtani (manik@jboss.org) * @since 2.1.0 */ @DefaultFactoryFor(classes = {TransactionManager.class}) public class TransactionManagerFactory extends ComponentFactory { @Override @SuppressWarnings("unchecked") protected T construct(Class componentType) { // See if we had a TransactionManager injected into our config TransactionManager transactionManager = configuration.getRuntimeConfig().getTransactionManager(); TransactionManagerLookup lookup = null; if (transactionManager == null) { // Nope. See if we can look it up from JNDI if (configuration.getTransactionManagerLookupClass() != null) { try { ClassLoader cl = Thread.currentThread().getContextClassLoader(); if (cl == null) cl = getClass().getClassLoader(); Class clazz = cl.loadClass(configuration.getTransactionManagerLookupClass()); lookup = (TransactionManagerLookup) clazz.newInstance(); } catch (Exception e) { throw new ConfigurationException("Problems looking up transaction manager", e); } } try { if (lookup != null) { transactionManager = lookup.getTransactionManager(); configuration.getRuntimeConfig().setTransactionManager(transactionManager); } } catch (Exception e) { log.info("failed looking up TransactionManager, will not use transactions", e); } } return (T) transactionManager; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/factories/RuntimeConfigAwareFactory.java0000644000175000017500000000245711010537324032310 0ustar twernertwernerpackage org.jboss.cache.factories; import org.jboss.cache.NodeFactory; import org.jboss.cache.RPCManager; import org.jboss.cache.config.ConfigurationException; import org.jboss.cache.config.RuntimeConfig; import org.jboss.cache.factories.annotations.DefaultFactoryFor; import org.jboss.cache.util.BeanUtils; import java.lang.reflect.Method; /** * An extension of the EmptyConstructorFactory that places a component in the {@link RuntimeConfig} after creating it. * * @author Manik Surtani (manik@jboss.org) * @since 2.1.0 */ @DefaultFactoryFor(classes = {RPCManager.class, NodeFactory.class}) public class RuntimeConfigAwareFactory extends EmptyConstructorFactory { @Override @SuppressWarnings("unchecked") protected T construct(Class componentType) { T component = super.construct(componentType); Method setter = BeanUtils.setterMethod(RuntimeConfig.class, componentType); if (setter != null) { try { setter.invoke(configuration.getRuntimeConfig(), component); } catch (Exception e) { throw new ConfigurationException("Unable to put newly constructed component of type " + componentType + " in the RuntimeConfig", e); } } return component; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/factories/ComponentRegistry.java0000644000175000017500000007776311032145203030731 0ustar twernertwernerpackage org.jboss.cache.factories; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.CacheException; import org.jboss.cache.CacheSPI; import org.jboss.cache.CacheStatus; import org.jboss.cache.Version; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.ConfigurationException; import org.jboss.cache.config.RuntimeConfig; import org.jboss.cache.factories.annotations.DefaultFactoryFor; import org.jboss.cache.factories.annotations.Destroy; import org.jboss.cache.factories.annotations.Inject; import org.jboss.cache.factories.annotations.NonVolatile; import org.jboss.cache.factories.annotations.Start; import org.jboss.cache.factories.annotations.Stop; import org.jboss.cache.util.BeanUtils; import org.jboss.cache.util.reflect.ReflectionUtil; import javax.management.MBeanServerFactory; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; /** * A registry where components which have been created are stored. Components are stored as singletons, registered under * a specific name. *

* Components can be retrieved from the registry using {@link #getComponent(Class)}. *

* Components can be registered using {@link #registerComponent(Object, Class)}, which will cause any dependencies to be * wired in as well. Components that need to be created as a result of wiring will be done using {@link #getOrCreateComponent(Class)}, * which will look up the default factory for the component type (factories annotated with the appropriate {@link DefaultFactoryFor} annotation. *

* Default factories are treated as components too and will need to be wired before being used. *

* The registry can exist in one of several states, as defined by the {@link CacheStatus} enumeration. In terms of the cache, * state changes in the following manner: *

    *
  • INSTANTIATED - when first constructed
  • *
  • CONSTRUCTED - when created using the DefaultCacheFactory
  • *
  • When {@link org.jboss.cache.Cache#create()} is called, the components are rewired.
  • *
  • STARTED - when {@link org.jboss.cache.Cache#start()} is called
  • *
  • STOPPED - when {@link org.jboss.cache.Cache#stop()} is called
  • *
  • DESTROYED - when {@link org.jboss.cache.Cache#destroy()} is called.
  • *
*

* Cache configuration can only be changed and will only be reinjected if the cache is not in the {@link org.jboss.cache.CacheStatus#STARTED} state. * * @author Manik Surtani (manik@jboss.org) * @since 2.1.0 */ @NonVolatile public class ComponentRegistry { /** * Contains class definitions of component factories that can be used to construct certain components */ Map> defaultFactories = null; private static final Log log = LogFactory.getLog(ComponentRegistry.class); private static final boolean trace = log.isTraceEnabled(); protected static final Object NULL_COMPONENT = new Object(); // component and method containers final Map componentLookup = new HashMap(); CacheStatus state = CacheStatus.INSTANTIATED; /** * Hook to shut down the cache when the JVM exits. */ private Thread shutdownHook; /** * A flag that the shutdown hook sets before calling cache.stop(). Allows stop() to identify if it has been called * from a shutdown hook. */ private boolean invokedFromShutdownHook; /** * Creates an instance of the component registry. The configuration passed in is automatically registered. * * @param configuration configuration with which this is created */ public ComponentRegistry(Configuration configuration, CacheSPI cache) { try { // bootstrap. registerDefaultClassLoader(null); registerComponent(this, ComponentRegistry.class); registerComponent(configuration, Configuration.class); registerComponent(new BootstrapFactory(cache, configuration, this), BootstrapFactory.class); } catch (Exception e) { throw new CacheException("Unable to construct ComponentRegistry", e); } } /** * Retrieves the state of the registry * * @return state of the registry */ public CacheStatus getState() { return state; } /** * Wires an object instance with dependencies annotated with the {@link org.jboss.cache.factories.annotations.Inject} annotation, creating more components * as needed based on the Configuration passed in if these additional components don't exist in the * {@link ComponentRegistry}. Strictly for components that don't otherwise live in the registry and have a lifecycle, such as Nodes. * * @param target object to wire * @throws ConfigurationException if there is a problem wiring the instance */ public void wireDependencies(Object target) throws ConfigurationException { try { // don't use the reflection cache for wireDependencies calls since these are not managed by the ComponentRegistry // and may be invoked at any time, even after the cache starts. List methods = ReflectionUtil.getAllMethods(target.getClass(), Inject.class); // search for anything we need to inject for (Method method : methods) invokeInjectionMethod(target, method); } catch (Exception e) { throw new ConfigurationException("Unable to configure component (type: " + target.getClass() + ", instance " + target + ")", e); } } /** * Registers the default class loader. This method *must* be called before any other components are registered, * typically called by bootstrap code. Defensively, it is called in the constructor of ComponentRegistry with a null * parameter. * * @param loader a class loader to use by default. If this is null, the class loader used to load this instance of ComponentRegistry is used. */ public void registerDefaultClassLoader(ClassLoader loader) { registerComponent(loader == null ? getClass().getClassLoader() : loader, ClassLoader.class); // make sure the class loader is non-volatile, so it survives restarts. componentLookup.get(ClassLoader.class.getName()).nonVolatile = true; } /** * This is hard coded for now, since scanning the classpath for factories annotated with {@link org.jboss.cache.factories.annotations.DefaultFactoryFor} * does not work with all class loaders. This is a temporary solution until a more elegant one can be designed. *

* BE SURE TO ADD ANY NEW FACTORY TYPES ANNOTATED WITH DefaultFactoryFor TO THIS SET!! *

* * @return set of known factory types. */ private Set> getHardcodedFactories() { Set> s = new HashSet>(); s.add(BootstrapFactory.class); s.add(BuddyManagerFactory.class); s.add(EmptyConstructorFactory.class); s.add(InterceptorChainFactory.class); s.add(RuntimeConfigAwareFactory.class); s.add(TransactionManagerFactory.class); s.add(ReplicationQueueFactory.class); s.add(LockManagerFactory.class); return s; } /** * Registers a component in the registry under the given type, and injects any dependencies needed. If a component * of this type already exists, it is overwritten. * * @param component component to register * @param type type of component */ public void registerComponent(Object component, Class type) { String name = type.getName(); Component old = componentLookup.get(name); if (old != null && old.instance.equals(component)) { // if they are equal don't bother if (trace) log.trace("Attempting to register a component equal to one that already exists under the same name (" + name + "). Not doing anything."); return; } Component c; if (old != null) { if (trace) log.trace("Replacing old component " + old + " with new instance " + component); old.instance = component; old.methodsScanned = false; c = old; if (state == CacheStatus.STARTED) populateLifecycleMethods(); } else { c = new Component(); c.name = name; c.instance = component; if (trace) log.trace("Registering component " + c + " under name " + name); componentLookup.put(name, c); } c.nonVolatile = component.getClass().isAnnotationPresent(NonVolatile.class); addComponentDependencies(c); // inject dependencies for this component c.injectDependencies(); } /** * Adds component dependencies for a given component, by populating {@link Component#injectionMethods}. * * @param c component to add dependencies to */ protected void addComponentDependencies(Component c) { Class type = c.instance.getClass(); List methods = ReflectionUtil.getAllMethods(type, Inject.class); c.injectionMethods.clear(); c.injectionMethods.addAll(methods); } @SuppressWarnings("unchecked") protected void invokeInjectionMethod(Object o, Method m) { Class[] dependencies = m.getParameterTypes(); Object[] params = new Object[dependencies.length]; for (int i = 0; i < dependencies.length; i++) { params[i] = getOrCreateComponent(dependencies[i]); } ReflectionUtil.invokeAccessibly(o, m, params); } /** * Retrieves a component if one exists, and if not, attempts to find a factory capable of constructing the component * (factories annotated with the {@link DefaultFactoryFor} annotation that is capable of creating the component class). *

* If an instance needs to be constructed, dependencies are then automatically wired into the instance, based on methods * on the component type annotated with {@link Inject}. *

* Summing it up, component retrieval happens in the following order:
* 1. Look for a component that has already been created and registered. * 2. Look for an appropriate component that exists in the {@link Configuration} that may be injected from an external system. * 3. Look for a class definition passed in to the {@link org.jboss.cache.config.Configuration} - such as an EvictionPolicy implementation * 4. Attempt to create it by looking for an appropriate factory (annotated with {@link DefaultFactoryFor}) *

* * @param componentClass type of component to be retrieved. Should not be null. * @return a fully wired component instance, or null if one cannot be found or constructed. * @throws ConfigurationException if there is a problem with consructing or wiring the instance. */ protected T getOrCreateComponent(Class componentClass) { T component = getComponent(componentClass); if (component == null) { // first see if this has been injected externally. component = getFromConfiguration(componentClass); boolean attemptedFactoryConstruction = false; if (component == null) { // create this component and add it to the registry ComponentFactory factory = getFactory(componentClass); component = factory.construct(componentClass); attemptedFactoryConstruction = true; } if (component != null) { registerComponent(component, componentClass); } else if (attemptedFactoryConstruction) { if (trace) log.trace("Registering a null for component " + componentClass.getSimpleName()); registerNullComponent(componentClass); } } return component; } /** * Retrieves a component factory instance capable of constructing components of a specified type. If the factory doesn't * exist in the registry, one is created. * * @param componentClass type of component to construct * @return component factory capable of constructing such components */ protected ComponentFactory getFactory(Class componentClass) { if (defaultFactories == null) scanDefaultFactories(); Class cfClass = defaultFactories.get(componentClass); if (cfClass == null) throw new ConfigurationException("No registered default factory for component " + componentClass + " found!"); // a component factory is a component too! See if one has been created and exists in the registry ComponentFactory cf = getComponent(cfClass); if (cf == null) { // hasn't yet been created. Create and put in registry cf = instantiateFactory(cfClass); if (cf == null) throw new ConfigurationException("Unable to locate component factory for component " + componentClass); // we simply register this factory. Registration will take care of constructing any dependencies. registerComponent(cf, cfClass); } // ensure the component factory is in the STARTED state! Component c = componentLookup.get(cfClass.getName()); if (c.instance != cf) throw new ConfigurationException("Component factory " + cfClass + " incorrectly registered!"); return cf; } /** * Scans the class path for classes annotated with {@link org.jboss.cache.factories.annotations.DefaultFactoryFor}, and * analyses which components can be created by such factories. */ void scanDefaultFactories() { defaultFactories = new HashMap>(); Set> factories = getHardcodedFactories(); for (Class factory : factories) { DefaultFactoryFor dFFAnnotation = factory.getAnnotation(DefaultFactoryFor.class); for (Class targetClass : dFFAnnotation.classes()) defaultFactories.put(targetClass, factory); } } /** * No such thing as a meta factory yet. Factories are created using this method which attempts to use an empty public * constructor. * * @param factory class of factory to be created * @return factory instance */ ComponentFactory instantiateFactory(Class factory) { try { return factory.newInstance(); } catch (Exception e) { // unable to get a hold of an instance!! throw new ConfigurationException("Unable to instantiate factory " + factory, e); } } /** * registers a special "null" component that has no dependencies. * * @param type type of component to register as a null */ void registerNullComponent(Class type) { registerComponent(NULL_COMPONENT, type); } /** * Retrieves a component from the {@link Configuration} or {@link RuntimeConfig}. * * @param componentClass component type * @return component, or null if it cannot be found */ @SuppressWarnings("unchecked") protected T getFromConfiguration(Class componentClass) { if (log.isDebugEnabled()) log.debug("Looking in configuration for an instance of " + componentClass + " that may have been injected from an external source."); Method getter = BeanUtils.getterMethod(Configuration.class, componentClass); T returnValue = null; if (getter != null) { try { returnValue = (T) getter.invoke(getConfiguration()); } catch (Exception e) { log.warn("Unable to invoke getter " + getter + " on Configuration.class!", e); } } // now try the RuntimeConfig - a legacy "registry" of sorts. if (returnValue == null) { getter = BeanUtils.getterMethod(RuntimeConfig.class, componentClass); if (getter != null) { try { returnValue = (T) getter.invoke(getConfiguration().getRuntimeConfig()); } catch (Exception e) { log.warn("Unable to invoke getter " + getter + " on RuntimeConfig.class!", e); } } } return returnValue; } /** * Retrieves the configuration component. * * @return a Configuration object */ protected Configuration getConfiguration() { // this is assumed to always be present as a part of the bootstrap/construction of a ComponentRegistry. return getComponent(Configuration.class); } /** * Retrieves a component of a specified type from the registry, or null if it cannot be found. * * @param type type to find * @return component, or null */ @SuppressWarnings("unchecked") public T getComponent(Class type) { Component wrapper = componentLookup.get(type.getName()); if (wrapper == null) return null; return (T) (wrapper.instance == NULL_COMPONENT ? null : wrapper.instance); } /** * Rewires components. Can only be called if the current state is WIRED or STARTED. */ public void rewire() { // need to re-inject everything again. for (Component c : new HashSet(componentLookup.values())) { // inject dependencies for this component c.injectDependencies(); } } /** * Scans each registered component for lifecycle methods, and adds them to the appropriate lists, and then sorts them * by priority. */ private void populateLifecycleMethods() { for (Component c : componentLookup.values()) { if (!c.methodsScanned) { c.methodsScanned = true; c.startMethods.clear(); c.stopMethods.clear(); c.destroyMethods.clear(); List methods = ReflectionUtil.getAllMethods(c.instance.getClass(), Start.class); for (Method m : methods) { PrioritizedMethod em = new PrioritizedMethod(); em.component = c; em.method = m; em.priority = m.getAnnotation(Start.class).priority(); c.startMethods.add(em); } methods = ReflectionUtil.getAllMethods(c.instance.getClass(), Stop.class); for (Method m : methods) { PrioritizedMethod em = new PrioritizedMethod(); em.component = c; em.method = m; em.priority = m.getAnnotation(Stop.class).priority(); c.stopMethods.add(em); } methods = ReflectionUtil.getAllMethods(c.instance.getClass(), Destroy.class); for (Method m : methods) { PrioritizedMethod em = new PrioritizedMethod(); em.component = c; em.method = m; em.priority = m.getAnnotation(Destroy.class).priority(); c.destroyMethods.add(em); } } } } /** * Removes any components not annotated as @NonVolatile. */ public void resetNonVolatile() { // destroy all components to clean up resources for (Component c : new HashSet(componentLookup.values())) { // the component is volatile!! if (!c.nonVolatile) { componentLookup.remove(c.name); } } if (trace) log.trace("Reset volatile components"); } // ------------------------------ START: Publicly available lifecycle methods ----------------------------- // These methods perform a check for appropriate transition and then delegate to similarly named internal methods. /** * Creates the components needed by a cache instance and sets the cache status to {@link org.jboss.cache.CacheStatus#CREATED} * when it is done. */ public void create() { if (!state.createAllowed()) { if (state.needToDestroyFailedCache()) destroy(); else return; } try { internalCreate(); } catch (Throwable t) { handleLifecycleTransitionFailure(t); } } /** * This starts the components in the cache, connecting to channels, starting service threads, etc. If the cache is * not in the {@link org.jboss.cache.CacheStatus#CREATED} state, {@link #create()} will be invoked first. */ public void start() { boolean createdInStart = false; if (!state.startAllowed()) { if (state.needToDestroyFailedCache()) destroy(); // this will take us back to DESTROYED if (state.needCreateBeforeStart()) { create(); createdInStart = true; } else return; } try { internalStart(createdInStart); } catch (Throwable t) { handleLifecycleTransitionFailure(t); } } /** * Stops the cache and sets the cache status to {@link org.jboss.cache.CacheStatus#STOPPED} once it is done. If the cache is not in * the {@link org.jboss.cache.CacheStatus#STARTED} state, this is a no-op. */ public void stop() { if (!state.stopAllowed()) { return; } // Trying to stop() from FAILED is valid, but may not work boolean failed = state == CacheStatus.FAILED; try { internalStop(); } catch (Throwable t) { if (failed) { log.warn("Attempted to stop() from FAILED state, but caught exception; try calling destroy()", t); } failed = true; handleLifecycleTransitionFailure(t); } finally { if (!failed) state = CacheStatus.STOPPED; } } /** * Destroys the cache and frees up any resources. Sets the cache status to {@link CacheStatus#DESTROYED} when it is done. *

* If the cache is in {@link org.jboss.cache.CacheStatus#STARTED} when this method is called, it will first call {@link #stop()} * to stop the cache. */ public void destroy() { if (!state.destroyAllowed()) { if (state.needStopBeforeDestroy()) { try { stop(); } catch (CacheException e) { log.warn("Needed to call stop() before destroying but stop() threw exception. Proceeding to destroy", e); } } else return; } try { internalDestroy(); } finally { // We always progress to destroyed state = CacheStatus.DESTROYED; } } // ------------------------------ END: Publicly available lifecycle methods ----------------------------- // ------------------------------ START: Actual internal lifecycle methods -------------------------------- /** * Sets the cacheStatus to FAILED and rethrows the problem as one * of the declared types. Converts any non-RuntimeException Exception * to CacheException. * * @param t throwable thrown during failure */ private void handleLifecycleTransitionFailure(Throwable t) { state = CacheStatus.FAILED; if (t instanceof CacheException) throw (CacheException) t; else if (t instanceof RuntimeException) throw (RuntimeException) t; else if (t instanceof Error) throw (Error) t; else throw new CacheException(t); } /** * The actual create implementation. */ private void internalCreate() { state = CacheStatus.CREATING; resetNonVolatile(); rewire(); state = CacheStatus.CREATED; } private void internalStart(boolean createdInStart) throws CacheException, IllegalArgumentException { if (!createdInStart) { // re-wire all dependencies in case stuff has changed since the cache was created // remove any components whose construction may have depended upon a configuration that may have changed. resetNonVolatile(); rewire(); } state = CacheStatus.STARTING; // start all internal components // first cache all start, stop and destroy methods. populateLifecycleMethods(); List startMethods = new ArrayList(componentLookup.size()); for (Component c : componentLookup.values()) startMethods.addAll(c.startMethods); // sort the start methods by priority Collections.sort(startMethods); // fire all START methods according to priority for (PrioritizedMethod em : startMethods) em.invoke(); addShutdownHook(); log.info("JBoss Cache version: " + Version.printVersion()); state = CacheStatus.STARTED; } private void addShutdownHook() { ArrayList al = MBeanServerFactory.findMBeanServer(null); boolean registerShutdownHook = (getConfiguration().getShutdownHookBehavior() == Configuration.ShutdownHookBehavior.DEFAULT && al.size() == 0) || getConfiguration().getShutdownHookBehavior() == Configuration.ShutdownHookBehavior.REGISTER; if (registerShutdownHook) { if (log.isTraceEnabled()) log.trace("Registering a shutdown hook. Configured behavior = " + getConfiguration().getShutdownHookBehavior()); shutdownHook = new Thread() { @Override public void run() { try { invokedFromShutdownHook = true; ComponentRegistry.this.stop(); } finally { invokedFromShutdownHook = false; } } }; Runtime.getRuntime().addShutdownHook(shutdownHook); } else { if (log.isTraceEnabled()) log.trace("Not registering a shutdown hook. Configured behavior = " + getConfiguration().getShutdownHookBehavior()); } } /** * Actual stop */ private void internalStop() { state = CacheStatus.STOPPING; // if this is called from a source other than the shutdown hook, deregister the shutdown hook. if (!invokedFromShutdownHook && shutdownHook != null) Runtime.getRuntime().removeShutdownHook(shutdownHook); List stopMethods = new ArrayList(componentLookup.size()); for (Component c : componentLookup.values()) stopMethods.addAll(c.stopMethods); Collections.sort(stopMethods); // fire all STOP methods according to priority for (PrioritizedMethod em : stopMethods) em.invoke(); state = CacheStatus.STOPPED; } /** * Actual destroy */ private void internalDestroy() { state = CacheStatus.DESTROYING; resetNonVolatile(); List destroyMethods = new ArrayList(componentLookup.size()); for (Component c : componentLookup.values()) destroyMethods.addAll(c.destroyMethods); Collections.sort(destroyMethods); // fire all DESTROY methods according to priority for (PrioritizedMethod em : destroyMethods) em.invoke(); state = CacheStatus.DESTROYED; } // ------------------------------ END: Actual internal lifecycle methods -------------------------------- /** * Asserts whether invocations are allowed on the cache or not. Returns true if invocations are to be allowed, * false otherwise. If the origin of the call is remote and the cache status is {@link org.jboss.cache.CacheStatus#STARTING}, * this method will block for up to {@link org.jboss.cache.config.Configuration#getStateRetrievalTimeout()} millis, checking * for a valid state. * * @param originLocal true if the call originates locally (i.e., from the {@link org.jboss.cache.invocation.CacheInvocationDelegate} or false if it originates remotely, i.e., from the {@link org.jboss.cache.marshall.CommandAwareRpcDispatcher}. * @return true if invocations are allowed, false otherwise. */ public boolean invocationsAllowed(boolean originLocal) { log.trace("Testing if invocations are allowed."); if (state.allowInvocations()) return true; // if this is a locally originating call and the cache is not in a valid state, return false. if (originLocal) return false; log.trace("Is remotely originating."); // else if this is a remote call and the status is STARTING, wait until the cache starts. if (state == CacheStatus.STARTING) { log.trace("Cache is starting; block."); try { blockUntilCacheStarts(); return true; } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } else { log.warn("Received a remote call but the cache is not in STARTED state - ignoring call."); } return false; } /** * Blocks until the current cache instance is in it's {@link org.jboss.cache.CacheStatus#STARTED started} phase. Blocks * for up to {@link org.jboss.cache.config.Configuration#getStateRetrievalTimeout()} milliseconds, throwing an IllegalStateException * if the cache doesn't reach this state even after this maximum wait time. * * @throws InterruptedException if interrupted while waiting * @throws IllegalStateException if even after waiting the cache has not started. */ private void blockUntilCacheStarts() throws InterruptedException, IllegalStateException { int pollFrequencyMS = 100; long startupWaitTime = getConfiguration().getStateRetrievalTimeout(); long giveUpTime = System.currentTimeMillis() + startupWaitTime; while (System.currentTimeMillis() < giveUpTime) { if (state.allowInvocations()) break; Thread.sleep(pollFrequencyMS); } // check if we have started. if (!state.allowInvocations()) throw new IllegalStateException("Cache not in STARTED state, even after waiting " + getConfiguration().getStateRetrievalTimeout() + " millis."); } /** * A wrapper representing a component in the registry */ public class Component { /** * A reference to the object instance for this component. */ Object instance; /** * The name of the component */ String name; boolean methodsScanned; /** * List of injection methods used to inject dependencies into the component */ List injectionMethods = new ArrayList(2); List startMethods = new ArrayList(2); List stopMethods = new ArrayList(2); List destroyMethods = new ArrayList(2); /** * If true, then this component is not flushed before starting the ComponentRegistry. */ boolean nonVolatile; @Override public String toString() { return "Component{" + "instance=" + instance + ", name=" + name + ", nonVolatile=" + nonVolatile + '}'; } /** * Injects dependencies into this component. */ public void injectDependencies() { for (Method m : injectionMethods) invokeInjectionMethod(instance, m); } } /** * Wrapper to encapsulate a method along with a priority */ static class PrioritizedMethod implements Comparable { Method method; Component component; int priority; public int compareTo(PrioritizedMethod o) { return (priority < o.priority ? -1 : (priority == o.priority ? 0 : 1)); } void invoke() { ReflectionUtil.invokeAccessibly(component.instance, method, null); } @Override public String toString() { return "PrioritizedMethod{" + "method=" + method + ", priority=" + priority + '}'; } } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/factories/CommandsFactory.java0000644000175000017500000004526611026046072030327 0ustar twernertwernerpackage org.jboss.cache.factories; import org.jboss.cache.CacheException; import org.jboss.cache.CacheSPI; import org.jboss.cache.DataContainer; import org.jboss.cache.Fqn; import org.jboss.cache.RPCManager; import org.jboss.cache.buddyreplication.BuddyFqnTransformer; import org.jboss.cache.buddyreplication.BuddyGroup; import org.jboss.cache.buddyreplication.BuddyManager; import org.jboss.cache.commands.DataCommand; import org.jboss.cache.commands.ReplicableCommand; import org.jboss.cache.commands.ReversibleCommand; import org.jboss.cache.commands.read.ExistsCommand; import org.jboss.cache.commands.read.GetChildrenNamesCommand; import org.jboss.cache.commands.read.GetDataMapCommand; import org.jboss.cache.commands.read.GetKeyValueCommand; import org.jboss.cache.commands.read.GetKeysCommand; import org.jboss.cache.commands.read.GetNodeCommand; import org.jboss.cache.commands.read.GravitateDataCommand; import org.jboss.cache.commands.remote.AnnounceBuddyPoolNameCommand; import org.jboss.cache.commands.remote.AssignToBuddyGroupCommand; import org.jboss.cache.commands.remote.ClusteredGetCommand; import org.jboss.cache.commands.remote.DataGravitationCleanupCommand; import org.jboss.cache.commands.remote.RemoveFromBuddyGroupCommand; import org.jboss.cache.commands.remote.ReplicateCommand; import org.jboss.cache.commands.tx.CommitCommand; import org.jboss.cache.commands.tx.OptimisticPrepareCommand; import org.jboss.cache.commands.tx.PrepareCommand; import org.jboss.cache.commands.tx.RollbackCommand; import org.jboss.cache.commands.write.*; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.annotations.Inject; import org.jboss.cache.factories.annotations.NonVolatile; import org.jboss.cache.interceptors.InterceptorChain; import org.jboss.cache.notifications.Notifier; import org.jboss.cache.transaction.GlobalTransaction; import org.jboss.cache.transaction.TransactionTable; import org.jgroups.Address; import javax.transaction.TransactionManager; import java.util.Collections; import java.util.List; import java.util.Map; /** * Factory for all types of cache commands. * Here are some of the purposes of this class: *

 *   - not creating CacheCommands directly (i.e. through new usage) as this would reduce unit testability
 *   - reduce the coupling between commands and other components. e.g. considering a commands that needs to knwo whether
 *     locking type is optimistic, we will pass in a 'optimistic' boolean flag rather than entire Configuration object
 * 
* * @author Mircea.Markus@jboss.com * @since 2.2 */ @NonVolatile public class CommandsFactory { private RPCManager rpcManager; private DataContainer dataContainer; private Notifier notifier; private InterceptorChain invoker; private BuddyManager buddyManager; private TransactionTable transactionTable; private CacheSPI cacheSpi; private Configuration configuration; private TransactionManager txManager; private BuddyFqnTransformer buddyFqnTransformer; public CommandsFactory() { } @Inject public void initialize(RPCManager rpc, DataContainer dataContainer, Notifier notifier, BuddyManager buddyManager, InterceptorChain invoker, TransactionTable transactionTable, CacheSPI cacheSpi, Configuration configuration, TransactionManager txManager, BuddyFqnTransformer buddyFqnTransformer) { this.rpcManager = rpc; this.dataContainer = dataContainer; this.notifier = notifier; this.buddyManager = buddyManager; this.invoker = invoker; this.transactionTable = transactionTable; this.cacheSpi = cacheSpi; this.configuration = configuration; this.txManager = txManager; this.buddyFqnTransformer = buddyFqnTransformer; } public PutDataMapCommand buildPutDataMapCommand(GlobalTransaction gtx, Fqn fqn, Map data) { PutDataMapCommand cmd = new PutDataMapCommand(gtx, fqn, data); cmd.initialize(notifier, dataContainer); return cmd; } public PutKeyValueCommand buildPutKeyValueCommand(GlobalTransaction gtx, Fqn fqn, Object key, Object value) { PutKeyValueCommand command = new PutKeyValueCommand(gtx, fqn, key, value); command.initialize(notifier, dataContainer); return command; } public PutForExternalReadCommand buildPutForExternalReadCommand(GlobalTransaction gtx, Fqn fqn, Object key, Object value) { PutForExternalReadCommand command = new PutForExternalReadCommand(gtx, fqn, key, value); command.initialize(notifier, dataContainer); return command; } public ReplicateCommand buildReplicateCommand(ReplicableCommand command) { ReplicateCommand cmd = new ReplicateCommand(command); cmd.initialize(invoker); return cmd; } public ReplicateCommand buildReplicateCommand(List modifications) { ReplicateCommand cmd = new ReplicateCommand(modifications); cmd.initialize(invoker); return cmd; } public PrepareCommand buildPrepareCommand(GlobalTransaction gtx, ReversibleCommand command, boolean onePhaseCommit) { return buildPrepareCommand(gtx, Collections.singletonList(command), rpcManager.getLocalAddress(), onePhaseCommit); } public PrepareCommand buildPrepareCommand(GlobalTransaction gtx, List modifications, Address address, boolean onePhaseCommit) { return new PrepareCommand(gtx, modifications, address, onePhaseCommit); } public CommitCommand buildCommitCommand(GlobalTransaction gtx) { return new CommitCommand(gtx); } public DataGravitationCleanupCommand buildDataGravitationCleanupCommand(Fqn primaryFqn, Fqn backupFqn) { DataGravitationCleanupCommand command = new DataGravitationCleanupCommand(primaryFqn, backupFqn); command.initialize(buddyManager, invoker, transactionTable, this, dataContainer, buddyFqnTransformer); return command; } public GravitateDataCommand buildGravitateDataCommand(Fqn fqn, Boolean searchSubtrees) { GravitateDataCommand command = new GravitateDataCommand(fqn, searchSubtrees, rpcManager.getLocalAddress()); command.initialize(dataContainer, cacheSpi, buddyFqnTransformer); return command; } public RemoveNodeCommand buildRemoveNodeCommand(GlobalTransaction gtx, Fqn fqn) { RemoveNodeCommand command = new RemoveNodeCommand(gtx, fqn); command.initialize(notifier, dataContainer); return command; } public ClearDataCommand buildClearDataCommand(GlobalTransaction gtx, Fqn fqn) { ClearDataCommand command = new ClearDataCommand(gtx, fqn); command.initialize(notifier, dataContainer); return command; } public EvictCommand buildEvictFqnCommand(Fqn fqn) { EvictCommand command = new EvictCommand(fqn); command.initialize(notifier, dataContainer); return command; } public InvalidateCommand buildInvalidateCommand(Fqn fqn) { if (configuration.isNodeLockingOptimistic()) { OptimisticInvalidateCommand command = new OptimisticInvalidateCommand(fqn); command.initialize(txManager); command.initialize(cacheSpi, dataContainer, notifier); return command; } else { InvalidateCommand command = new InvalidateCommand(fqn); command.initialize(cacheSpi, dataContainer, notifier); return command; } } public RemoveKeyCommand buildRemoveKeyCommand(GlobalTransaction tx, Fqn fqn, Object key) { RemoveKeyCommand command = new RemoveKeyCommand(tx, fqn, key); command.initialize(notifier, dataContainer); return command; } public GetDataMapCommand buildGetDataMapCommand(Fqn fqn) { GetDataMapCommand command = new GetDataMapCommand(fqn); command.initialize(dataContainer); return command; } public ExistsCommand buildExistsNodeCommand(Fqn fqn) { ExistsCommand command = new ExistsCommand(fqn); command.initialize(dataContainer); return command; } public GetKeyValueCommand buildGetKeyValueCommand(Fqn fqn, Object key, boolean sendNodeEvent) { GetKeyValueCommand command = new GetKeyValueCommand(fqn, key, sendNodeEvent); command.initialize(dataContainer, notifier); return command; } public GetNodeCommand buildGetNodeCommand(Fqn fqn) { GetNodeCommand command = new GetNodeCommand(fqn); command.initialize(dataContainer); return command; } public GetKeysCommand buildGetKeysCommand(Fqn fqn) { GetKeysCommand command = new GetKeysCommand(fqn); command.initialize(dataContainer); return command; } public GetChildrenNamesCommand buildGetChildrenNamesCommand(Fqn fqn) { GetChildrenNamesCommand command = new GetChildrenNamesCommand(fqn); command.initialize(dataContainer); return command; } public MoveCommand buildMoveCommand(Fqn from, Fqn to) { MoveCommand command = new MoveCommand(from, to); command.initialize(notifier, dataContainer); return command; } public RollbackCommand buildRollbackCommand(GlobalTransaction gtx) { return new RollbackCommand(gtx); } public OptimisticPrepareCommand buildOptimisticPrepareCommand(GlobalTransaction gtx, List modifications, Map data, Address address, boolean onePhaseCommit) { return new OptimisticPrepareCommand(gtx, modifications, data, address, onePhaseCommit); } public AnnounceBuddyPoolNameCommand buildAnnounceBuddyPoolNameCommand(Address address, String buddyPoolName) { AnnounceBuddyPoolNameCommand command = new AnnounceBuddyPoolNameCommand(address, buddyPoolName); command.initialize(buddyManager); return command; } public RemoveFromBuddyGroupCommand buildRemoveFromBuddyGroupCommand(String groupName) { RemoveFromBuddyGroupCommand command = new RemoveFromBuddyGroupCommand(groupName); command.initialize(buddyManager); return command; } public AssignToBuddyGroupCommand buildAssignToBuddyGroupCommand(BuddyGroup group, Map state) { AssignToBuddyGroupCommand command = new AssignToBuddyGroupCommand(group, state); command.initialize(buddyManager); return command; } public ClusteredGetCommand buildClusteredGetCommand(Boolean searchBackupSubtrees, DataCommand dataCommand) { ClusteredGetCommand command = new ClusteredGetCommand(searchBackupSubtrees, dataCommand); command.initialize(dataContainer, invoker); return command; } public CreateNodeCommand buildCreateNodeCommand(Fqn fqn) { CreateNodeCommand command = new CreateNodeCommand(fqn); command.initialize(dataContainer); return command; } /** * Builds a cache command based on the ID passed in and an object array of parameters * * @param id id of the command to build * @param parameters parameters attached to the command * @return a newly constructed cache command */ public ReplicableCommand fromStream(int id, Object[] parameters) { ReplicableCommand command; switch (id) { case ExistsCommand.METHOD_ID: { ExistsCommand result = new ExistsCommand(); result.initialize(dataContainer); command = result; break; } case GetChildrenNamesCommand.METHOD_ID: { GetChildrenNamesCommand returnValue = new GetChildrenNamesCommand(); returnValue.initialize(dataContainer); command = returnValue; break; } case GetDataMapCommand.METHOD_ID: { GetDataMapCommand returnValue = new GetDataMapCommand(); returnValue.initialize(dataContainer); command = returnValue; break; } case GetKeysCommand.METHOD_ID: { GetKeysCommand returnValue = new GetKeysCommand(); returnValue.initialize(dataContainer); command = returnValue; break; } case GetKeyValueCommand.METHOD_ID: { GetKeyValueCommand returnValue = new GetKeyValueCommand(); returnValue.initialize(dataContainer, notifier); command = returnValue; break; } case GetNodeCommand.METHOD_ID: { GetNodeCommand returnValue = new GetNodeCommand(); returnValue.initialize(dataContainer); command = returnValue; break; } case MoveCommand.METHOD_ID: { MoveCommand returnValue = new MoveCommand(); returnValue.initialize(notifier, dataContainer); command = returnValue; break; } case PutDataMapCommand.METHOD_ID: case PutDataMapCommand.ERASE_METHOD_ID: case PutDataMapCommand.ERASE_VERSIONED_METHOD_ID: case PutDataMapCommand.VERSIONED_METHOD_ID: { PutDataMapCommand returnValue = new PutDataMapCommand(); returnValue.initialize(notifier, dataContainer); command = returnValue; break; } case PutKeyValueCommand.METHOD_ID: case PutKeyValueCommand.VERSIONED_METHOD_ID: { PutKeyValueCommand returnValue = new PutKeyValueCommand(); returnValue.initialize(notifier, dataContainer); command = returnValue; break; } case PutForExternalReadCommand.METHOD_ID: case PutForExternalReadCommand.VERSIONED_METHOD_ID: { PutForExternalReadCommand returnValue = new PutForExternalReadCommand(); returnValue.initialize(notifier, dataContainer); command = returnValue; break; } case ClearDataCommand.METHOD_ID: case ClearDataCommand.VERSIONED_METHOD_ID: { ClearDataCommand returnValue = new ClearDataCommand(); returnValue.initialize(notifier, dataContainer); command = returnValue; break; } case RemoveKeyCommand.METHOD_ID: case RemoveKeyCommand.VERSIONED_METHOD_ID: { RemoveKeyCommand returnValue = new RemoveKeyCommand(); returnValue.initialize(notifier, dataContainer); command = returnValue; break; } case RemoveNodeCommand.METHOD_ID: case RemoveNodeCommand.VERSIONED_METHOD_ID: { RemoveNodeCommand returnValue = new RemoveNodeCommand(); returnValue.initialize(notifier, dataContainer); command = returnValue; break; } case CreateNodeCommand.METHOD_ID: { CreateNodeCommand returnValue = new CreateNodeCommand(); returnValue.initialize(dataContainer); command = returnValue; break; } // --- transactional method calls case PrepareCommand.METHOD_ID: { command = new PrepareCommand(); break; } case OptimisticPrepareCommand.METHOD_ID: { command = new OptimisticPrepareCommand(); break; } case CommitCommand.METHOD_ID: { command = new CommitCommand(); break; } case RollbackCommand.METHOD_ID: { command = new RollbackCommand(); break; } // --- replicate methods case ReplicateCommand.MULTIPLE_METHOD_ID: case ReplicateCommand.SINGLE_METHOD_ID: { ReplicateCommand returnValue = new ReplicateCommand(); returnValue.initialize(invoker); command = returnValue; break; } case InvalidateCommand.METHOD_ID: { if (configuration.isNodeLockingOptimistic()) { OptimisticInvalidateCommand returnValue = new OptimisticInvalidateCommand(); returnValue.initialize(txManager); returnValue.initialize(cacheSpi, dataContainer, notifier); command = returnValue; } else { InvalidateCommand returnValue = new InvalidateCommand(); returnValue.initialize(cacheSpi, dataContainer, notifier); command = returnValue; } break; } case ClusteredGetCommand.METHOD_ID: { ClusteredGetCommand returnValue = new ClusteredGetCommand(); returnValue.initialize(dataContainer, invoker); command = returnValue; break; } // ---- Buddy replication - group organisation commands case AnnounceBuddyPoolNameCommand.METHOD_ID: { AnnounceBuddyPoolNameCommand returnValue = new AnnounceBuddyPoolNameCommand(); returnValue.initialize(buddyManager); command = returnValue; break; } case AssignToBuddyGroupCommand.METHOD_ID: { AssignToBuddyGroupCommand returnValue = new AssignToBuddyGroupCommand(); returnValue.initialize(buddyManager); command = returnValue; break; } case RemoveFromBuddyGroupCommand.METHOD_ID: { RemoveFromBuddyGroupCommand returnValue = new RemoveFromBuddyGroupCommand(); returnValue.initialize(buddyManager); command = returnValue; break; } case DataGravitationCleanupCommand.METHOD_ID: { DataGravitationCleanupCommand returnValue = new DataGravitationCleanupCommand(); returnValue.initialize(buddyManager, invoker, transactionTable, this, dataContainer, buddyFqnTransformer); command = returnValue; break; } case GravitateDataCommand.METHOD_ID: { GravitateDataCommand returnValue = new GravitateDataCommand(rpcManager.getLocalAddress()); returnValue.initialize(dataContainer, cacheSpi, buddyFqnTransformer); command = returnValue; break; } default: throw new CacheException("Unknown command id " + id + "!"); } command.setParameters(id, parameters); return command; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/CacheFactory.java0000644000175000017500000001443610712131130025574 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache; import net.jcip.annotations.ThreadSafe; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.ConfigurationException; import java.io.InputStream; /** * This factory constructs a cache from a given or default configuration set. *

* Typical usage would be: *

*

 *   CacheFactory factory = DefaultCacheFactory.getInstance();
 *   Cache cache = factory.createCache("replSync-service.xml"); // expects this file to be in classpath
 *   cache.stop();
 * 
* Factory methods provide options for creating a cache using *
    *
  • default configuration settings
  • *
  • an XML file containing the configuration
  • *
  • a constructed and populated {@link org.jboss.cache.config.Configuration} object
  • *
* In addition, methods provide anadditional option to create and return a cache without starting it. * * @author Manik Surtani (manik@jboss.org) * @see org.jboss.cache.Cache * @see org.jboss.cache.DefaultCacheFactory * @since 2.0.0 */ @ThreadSafe public interface CacheFactory { /** * Creates and starts a {@link Cache} instance using default configuration settings. See {@link Configuration} for default values. * * @return a cache * @throws ConfigurationException if there are problems with the default configuration */ Cache createCache() throws ConfigurationException; /** * Creates and optionally starts a {@link Cache} instance using default configuration settings. See {@link Configuration} for default values. * * @param start if true, starts the cache * @return a cache * @throws ConfigurationException if there are problems with the default configuration */ Cache createCache(boolean start) throws ConfigurationException; /** * Creates and starts a {@link org.jboss.cache.Cache} instance. The following are all valid calls: *
    *    factory.createCache("myCacheService.xml"); // file is in class path
    *    factory.createCache("etc/myCacheService.xml"); // file is in etc/ relative to the directory you started the JVM
    *    factory.createCache("/home/jbosscache/myCacheService.xml"); // file is in the /home/jbosscache directory
    * 
* * @param configFileName the named XML file should exist in the classpath or should be a fully qualified or relative (to your JVM working directory) path to a file on the local file system. Note that the classpath is checked first for the existence of this file. * @return a running {@link org.jboss.cache.Cache} instance * @throws org.jboss.cache.config.ConfigurationException * if there are problems with the configuration */ Cache createCache(String configFileName) throws ConfigurationException; /** * Creates {@link Cache} instance, and optionally starts it. * * @param configFileName the named XML file should exist in the classpath or should be a fully qualified or relative (to your JVM working directory) path to a file on the local file system. Note that the classpath is checked first for the existence of this file. * @param start if true, the cache is started before returning. * @return an optionally running {@link Cache} instance * @throws org.jboss.cache.config.ConfigurationException * if there are problems with the configuration * @see #createCache(String) for examples on valid config file names. */ Cache createCache(String configFileName, boolean start) throws ConfigurationException; /** * Creates a {@link Cache} instance based on a {@link org.jboss.cache.config.Configuration} passed in. *

* Ensure that the Configuration you pass in is not used by another cache instance in the same JVM, * as it may be concurrently modified. Clone the configuration, if shared, using * {@link org.jboss.cache.config.Configuration#clone()}. * * @param configuration the {@link Configuration} object that is passed in to setCache the {@link Cache}. * @return a running {@link Cache} instance * @throws org.jboss.cache.config.ConfigurationException * if there are problems with the configuration */ Cache createCache(Configuration configuration) throws ConfigurationException; /** * Creates {@link Cache} instance, and optionally starts it, based on a {@link org.jboss.cache.config.Configuration} passed in. *

* Ensure that the Configuration you pass in is not used by another cache instance in the same JVM, * as it may be concurrently modified. Clone the configuration, if shared, using * {@link org.jboss.cache.config.Configuration#clone()}. * * @param configuration the {@link Configuration} object that is passed in to setCache the {@link Cache}. * @param start if true, the cache is started before returning. * @return an optionally running {@link Cache} instance * @throws org.jboss.cache.config.ConfigurationException * if there are problems with the configuration */ Cache createCache(Configuration configuration, boolean start) throws ConfigurationException; /** * Creates a {@link Cache} instance based on an {@link java.io.InputStream} passed in, which should be a stream to a valid * XML configuration file. * * @param is the {@link java.io.InputStream} * @return a running {@link Cache} instance * @throws org.jboss.cache.config.ConfigurationException * if there are problems with the configuration * @since 2.1.0 */ Cache createCache(InputStream is) throws ConfigurationException; /** * Creates a {@link Cache} instance based on an {@link java.io.InputStream} passed in, which should be a stream to a valid * XML configuration file. * * @param is the {@link java.io.InputStream} * * @param start if true, the cache is started before returning. * @return a running {@link Cache} instance * @throws org.jboss.cache.config.ConfigurationException * if there are problems with the configuration * @since 2.1.0 */ Cache createCache(InputStream is, boolean start) throws ConfigurationException; } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/RegionNotEmptyException.java0000644000175000017500000000142210660427364030055 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache; /** * Thrown when an attempt is made to {@link RegionManager#activate(Fqn)} activate a subtree} * root in Fqn that already has an existing node in the cache. * * @author Brian Stansberry */ public class RegionNotEmptyException extends CacheException { /** * The serialVersionUID */ private static final long serialVersionUID = 1L; public RegionNotEmptyException() { super(); } public RegionNotEmptyException(String msg) { super(msg); } public RegionNotEmptyException(String msg, Throwable cause) { super(msg, cause); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/statetransfer/0000755000175000017500000000000011376174016025274 5ustar twernertwerner././@LongLink0000000000000000000000000000015000000000000011561 Lustar rootrootjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/statetransfer/DefaultStateTransferGenerator.javajbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/statetransfer/DefaultStateTransferGenerator.j0000644000175000017500000001160711032145203033376 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.statetransfer; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.Node; import org.jboss.cache.NodeSPI; import org.jboss.cache.Version; import org.jboss.cache.loader.CacheLoader; import org.jboss.cache.marshall.NodeData; import org.jboss.cache.marshall.NodeDataExceptionMarker; import java.io.ObjectOutputStream; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; public class DefaultStateTransferGenerator implements StateTransferGenerator { public static final short STATE_TRANSFER_VERSION = Version.getVersionShort("2.0.0.GA"); private Log log = LogFactory.getLog(getClass().getName()); private CacheSPI cache; private Set internalFqns; protected DefaultStateTransferGenerator(CacheSPI cache) { this.cache = cache; this.internalFqns = cache.getInternalFqns(); } public void generateState(ObjectOutputStream out, Node rootNode, boolean generateTransient, boolean generatePersistent, boolean suppressErrors) throws Throwable { Fqn fqn = rootNode.getFqn(); try { cache.getMarshaller().objectToObjectStream(STATE_TRANSFER_VERSION, out); if (generateTransient) { //transient + marker if (log.isTraceEnabled()) { log.trace("writing transient state for " + fqn); } marshallTransientState((NodeSPI) rootNode, out); delimitStream(out); if (log.isTraceEnabled()) { log.trace("transient state succesfully written"); } //associated + marker if (log.isTraceEnabled()) { log.trace("writing associated state"); } marshallAssociatedState(fqn, out); delimitStream(out); if (log.isTraceEnabled()) { log.trace("associated state succesfully written"); } } else { //we have to write two markers for transient and associated delimitStream(out); delimitStream(out); } CacheLoader cacheLoader = cache.getCacheLoaderManager() == null ? null : cache.getCacheLoaderManager().getCacheLoader(); if (cacheLoader != null && generatePersistent) { if (log.isTraceEnabled()) { log.trace("writing persistent state for " + fqn + ",using " + cache.getCacheLoaderManager().getCacheLoader().getClass()); } if (fqn.isRoot()) { cacheLoader.loadEntireState(out); } else { cacheLoader.loadState(fqn, out); } if (log.isTraceEnabled()) { log.trace("persistent state succesfully written"); } } delimitStream(out); } catch (Throwable t) { cache.getMarshaller().objectToObjectStream(new NodeDataExceptionMarker(t, cache.getLocalAddress()), out); throw t; } } /** * Places a delimiter marker on the stream * * @param out stream * @throws IOException if there are errs */ protected void delimitStream(ObjectOutputStream out) throws Exception { cache.getMarshaller().objectToObjectStream(StateTransferManager.STREAMING_DELIMITER_NODE, out); } /** * Do a preorder traversal: visit the node first, then the node's children * * @param out * @throws Exception */ protected void marshallTransientState(NodeSPI node, ObjectOutputStream out) throws Exception { List nodeData = new LinkedList(); generateNodeDataList(node, nodeData); cache.getMarshaller().objectToObjectStream(nodeData, out, node.getFqn()); } protected void generateNodeDataList(NodeSPI node, List list) throws Exception { if (internalFqns.contains(node.getFqn())) { return; } Map attrs; NodeData nd; // first handle the current node attrs = node.getInternalState(false); if (attrs.size() == 0) { nd = new NodeData(node.getFqn()); } else { nd = new NodeData(node.getFqn(), attrs); } list.add(nd); // then visit the children for (NodeSPI child : node.getChildrenDirect()) generateNodeDataList(child, list); } /** * Does nothing in this base class; can be overridden in a subclass. */ protected void marshallAssociatedState(Fqn fqn, ObjectOutputStream baos) throws Exception { // no-op in this base class } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/statetransfer/StateTransferGenerator.java0000644000175000017500000000070611005374170032566 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.statetransfer; import org.jboss.cache.Node; import java.io.ObjectOutputStream; public interface StateTransferGenerator { void generateState(ObjectOutputStream stream, Node rootNode, boolean generateTransient, boolean generatePersistent, boolean suppressErrors) throws Throwable; }././@LongLink0000000000000000000000000000015100000000000011562 Lustar rootrootjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/statetransfer/DefaultStateTransferIntegrator.javajbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/statetransfer/DefaultStateTransferIntegrator.0000644000175000017500000003142411032145203033413 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.statetransfer; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.CacheException; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.Node; import org.jboss.cache.NodeFactory; import org.jboss.cache.NodeSPI; import org.jboss.cache.Region; import org.jboss.cache.buddyreplication.BuddyManager; import org.jboss.cache.eviction.EvictedEventNode; import org.jboss.cache.eviction.NodeEventType; import org.jboss.cache.loader.CacheLoader; import org.jboss.cache.loader.CacheLoaderManager; import org.jboss.cache.marshall.NodeData; import org.jboss.cache.marshall.NodeDataExceptionMarker; import org.jboss.cache.marshall.NodeDataMarker; import org.jboss.cache.notifications.event.NodeModifiedEvent; import java.io.IOException; import java.io.ObjectInputStream; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; public class DefaultStateTransferIntegrator implements StateTransferIntegrator { protected Log log = LogFactory.getLog(getClass().getName()); private CacheSPI cache; private Fqn targetFqn; private NodeFactory factory; private NodeFactory.NodeType nodeType; private Set internalFqns; public DefaultStateTransferIntegrator(Fqn targetFqn, CacheSPI cache) { this.targetFqn = targetFqn; this.cache = cache; this.factory = cache.getConfiguration().getRuntimeConfig().getNodeFactory(); this.nodeType = cache.getConfiguration().isNodeLockingOptimistic() ? NodeFactory.NodeType.VERSIONED_NODE : NodeFactory.NodeType.UNVERSIONED_NODE; this.internalFqns = cache.getInternalFqns(); } public void integrateState(ObjectInputStream ois, Node target) throws Exception { integrateTransientState(ois, (NodeSPI) target); integrateAssociatedState(ois); integratePersistentState(ois); } protected void integrateTransientState(ObjectInputStream in, NodeSPI target) throws Exception { boolean transientSet = false; // ClassLoader oldCL = setClassLoader(cl); try { if (log.isTraceEnabled()) { log.trace("integrating transient state for " + target); } integrateTransientState(target, in); transientSet = true; if (log.isTraceEnabled()) { log.trace("transient state successfully integrated"); } notifyAllNodesCreated(cache.getInvocationContext(), target); } catch (Exception e) { throw new CacheException(e); } finally { if (!transientSet) { target.clearDataDirect(); target.removeChildrenDirect(); } // resetClassLoader(oldCL); } } /** * Provided for subclasses that deal with associated state. * * @throws Exception */ protected void integrateAssociatedState(ObjectInputStream in) throws Exception { // no-op in this base class // just read marker cache.getMarshaller().objectFromObjectStream(in); } protected void integratePersistentState(ObjectInputStream in) throws Exception { CacheLoaderManager loaderManager = cache.getCacheLoaderManager(); CacheLoader loader = loaderManager == null ? null : loaderManager.getCacheLoader(); if (loader == null) { if (log.isTraceEnabled()) { log.trace("cache loader is null, will not attempt to integrate persistent state"); } } else { if (log.isTraceEnabled()) { log.trace("integrating persistent state using " + loader.getClass().getName()); } boolean persistentSet = false; try { if (targetFqn.isRoot()) { loader.storeEntireState(in); } else { loader.storeState(targetFqn, in); } persistentSet = true; } catch (ClassCastException cce) { log.error("Failed integrating persistent state. One of cacheloaders is not" + " adhering to state stream format. See JBCACHE-738."); throw cce; } finally { if (!persistentSet) { log.warn("persistent state integration failed, removing all nodes from loader"); loader.remove(targetFqn); } else { if (log.isTraceEnabled()) { log.trace("persistent state integrated successfully"); } } } } } protected NodeFactory getFactory() { return factory; } protected NodeFactory.NodeType getNodeType() { return nodeType; } protected Fqn getTargetFqn() { return targetFqn; } /** * Generates NodeAdded notifications for all nodes of the tree. This is * called whenever the tree is initially retrieved (state transfer) */ private void notifyAllNodesCreated(InvocationContext ctx, NodeSPI curr) { if (curr == null) return; ctx.setOriginLocal(false); cache.getNotifier().notifyNodeCreated(curr.getFqn(), true, ctx); cache.getNotifier().notifyNodeCreated(curr.getFqn(), false, ctx); // AND notify that they have been modified!! if (!curr.getKeysDirect().isEmpty()) { cache.getNotifier().notifyNodeModified(curr.getFqn(), true, NodeModifiedEvent.ModificationType.PUT_MAP, Collections.emptyMap(), ctx); cache.getNotifier().notifyNodeModified(curr.getFqn(), false, NodeModifiedEvent.ModificationType.PUT_MAP, curr.getDataDirect(), ctx); } ctx.setOriginLocal(true); Set children = curr.getChildrenDirect(); for (NodeSPI n : children) { notifyAllNodesCreated(ctx, n); } } /* private ClassLoader setClassLoader(ClassLoader newLoader) { ClassLoader oldClassLoader = null; if (newLoader != null) { oldClassLoader = Thread.currentThread().getContextClassLoader(); Thread.currentThread().setContextClassLoader(newLoader); } return oldClassLoader; } private void resetClassLoader(ClassLoader oldLoader) { if (oldLoader != null) { Thread.currentThread().setContextClassLoader(oldLoader); } } */ private void integrateTransientState(NodeSPI target, ObjectInputStream in) throws Exception { target.removeChildrenDirect(); List list = readNodesAsList(in); if (list != null) { // if the list was null we read an EOF marker!! So don't bother popping it off the stack later. Iterator nodeDataIterator = list.iterator(); // Read the first NodeData and integrate into our target if (nodeDataIterator.hasNext()) { NodeData nd = nodeDataIterator.next(); //are there any transient nodes at all? if (nd != null && !nd.isMarker()) { Map attributes = nd.getAttributes(); target.setInternalState(attributes); // Check whether this is an integration into the buddy backup // subtree Fqn tferFqn = nd.getFqn(); Fqn tgtFqn = target.getFqn(); boolean move = tgtFqn.isChildOrEquals(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN) && !tferFqn.isChildOrEquals(tgtFqn); // If it is an integration, calculate how many levels of offset int offset = move ? tgtFqn.size() - tferFqn.size() : 0; integrateStateTransferChildren(target, offset, nodeDataIterator); integrateRetainedNodes(target); } } // read marker off stack cache.getMarshaller().objectFromObjectStream(in); } } @SuppressWarnings("unchecked") private List readNodesAsList(ObjectInputStream in) throws Exception { Object obj = cache.getMarshaller().objectFromObjectStream(in); if (obj instanceof NodeDataMarker) return null; return (List) obj; } private NodeData integrateStateTransferChildren(NodeSPI parent, int offset, Iterator nodeDataIterator) throws IOException, ClassNotFoundException { int parent_level = parent.getFqn().size(); int target_level = parent_level + 1; Fqn fqn; int size; Object name; NodeData nd = nodeDataIterator.hasNext() ? nodeDataIterator.next() : null; while (nd != null && !nd.isMarker()) { fqn = nd.getFqn(); // If we need to integrate into the buddy backup subtree, // change the Fqn to fit under it if (offset > 0) { fqn = Fqn.fromRelativeFqn(parent.getFqn().getAncestor(offset), fqn); } size = fqn.size(); if (size <= parent_level) { return nd; } else if (size > target_level) { throw new IllegalStateException("NodeData " + fqn + " is not a direct child of " + parent.getFqn()); } name = fqn.get(size - 1); Map attrs = nd.getAttributes(); // We handle this NodeData. Create a TreeNode and // integrate its data NodeSPI target = factory.createDataNode(name, fqn, parent, attrs, false); parent.addChild(name, target); // JBCACHE-913 Region region = cache.getRegion(fqn, false); if (region != null && region.getEvictionPolicy() != null) { region.putNodeEvent(new EvictedEventNode(fqn, NodeEventType.ADD_NODE_EVENT, attrs == null ? 0 : attrs.size())); } // Recursively call, which will walk down the tree // and return the next NodeData that's a child of our parent nd = integrateStateTransferChildren(target, offset, nodeDataIterator); } if (nd != null && nd.isExceptionMarker()) { NodeDataExceptionMarker ndem = (NodeDataExceptionMarker) nd; throw new CacheException("State provider node " + ndem.getCacheNodeIdentity() + " threw exception during loadState", ndem.getCause()); } return null; } private Set retainInternalNodes(Node target) { Set result = new HashSet(); Fqn targetFqn = target.getFqn(); for (Fqn internalFqn : internalFqns) { if (internalFqn.isChildOf(targetFqn)) { Node internalNode = getInternalNode(target, internalFqn); if (internalNode != null) { result.add(internalNode); } } } return result; } private Node getInternalNode(Node parent, Fqn internalFqn) { Object name = internalFqn.get(parent.getFqn().size()); cache.getInvocationContext().getOptionOverrides().setSkipCacheStatusCheck(true); Node result = parent.getChild(name); if (result != null && internalFqn.size() < result.getFqn().size()) { // need to recursively walk down the tree result = getInternalNode(result, internalFqn); } return result; } private void integrateRetainedNodes(NodeSPI target) { Set retainedNodes = retainInternalNodes(target); Fqn rootFqn = target.getFqn(); for (Node retained : retainedNodes) { if (retained.getFqn().isChildOf(rootFqn)) { integrateRetainedNode(target, retained); } } } private void integrateRetainedNode(NodeSPI ancestor, Node descendant) { Fqn descFqn = descendant.getFqn(); Fqn ancFqn = ancestor.getFqn(); Object name = descFqn.get(ancFqn.size()); NodeSPI child = (NodeSPI) ancestor.getChild(name); if (ancFqn.size() == descFqn.size() + 1) { if (child == null) { ancestor.addChild(name, descendant); } else { log.warn("Received unexpected internal node " + descFqn + " in transferred state"); } } else { if (child == null) { // Missing level -- have to create empty node // This shouldn't really happen -- internal fqns should // be immediately under the root child = factory.createDataNode(name, Fqn.fromRelativeElements(ancFqn, name), ancestor, null, true); ancestor.addChild(name, child); } // Keep walking down the tree integrateRetainedNode(child, descendant); } } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/statetransfer/StateTransferManager.java0000644000175000017500000002746411017543331032224 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.statetransfer; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.CacheException; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.NodeSPI; import org.jboss.cache.RegionEmptyException; import org.jboss.cache.RegionManager; import org.jboss.cache.config.Configuration; import org.jboss.cache.factories.annotations.Inject; import org.jboss.cache.factories.annotations.NonVolatile; import org.jboss.cache.loader.CacheLoaderManager; import org.jboss.cache.lock.LockManager; import static org.jboss.cache.lock.LockType.READ; import org.jboss.cache.lock.TimeoutException; import org.jboss.cache.marshall.InactiveRegionException; import org.jboss.cache.marshall.Marshaller; import org.jboss.cache.marshall.NodeData; import org.jboss.cache.marshall.NodeDataMarker; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; @NonVolatile public class StateTransferManager { protected final static Log log = LogFactory.getLog(StateTransferManager.class); public static final NodeData STREAMING_DELIMITER_NODE = new NodeDataMarker(); public static final String PARTIAL_STATE_DELIMITER = "_PARTIAL_STATE_DELIMITER"; private CacheSPI cache; private Marshaller marshaller; private RegionManager regionManager; private Configuration configuration; private LockManager lockManager; public StateTransferManager() { } @Inject public void injectDependencies(CacheSPI cache, Marshaller marshaller, RegionManager regionManager, Configuration configuration, LockManager lockManager) { this.cache = cache; this.regionManager = regionManager; this.marshaller = marshaller; this.configuration = configuration; this.lockManager = lockManager; } public StateTransferManager(CacheSPI cache) { this.cache = cache; } /** * Writes the state for the portion of the tree named by fqn to * the provided OutputStream. *

*

* * @param out stream to write state to * @param fqn Fqn indicating the uppermost node in the * portion of the tree whose state should be returned. * @param timeout max number of ms this method should wait to acquire * a read lock on the nodes being transferred * @param force if a read lock cannot be acquired after * timeout ms, should the lock acquisition * be forced, and any existing transactions holding locks * on the nodes be rolled back? NOTE: * In release 1.2.4, this parameter has no effect. * @param suppressErrors should any Throwable thrown be suppressed? * @throws Throwable in event of error */ public void getState(ObjectOutputStream out, Fqn fqn, long timeout, boolean force, boolean suppressErrors) throws Throwable { // can't give state for regions currently being activated/inactivated boolean canProvideState = (!regionManager.isInactive(fqn) && cache.peek(fqn, false) != null); boolean fetchTransientState = configuration.isFetchInMemoryState(); CacheLoaderManager cacheLoaderManager = cache.getCacheLoaderManager(); boolean fetchPersistentState = cacheLoaderManager != null && cacheLoaderManager.isFetchPersistentState(); if (canProvideState && (fetchPersistentState || fetchTransientState)) { marshaller.objectToObjectStream(true, out); StateTransferGenerator generator = getStateTransferGenerator(); Object owner = getOwnerForLock(); long startTime = System.currentTimeMillis(); NodeSPI rootNode = cache.peek(fqn, false, false); try { if (log.isDebugEnabled()) { log.debug("locking the " + fqn + " subtree to return the in-memory (transient) state"); } acquireLocksForStateTransfer(rootNode, owner, timeout, true, force); generator.generateState(out, rootNode, fetchTransientState, fetchPersistentState, suppressErrors); if (log.isDebugEnabled()) { log.debug("Successfully generated state in " + (System.currentTimeMillis() - startTime) + " msec"); } } finally { releaseStateTransferLocks(rootNode, owner, true); } } else { marshaller.objectToObjectStream(false, out); Exception e = null; if (!canProvideState) { String exceptionMessage = "Cache instance at " + cache.getLocalAddress() + " cannot provide state for fqn " + fqn + "."; if (regionManager.isInactive(fqn)) { exceptionMessage += " Region for fqn " + fqn + " is inactive."; e = new InactiveRegionException(exceptionMessage); } // this is not really an exception. Just provide empty state. The exception is just a signal. Yes, lousy. - JBCACHE-1349 if (cache.peek(fqn, false, false) == null) { e = new RegionEmptyException(); } } if (!fetchPersistentState && !fetchTransientState) { e = new CacheException("Cache instance at " + cache.getLocalAddress() + " is not configured to provide state"); } marshaller.objectToObjectStream(e, out); if (e != null) throw e; } } /** * Set the portion of the cache rooted in targetRoot * to match the given state. Updates the contents of targetRoot * to reflect those in new_state. *

* NOTE: This method performs no locking of nodes; it * is up to the caller to lock targetRoot before calling * this method. *

* This method will use any {@link ClassLoader} needed as defined by the active {@link org.jboss.cache.Region} * in the {@link org.jboss.cache.RegionManager}, pertaining to the targetRoot passed in. * * @param in an input stream containing the state * @param targetRoot fqn of the node into which the state should be integrated * @throws Exception In event of error */ public void setState(ObjectInputStream in, Fqn targetRoot) throws Exception { NodeSPI target = cache.peek(targetRoot, false, false); if (target == null) { // Create the integration root, but do not replicate cache.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); //needed for BR state transfers cache.getInvocationContext().getOptionOverrides().setSkipCacheStatusCheck(true); cache.put(targetRoot, null); target = cache.peek(targetRoot, false, false); } Object o = marshaller.objectFromObjectStream(in); Boolean hasState = (Boolean) o; if (hasState) { setState(in, target); } else { throw new CacheException("Cache instance at " + cache.getLocalAddress() + " cannot integrate state since state provider could not provide state due to " + marshaller.objectFromObjectStream(in)); } } /** * Set the portion of the cache rooted in targetRoot * to match the given state. Updates the contents of targetRoot * to reflect those in new_state. *

* NOTE: This method performs no locking of nodes; it * is up to the caller to lock targetRoot before calling * this method. * * @param state a serialized byte[][] array where element 0 is the * transient state (or null) , and element 1 is the * persistent state (or null) * @param targetRoot node into which the state should be integrated */ private void setState(ObjectInputStream state, NodeSPI targetRoot) throws Exception { Object owner = getOwnerForLock(); long timeout = configuration.getStateRetrievalTimeout(); long startTime = System.currentTimeMillis(); try { // Acquire a lock on the root node acquireLocksForStateTransfer(targetRoot, owner, timeout, true, true); /* * Vladimir/Manik/Brian (Dec 7,2006) * * integrator.integrateState(in,targetRoot, cl) will call cache.put for each * node read from stream. Having option override below allows nodes read * to be directly stored into a tree since we bypass interceptor chain. * */ // Option option = new Option(); // option.setBypassInterceptorChain(true); // cache.getInvocationContext().setOptionOverrides(option); // StateTransferIntegrator integrator = getStateTransferIntegrator(state, targetRoot.getFqn()); if (log.isDebugEnabled()) { log.debug("starting state integration at node " + targetRoot); } integrator.integrateState(state, targetRoot); if (log.isDebugEnabled()) { log.debug("successfully integrated state in " + (System.currentTimeMillis() - startTime) + " msec"); } } finally { releaseStateTransferLocks(targetRoot, owner, true); } } /** * Acquires locks on a root node for an owner for state transfer. */ protected void acquireLocksForStateTransfer(NodeSPI root, Object lockOwner, long timeout, boolean lockChildren, boolean force) throws Exception { try { if (lockChildren) { lockManager.lockAll(root, READ, lockOwner, timeout, true); } else { lockManager.lock(Fqn.ROOT, READ, lockOwner, timeout); } } catch (TimeoutException te) { log.error("Caught TimeoutException acquiring locks on region " + root.getFqn(), te); if (force) { // Until we have FLUSH in place, don't force locks // forceAcquireLock(root, lockOwner, lockChildren); throw te; } else { throw te; } } } /** * Releases all state transfer locks acquired. * * @see #acquireLocksForStateTransfer */ protected void releaseStateTransferLocks(NodeSPI root, Object lockOwner, boolean childrenLocked) { try { if (childrenLocked) { lockManager.unlockAll(root, lockOwner); } else { lockManager.unlock(Fqn.ROOT, lockOwner); } } catch (Throwable t) { log.error("failed releasing locks", t); } } protected StateTransferGenerator getStateTransferGenerator() { return StateTransferFactory.getStateTransferGenerator(cache); } protected StateTransferIntegrator getStateTransferIntegrator(ObjectInputStream istream, Fqn fqn) throws Exception { return StateTransferFactory.getStateTransferIntegrator(istream, fqn, cache); } /** * Returns an object suitable for use in node locking, either the current * transaction or the current thread if there is no transaction. */ private Object getOwnerForLock() { Object owner = cache.getCurrentTransaction(); if (owner == null) { owner = Thread.currentThread(); } return owner; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/statetransfer/StateTransferFactory.java0000644000175000017500000000477011032145203032245 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.statetransfer; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.Version; import java.io.IOException; import java.io.ObjectInputStream; /** * Factory class able to create {@link StateTransferGenerator} and * {@link StateTransferIntegrator} instances. * * @author Brian Stansberry * @version $Revision: 6120 $ */ public abstract class StateTransferFactory { private static final short RV_200 = Version.getVersionShort("2.0.0"); private static final Log log = LogFactory.getLog(StateTransferFactory.class); /** * Gets the StateTransferGenerator able to handle the given cache instance. * * @param cache the cache * @return the {@link StateTransferGenerator} * @throws IllegalStateException if the cache's ReplicationVersion is < 2.0.0 */ public static StateTransferGenerator getStateTransferGenerator(CacheSPI cache) { short version = cache.getConfiguration().getReplicationVersion(); // Compiler won't let me use a switch if (version < RV_200 && version > 0) // <= 0 is actually a version > 15.31.63 throw new IllegalStateException("State transfer with cache replication version < 2.0.0 not supported"); else return new DefaultStateTransferGenerator(cache); // current default } public static StateTransferIntegrator getStateTransferIntegrator(ObjectInputStream in, Fqn fqn, CacheSPI cache) throws Exception { short version; try { version = (Short) cache.getMarshaller().objectFromObjectStream(in); } catch (IOException io) { // something is wrong with this stream, close it try { in.close(); } catch (IOException e) { if (log.isWarnEnabled()) log.warn("Unable to close stream!", e); } throw new IllegalStateException("Stream corrupted ", io); } // Compiler won't let me use a switch if (version < RV_200 && version > 0) // <= 0 is actually a version > 15.31.63 throw new IllegalStateException("State transfer with cache replication version < 2.0.0 not supported"); else return new DefaultStateTransferIntegrator(fqn, cache); // current default } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/statetransfer/StateTransferIntegrator.java0000644000175000017500000000053410553427363032767 0ustar twernertwerner/* * JBoss, the OpenSource J2EE webOS * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.statetransfer; import org.jboss.cache.Node; import java.io.ObjectInputStream; public interface StateTransferIntegrator { void integrateState(ObjectInputStream ois, Node target) throws Exception; }jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/ConsoleListener.java0000755000175000017500000001311511004125045026350 0ustar twernertwernerpackage org.jboss.cache; import org.jboss.cache.notifications.annotation.*; import org.jboss.cache.notifications.event.Event; import org.jboss.cache.notifications.event.NodeEvent; import org.jboss.cache.notifications.event.ViewChangedEvent; /** * This class provides a non-graphical view of JBossCache replication * events for a replicated cache. *

* It can be utilized as a standalone application or as a component in other * applications. *

* WARNING: take care when using this class in conjunction with * transactionally replicated cache's as it can cause deadlock situations due to * the reading of values for nodes in the cache. * * @author Jimmy Wilson 12-2004 */ @CacheListener public class ConsoleListener { private CacheSPI cache; private boolean startCache; /** * Constructor. *

* When using this constructor, this class with attempt to start and stop * the specified cache. * * @param cache the cache to monitor for replication events. */ public ConsoleListener(CacheSPI cache) throws Exception { this(cache, true, true); } /** * Constructor. * * @param cache the cache to monitor for replication events. * @param startCache indicates whether or not the cache should be started by * this class. * @param stopCache indicates whether or not the cache should be stopped by * this class. */ public ConsoleListener(CacheSPI cache, boolean startCache, boolean stopCache) throws Exception { this.cache = cache; this.startCache = startCache; if (stopCache) { new ListenerShutdownHook().register(); } } /** * Instructs this class to listen for cache replication events. *

* This method waits indefinately. Use the notify method of this class * (using traditional Java thread notification semantics) to cause this * method to return. */ public void listen() throws Exception { listen(true); } /** * Instructs this class to listen for cache replication events. * * @param wait whether or not this method should wait indefinately. *

* If this parameter is set to true, using the * notify method of this class (using traditional Java thread * notification semantics) will cause this method to return. */ public void listen(boolean wait) throws Exception { cache.getNotifier().addCacheListener(this); if (startCache) { cache.start(); } synchronized (this) { while (wait) { wait(); } } } @CacheStarted @CacheStopped public void printDetails(Event e) { printEvent("Cache started."); } @NodeCreated @NodeLoaded @NodeModified @NodeRemoved @NodeVisited @NodeMoved @NodeEvicted @NodeActivated @NodePassivated public void printDetailsWithFqn(NodeEvent e) { if (e.isPre()) { printEvent("Event " + e.getType() + " on node [" + e.getFqn() + "] about to be invoked"); } else { printEvent("Event " + e.getType() + " on node [" + e.getFqn() + "] invoked"); } } @ViewChanged public void printNewView(ViewChangedEvent e) { printEvent("View change: " + e.getNewView()); } /** * Prints an event message. * * @param eventSuffix the suffix of the event message. */ private void printEvent(String eventSuffix) { System.out.print("EVENT"); System.out.print(' '); System.out.println(eventSuffix); } /** * This class provides a shutdown hook for shutting down the cache. */ private class ListenerShutdownHook extends Thread { /** * Registers this hook for invocation during shutdown. */ public void register() { Runtime.getRuntime().addShutdownHook(this); } /* * Thread overrides. */ @Override public void run() { cache.stop(); } } /** * The main method. * * @param args command line arguments dictated by convention. *

* The first command line argument is the name of the * JBossCache configuration file to be utilized * for configuration of the cache. Only the name of the * configuration file is necessary as it is read off of the * classpath. *

* If a configuration file is not specified on the command line, * jboss-cache.xml will be the assumed file name. *

* All command line arguments after the first are ignored. */ public static void main(String[] args) { final String DEFAULT_CONFIG_FILE_NAME = "jboss-cache.xml"; try { String configFileName = DEFAULT_CONFIG_FILE_NAME; if (args.length >= 1) { configFileName = args[0]; } else { System.out.print("No xml config file argument is supplied. Will use jboss-cache.xml from classpath"); } CacheSPI cache = (CacheSPI) new DefaultCacheFactory().createCache(configFileName); ConsoleListener listener = new ConsoleListener(cache); listener.listen(); } catch (Throwable throwable) { throwable.printStackTrace(); } } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/buddyreplication/0000755000175000017500000000000011376174022025745 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/buddyreplication/NextMemberBuddyLocator.java0000644000175000017500000001556611004116445033200 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.buddyreplication; import net.jcip.annotations.ThreadSafe; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.config.BuddyReplicationConfig.BuddyLocatorConfig; import org.jgroups.Address; import org.jgroups.stack.IpAddress; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.SocketException; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; import java.util.Map; /** * This buddy locator uses a next-in-line algorithm to select buddies for a buddy group. * This algorithm allows for the following properties, all of which are optional. *

*

    *
  • More than one buddy per group - the numBuddies property, defaulting to 1 if ommitted.
  • *
  • Ability to skip buddies on the same host when selecting buddies - the ignoreColocatedBuddies * property, defaulting to true if ommitted. Note that this is just a hint though, and if all nstances in * a cluster are colocated, the algorithm will be forced to pick a colocated instance even if this is property * set to true.
  • *
* * @author Manik Surtani (manik@jboss.org) */ @ThreadSafe public class NextMemberBuddyLocator implements BuddyLocator { private final Log log = LogFactory.getLog(NextMemberBuddyLocator.class); private NextMemberBuddyLocatorConfig config = new NextMemberBuddyLocatorConfig(); public BuddyLocatorConfig getConfig() { return config; } public void init(BuddyLocatorConfig buddyLocatorConfig) { if (buddyLocatorConfig instanceof NextMemberBuddyLocatorConfig) { this.config = (NextMemberBuddyLocatorConfig) buddyLocatorConfig; } else if (buddyLocatorConfig != null) { this.config = new NextMemberBuddyLocatorConfig(buddyLocatorConfig); } else { // We were passed null; just use a default config this.config = new NextMemberBuddyLocatorConfig(); } } public List
locateBuddies(Map buddyPoolMap, List
currentMembership, Address dataOwner) { int numBuddiesToFind = Math.min(config.getNumBuddies(), currentMembership.size()); List
buddies = new ArrayList
(numBuddiesToFind); // find where we are in the list. int dataOwnerSubscript = currentMembership.indexOf(dataOwner); int i = 0; boolean ignoreColocatedBuddiesForSession = config.isIgnoreColocatedBuddies(); while (buddies.size() < numBuddiesToFind) { int subscript = i + dataOwnerSubscript + 1; // make sure we loop around the list if (subscript >= currentMembership.size()) subscript = subscript - currentMembership.size(); // now if subscript is STILL greater than or equal to the current membership size, we've looped around // completely and still havent found any more suitable candidates. Try with colocation hint disabled. if (subscript >= currentMembership.size() && ignoreColocatedBuddiesForSession) { ignoreColocatedBuddiesForSession = false; i = 0; if (log.isInfoEnabled()) { log.info("Expected to look for " + numBuddiesToFind + " buddies but could only find " + buddies.size() + " suitable candidates - trying with colocated buddies as well."); } continue; } // now try disabling the buddy pool if (subscript >= currentMembership.size() && buddyPoolMap != null) { buddyPoolMap = null; ignoreColocatedBuddiesForSession = config.isIgnoreColocatedBuddies();// reset this flag i = 0; if (log.isInfoEnabled()) { log.info("Expected to look for " + numBuddiesToFind + " buddies but could only find " + buddies.size() + " suitable candidates - trying again, ignoring buddy pool hints."); } continue; } // now if subscript is STILL greater than or equal to the current membership size, we've looped around // completely and still havent found any more suitable candidates. Give up with however many we have. if (subscript >= currentMembership.size()) { if (log.isInfoEnabled()) { log.info("Expected to look for " + numBuddiesToFind + " buddies but could only find " + buddies.size() + " suitable candidates!"); } break; } Address candidate = currentMembership.get(subscript); if ( !candidate.equals(dataOwner) && // ignore self from selection as buddy !buddies.contains(candidate) && // havent already considered this candidate (!ignoreColocatedBuddiesForSession || !isColocated(candidate, dataOwner)) && // ignore colocated buddies (isInSameBuddyPool(buddyPoolMap, candidate, dataOwner))// try and find buddies in the same buddy pool first ) { buddies.add(candidate); } i++; } if (log.isTraceEnabled()) log.trace("Selected buddy group as " + buddies); return buddies; } private boolean isInSameBuddyPool(Map buddyPoolMap, Address candidate, Address dataOwner) { if (buddyPoolMap == null) return true; Object ownerPoolName = buddyPoolMap.get(dataOwner); Object candidatePoolName = buddyPoolMap.get(candidate); return !(ownerPoolName == null || candidatePoolName == null) && ownerPoolName.equals(candidatePoolName); } private boolean isColocated(Address candidate, Address dataOwner) { // assume they're both IpAddresses?? InetAddress inetC = ((IpAddress) candidate).getIpAddress(); InetAddress inetD = ((IpAddress) dataOwner).getIpAddress(); if (inetC.equals(inetD)) return true; // now check other interfaces. try { for (Enumeration nics = NetworkInterface.getNetworkInterfaces(); nics.hasMoreElements();) { NetworkInterface i = nics.nextElement(); for (Enumeration addrs = i.getInetAddresses(); addrs.hasMoreElements();) { InetAddress addr = addrs.nextElement(); if (addr.equals(inetC)) return true; } } } catch (SocketException e) { if (log.isDebugEnabled()) log.debug("Unable to read NICs on host", e); if (log.isWarnEnabled()) { log.warn("UNable to read all network interfaces on host " + inetD + " to determine colocation of " + inetC + ". Assuming " + inetC + " is NOT colocated with " + inetD); } } return false; } }jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/buddyreplication/BuddyManager.java0000644000175000017500000012436311075062420031155 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.buddyreplication; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.CacheException; import org.jboss.cache.CacheSPI; import org.jboss.cache.DataContainer; import org.jboss.cache.Fqn; import org.jboss.cache.Node; import org.jboss.cache.RPCManager; import org.jboss.cache.Region; import org.jboss.cache.RegionEmptyException; import org.jboss.cache.RegionManager; import org.jboss.cache.commands.ReplicableCommand; import org.jboss.cache.commands.VisitableCommand; import org.jboss.cache.commands.remote.AnnounceBuddyPoolNameCommand; import org.jboss.cache.commands.remote.AssignToBuddyGroupCommand; import org.jboss.cache.commands.remote.RemoveFromBuddyGroupCommand; import org.jboss.cache.commands.remote.ReplicateCommand; import org.jboss.cache.config.BuddyReplicationConfig; import org.jboss.cache.config.BuddyReplicationConfig.BuddyLocatorConfig; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.Option; import org.jboss.cache.factories.CommandsFactory; import org.jboss.cache.factories.annotations.Inject; import org.jboss.cache.factories.annotations.Start; import org.jboss.cache.factories.annotations.Stop; import org.jboss.cache.io.ExposedByteArrayOutputStream; import org.jboss.cache.lock.TimeoutException; import org.jboss.cache.notifications.Notifier; import org.jboss.cache.notifications.annotation.CacheListener; import org.jboss.cache.notifications.annotation.ViewChanged; import org.jboss.cache.notifications.event.ViewChangedEvent; import org.jboss.cache.statetransfer.StateTransferManager; import org.jboss.cache.util.concurrent.ConcurrentHashSet; import org.jboss.cache.util.reflect.ReflectionUtil; import org.jboss.util.stream.MarshalledValueInputStream; import org.jboss.util.stream.MarshalledValueOutputStream; import org.jgroups.Address; import org.jgroups.Channel; import org.jgroups.View; import org.jgroups.util.Util; import java.io.ByteArrayInputStream; import java.util.ArrayList; import java.util.Collection; 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.SortedSet; import java.util.TreeSet; import java.util.Vector; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; /** * Class that manages buddy replication groups. * * @author Manik Surtani (manik@jboss.org) */ public class BuddyManager { private final Log log = LogFactory.getLog(BuddyManager.class); private boolean trace; BuddyReplicationConfig config; BuddyLocator buddyLocator; Fqn2BuddyFqnVisitor fqnVisitorFqn2; CommandsFactory commandsFactory; /** * back-refernce to the CacheImpl object */ private CacheSPI cache; private Configuration configuration; private RegionManager regionManager; private Notifier notifier; private StateTransferManager stateTransferManager; private RPCManager rpcManager; /** * The buddy group set up for this instance */ BuddyGroup buddyGroup; /** * Map of buddy pools received from broadcasts */ final Map buddyPool = new ConcurrentHashMap(); /** * The nullBuddyPool is a set of addresses that have not specified buddy pools. */ final Set
nullBuddyPool = new ConcurrentHashSet
(); /** * Map of buddy groups the current instance participates in as a backup node. * Keyed on String group name, values are BuddyGroup objects. * Needs to deal with concurrent access - concurrent assignTo/removeFrom buddy grp */ final Map buddyGroupsIParticipateIn = new ConcurrentHashMap(); /** * Queue to deal with queued up view change requests - which are handled asynchronously */ private final BlockingQueue queue = new LinkedBlockingQueue(); /** * Async thread that handles items on the view change queue */ private final AsyncViewChangeHandlerThread asyncViewChangeHandler = new AsyncViewChangeHandlerThread(); /** * Constants representng the buddy backup subtree */ public static final String BUDDY_BACKUP_SUBTREE = "_BUDDY_BACKUP_"; public static final Fqn BUDDY_BACKUP_SUBTREE_FQN = Fqn.fromString(BUDDY_BACKUP_SUBTREE); /** * number of times to retry communicating with a selected buddy if the buddy has not been initialised. */ private final static int UNINIT_BUDDIES_RETRIES = 5; /** * wait time between retries */ private static final long[] UNINIT_BUDDIES_RETRY_NAPTIME = {500, 1000, 1500, 2000, 2500}; /** * Lock to synchronise on to ensure buddy pool info is received before buddies are assigned to groups. */ private final Object poolInfoNotifierLock = new Object(); private final CountDownLatch initialisationLatch = new CountDownLatch(1); // a dummy MembershipChange - a poison-pill to be placed on the membership change queue to notify async handler // threads to exit gracefully when the BuddyManager has been stopped. private static final MembershipChange STOP_NOTIFIER = new MembershipChange(null, null); private ViewChangeListener viewChangeListener; // the view-change viewChangeListener private boolean receivedBuddyInfo; private DataContainer dataContainer; private BuddyFqnTransformer buddyFqnTransformer; public BuddyManager() { } public BuddyManager(BuddyReplicationConfig config) { setupInternals(config); } private void setupInternals(BuddyReplicationConfig config) { this.config = config; trace = log.isTraceEnabled(); BuddyLocatorConfig blc = config.getBuddyLocatorConfig(); try { // it's OK if the buddy locator config is null. buddyLocator = (blc == null) ? createDefaultBuddyLocator() : createBuddyLocator(blc); } catch (Exception e) { log.warn("Caught exception instantiating buddy locator", e); log.error("Unable to instantiate specified buddyLocatorClass [" + blc + "]. Using default buddyLocator [" + NextMemberBuddyLocator.class.getName() + "] instead, with default properties."); buddyLocator = createDefaultBuddyLocator(); } // Update the overall config with the BuddyLocatorConfig actually used if (blc != buddyLocator.getConfig()) { config.setBuddyLocatorConfig(buddyLocator.getConfig()); } } @Inject public void injectDependencies(CacheSPI cache, Configuration configuration, RegionManager regionManager, StateTransferManager stateTransferManager, RPCManager rpcManager, Notifier notifier, CommandsFactory factory, DataContainer dataContainer, BuddyFqnTransformer transformer) { this.cache = cache; this.configuration = configuration; this.regionManager = regionManager; this.stateTransferManager = stateTransferManager; this.rpcManager = rpcManager; this.notifier = notifier; this.commandsFactory = factory; this.dataContainer = dataContainer; buddyFqnTransformer = transformer; } public BuddyReplicationConfig getConfig() { return config; } protected BuddyLocator createBuddyLocator(BuddyLocatorConfig config) throws ClassNotFoundException, IllegalAccessException, InstantiationException { BuddyLocator bl = (BuddyLocator) Class.forName(config.getBuddyLocatorClass()).newInstance(); bl.init(config); return bl; } protected BuddyLocator createDefaultBuddyLocator() { BuddyLocator bl = new NextMemberBuddyLocator(); bl.init(null); return bl; } public boolean isEnabled() { return config.isEnabled(); } public String getBuddyPoolName() { return config.getBuddyPoolName(); } /** * Stops the buddy manager and the related async thread. */ @Stop(priority = 5) public void stop() { if (isEnabled()) { log.debug("Stopping BuddyManager"); // unregister the viewChangeListener if (cache != null) cache.removeCacheListener(viewChangeListener); try { queue.clear(); queue.put(STOP_NOTIFIER); } catch (InterruptedException ie) { // do nothing - we're stopping anyway } } } @Start(priority = 20) public void init() throws CacheException { setupInternals(configuration.getBuddyReplicationConfig()); if (isEnabled()) { log.debug("Starting BuddyManager"); dataContainer.registerInternalFqn(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN); buddyGroup = new BuddyGroup(); buddyGroup.setDataOwner(cache.getLocalAddress()); Address localAddress = rpcManager.getLocalAddress(); if (localAddress == null) { if (configuration.getCacheMode() == Configuration.CacheMode.LOCAL) { log.warn("Buddy replication is enabled but cache mode is LOCAL - not starting BuddyManager!"); ReflectionUtil.setValue(config, "accessible", true); config.setEnabled(false); return; } else { throw new CacheException("Unable to initialize BuddyManager - the RPCManager has not connected to the cluster and local Address is null!"); } } buddyGroup.setGroupName(buddyFqnTransformer.getGroupNameFromAddress(localAddress)); if (config.getBuddyPoolName() != null) { buddyPool.put(buddyGroup.getDataOwner(), config.getBuddyPoolName()); } broadcastBuddyPoolMembership(); if (!cache.exists(BUDDY_BACKUP_SUBTREE_FQN)) cache.getRoot().addChildDirect(BUDDY_BACKUP_SUBTREE_FQN); // allow waiting threads to process. initialisationLatch.countDown(); // register a CacheImpl Listener to reassign buddies as and when view changes occur viewChangeListener = new ViewChangeListener(); cache.addCacheListener(viewChangeListener); // assign buddies based on what we know now reassignBuddies(cache.getMembers()); queue.clear(); asyncViewChangeHandler.start(); initFqnTransformer(buddyGroup.getGroupName(), commandsFactory); } } void initFqnTransformer(String groupName, CommandsFactory commandsFactory) { fqnVisitorFqn2 = new Fqn2BuddyFqnVisitor(groupName, commandsFactory); fqnVisitorFqn2.setBuddyFqnTransformer(buddyFqnTransformer); } public boolean isAutoDataGravitation() { return config.isAutoDataGravitation(); } public boolean isDataGravitationRemoveOnFind() { return config.isDataGravitationRemoveOnFind(); } public boolean isDataGravitationSearchBackupTrees() { return config.isDataGravitationSearchBackupTrees(); } public int getBuddyCommunicationTimeout() { return config.getBuddyCommunicationTimeout(); } // -------------- methods to be called by the tree cache viewChangeListener -------------------- static class MembershipChange { final List
oldMembers; final List
newMembers; public MembershipChange(List
oldMembers, List
newMembers) { this.oldMembers = oldMembers; this.newMembers = newMembers; } @Override public String toString() { return "MembershipChange: Old members = " + oldMembers + " New members = " + newMembers; } } private synchronized void enqueueViewChange(MembershipChange mc) { // put this on a queue try { if (queue.peek() != STOP_NOTIFIER) { //first empty the queue. All queued up view changes that have not been processed yet are now obsolete. queue.clear(); if (trace) log.trace("Enqueueing " + mc + " for async processing"); queue.put(mc); } } catch (InterruptedException e) { log.warn("Caught interrupted exception trying to enqueue a view change event", e); } } /** * Called by the TreeCacheListener when a * view change is detected. Used to find new buddies if * existing buddies have died or if new members to the cluster * have been added. Makes use of the BuddyLocator and then * makes RPC calls to remote nodes to assign/remove buddies. */ private void reassignBuddies(List
members) throws CacheException { List
membership = new ArrayList
(members); // defensive copy if (log.isDebugEnabled()) { log.debug("Data owner address " + cache.getLocalAddress()); log.debug("Entering updateGroup. Current group: " + buddyGroup + ". Current View membership: " + membership); } // some of my buddies have died! List
newBuddies = buddyLocator.locateBuddies(buddyPool, membership, buddyGroup.getDataOwner()); List
unreachableBuddies; if (!(unreachableBuddies = checkBuddyStatus(newBuddies)).isEmpty()) { // some of the new buddies are unreachable. Ditch them, try the algo again. membership.removeAll(unreachableBuddies); newBuddies = buddyLocator.locateBuddies(buddyPool, membership, buddyGroup.getDataOwner()); } List
uninitialisedBuddies = new ArrayList
(); List
originalBuddies = buddyGroup.getBuddies(); for (Address newBuddy : newBuddies) { if (!originalBuddies.contains(newBuddy)) { uninitialisedBuddies.add(newBuddy); } } List
obsoleteBuddies = new ArrayList
(); // find obsolete buddies for (Address origBuddy : originalBuddies) { if (!newBuddies.contains(origBuddy)) { obsoleteBuddies.add(origBuddy); } } // Update buddy list boolean buddyGroupMutated = !obsoleteBuddies.isEmpty() || !uninitialisedBuddies.isEmpty(); if (!obsoleteBuddies.isEmpty()) { removeFromGroup(obsoleteBuddies); } else { log.trace("No obsolete buddies found, nothing to announce."); } if (!uninitialisedBuddies.isEmpty()) { addBuddies(newBuddies); } else { log.trace("No uninitialized buddies found, nothing to announce."); } if (buddyGroupMutated) { if (log.isInfoEnabled()) log.info("Buddy group members have changed. New buddy group: " + buddyGroup); configuration.getRuntimeConfig().setBuddyGroup(buddyGroup); notifier.notifyBuddyGroupChange(buddyGroup, false); } else log.debug("Nothing has changed; new buddy list is identical to the old one."); } /** * Tests whether all members in the list are valid JGroups members. * * @param members * @return */ private List
checkBuddyStatus(List
members) { Channel ch = configuration.getRuntimeConfig().getChannel(); View currentView = ch.getView(); List
deadBuddies = new LinkedList
(); for (Address a : members) if (!currentView.containsMember(a)) deadBuddies.add(a); return deadBuddies; } // -------------- methods to be called by the tree cache -------------------- /** * Called by CacheImpl._remoteAnnounceBuddyPoolName(Address address, String buddyPoolName) * when a view change occurs and caches need to inform the cluster of which buddy pool it is in. */ public void handlePoolNameBroadcast(Address address, String poolName) { if (log.isDebugEnabled()) { log.debug("BuddyManager@" + Integer.toHexString(hashCode()) + ": received announcement that cache instance " + address + " is in buddy pool " + poolName); } if (poolName != null) { buddyPool.put(address, poolName); } else { synchronized (nullBuddyPool) { if (!nullBuddyPool.contains(address)) nullBuddyPool.add(address); } } // notify any waiting view change threads that buddy pool info has been received. synchronized (poolInfoNotifierLock) { log.trace("Notifying any waiting view change threads that we have received buddy pool info."); receivedBuddyInfo = true; poolInfoNotifierLock.notifyAll(); } } /** * Called by CacheImpl._remoteRemoveFromBuddyGroup(String groupName) * when a method call for this is received from a remote cache. */ public void handleRemoveFromBuddyGroup(String groupName) throws BuddyNotInitException { try { if (!initialisationLatch.await(0, TimeUnit.NANOSECONDS)) throw new BuddyNotInitException("Not yet initialised"); } catch (InterruptedException e) { log.debug("Caught InterruptedException", e); } if (log.isInfoEnabled()) log.info("Removing self from buddy group " + groupName); for (Map.Entry me : buddyPool.entrySet()) { if (me.getValue().equals(groupName)) { buddyGroupsIParticipateIn.remove(me.getKey()); break; } } // remove backup data for this group if (log.isInfoEnabled()) log.info("Removing backup data for group " + groupName); try { // should be a LOCAL call. cache.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); cache.removeNode(Fqn.fromRelativeElements(BUDDY_BACKUP_SUBTREE_FQN, groupName)); } catch (CacheException e) { log.error("Unable to remove backup data for group " + groupName, e); } finally { cache.getInvocationContext().getOptionOverrides().setCacheModeLocal(false); } } /** * Called by CacheImpl._remoteAssignToBuddyGroup(BuddyGroup g) when a method * call for this is received from a remote cache. * * @param newGroup the buddy group * @param state Map of any state from the DataOwner. Cannot * be null. */ public void handleAssignToBuddyGroup(BuddyGroup newGroup, Map state) throws Exception { try { if (!initialisationLatch.await(0, TimeUnit.NANOSECONDS)) { if (log.isDebugEnabled()) log.debug("Local buddy mamanger not initialized, rejecting assign call " + newGroup); throw new BuddyNotInitException("Not yet initialised"); } } catch (InterruptedException e) { log.debug("Caught InterruptedException", e); } if (log.isInfoEnabled()) log.info("Assigning self to buddy group " + newGroup); buddyGroupsIParticipateIn.put(newGroup.getDataOwner(), newGroup); // Integrate state transfer from the data owner of the buddy group Fqn integrationBase = buddyFqnTransformer.getBackupRoot(newGroup.getDataOwner()); if (state.isEmpty()) { if (configuredToFetchState()) log.info("Data owner has no state to set, even though buddy is configured to accept state. Assuming there is no data on the data owner."); // create the backup region anyway Option o = cache.getInvocationContext().getOptionOverrides(); o.setSkipCacheStatusCheck(true); o = cache.getInvocationContext().getOptionOverrides(); o.setCacheModeLocal(true); o.setSkipCacheStatusCheck(true); cache.getRoot().addChild(Fqn.fromElements(BUDDY_BACKUP_SUBTREE, newGroup.getGroupName())); } else { for (Map.Entry entry : state.entrySet()) { Fqn fqn = entry.getKey(); if (!regionManager.isInactive(fqn)) { if (trace) log.trace("Integrating state for region " + fqn); //ClassLoader cl = (marshaller == null) ? null : marshaller.getClassLoader(fqnS); Fqn integrationRoot = Fqn.fromRelativeFqn(integrationBase, fqn); byte[] stateBuffer = entry.getValue(); MarshalledValueInputStream in = null; try { ByteArrayInputStream bais = new ByteArrayInputStream(stateBuffer); in = new MarshalledValueInputStream(bais); //stateMgr.setState(in, integrationRoot, cl); stateTransferManager.setState(in, integrationRoot); } catch (Throwable t) { if (t instanceof CacheException) { //excepected/common and can happen due to inactive regions and so on log.debug(t); } else { //something has gone wrong log.error("State for fqn " + fqn + " could not be transferred to a buddy at " + cache.getLocalAddress(), t); } } finally { if (in != null) { in.close(); } } } else { log.trace("Received state for region " + fqn + " but region is inactive"); } } } } /** * Returns a List identifying the DataOwner for each buddy * group for which this node serves as a backup node. */ public List
getBackupDataOwners() { List
owners = new ArrayList
(); for (BuddyGroup group : buddyGroupsIParticipateIn.values()) { owners.add(group.getDataOwner()); } return owners; } // -------------- static util methods ------------------ // -------------- methods to be called by the BaseRPCINterceptor -------------------- /** * Returns a list of buddies for which this instance is Data Owner. * List excludes self. Used by the BaseRPCInterceptor when deciding * who to replicate to. */ public List
getBuddyAddresses() { return buddyGroup.getBuddies(); } /** * Created as an optimisation for JGroups, which uses vectors. * * @since 2.2.0 */ public Vector
getBuddyAddressesAsVector() { return buddyGroup.getBuddiesAsVector(); } public List
getMembersOutsideBuddyGroup() { List
members = new ArrayList
(rpcManager.getMembers()); members.remove(rpcManager.getLocalAddress()); members.removeAll(getBuddyAddresses()); return members; } /** * @see Fqn2BuddyFqnVisitor */ public VisitableCommand transformFqns(VisitableCommand call) { try { VisitableCommand transformed = (VisitableCommand) call.acceptVisitor(null, fqnVisitorFqn2); if (trace) log.trace("Transformed " + call + " to " + transformed); return transformed; } catch (Throwable throwable) { log.error("error while transforming an call", throwable); throw new CacheException(throwable); } } public ReplicateCommand transformReplicateCommand(ReplicateCommand rc) { ReplicateCommand clone = rc.clone(); if (rc.isSingleCommand()) { clone.setSingleModification(transformFqns((VisitableCommand) rc.getSingleModification())); } else { List transformed = new ArrayList(rc.getModifications().size()); for (ReplicableCommand cmd : rc.getModifications()) { transformed.add(transformFqns((VisitableCommand) cmd)); } clone.setModifications(transformed); } return clone; } // -------------- internal helpers methods -------------------- private void removeFromGroup(List
buddies) { if (log.isDebugEnabled()) { log.debug("Removing obsolete buddies from buddy group [" + buddyGroup.getGroupName() + "]. Obsolete buddies are " + buddies); } buddyGroup.removeBuddies(buddies); // now broadcast a message to the removed buddies. RemoveFromBuddyGroupCommand command = commandsFactory.buildRemoveFromBuddyGroupCommand(buddyGroup.getGroupName()); int attemptsLeft = UNINIT_BUDDIES_RETRIES; int currentAttempt = 0; while (attemptsLeft-- > 0) { try { makeRemoteCall(buddies, command); break; } catch (Exception e) { if (e instanceof BuddyNotInitException || e.getCause() instanceof BuddyNotInitException) { if (attemptsLeft > 0) { log.info("One of the buddies have not been initialised. Will retry after a short nap."); try { Thread.sleep(UNINIT_BUDDIES_RETRY_NAPTIME[currentAttempt++]); } catch (InterruptedException e1) { // what do we do? log.trace("Thread interrupted while sleeping/waiting for a retry", e1); } } else { throw new BuddyNotInitException("Unable to contact buddy after " + UNINIT_BUDDIES_RETRIES + " retries"); } } else { log.error("Unable to communicate with Buddy for some reason", e); } } } log.trace("removeFromGroup notification complete"); } @SuppressWarnings("deprecation") private void addBuddies(List
buddies) throws CacheException { if (log.isDebugEnabled()) { log.debug("Assigning new buddies to buddy group [" + buddyGroup.getGroupName() + "]. New buddies are " + buddies); } buddyGroup.addBuddies(buddies); // Create the state transfer map Map stateMap = new HashMap(); if (configuredToFetchState()) { byte[] state; if (configuration.isUseRegionBasedMarshalling()) { Collection regions = regionManager.getAllRegions(Region.Type.MARSHALLING); if (regions.size() > 0) { for (Region r : regions) { Fqn f = r.getFqn(); state = acquireState(f); if (state != null) stateMap.put(f, state); } } else if (!configuration.isInactiveOnStartup()) { // No regions defined; try the root state = acquireState(Fqn.ROOT); if (state != null) { stateMap.put(Fqn.ROOT, state); } } } else { state = acquireState(Fqn.ROOT); if (state != null) { stateMap.put(Fqn.ROOT, state); } } } else { if (trace) log.trace("Not configured to provide state!"); } // now broadcast a message to the newly assigned buddies. AssignToBuddyGroupCommand membershipCall = commandsFactory.buildAssignToBuddyGroupCommand(buddyGroup, stateMap); int attemptsLeft = UNINIT_BUDDIES_RETRIES; int currentAttempt = 0; while (attemptsLeft-- > 0) { try { makeRemoteCall(buddies, membershipCall); break; } catch (Exception e) { if (e instanceof BuddyNotInitException || e.getCause() instanceof BuddyNotInitException) { if (attemptsLeft > 0) { log.info("One of the buddies have not been initialised. Will retry after a short nap."); try { Thread.sleep(UNINIT_BUDDIES_RETRY_NAPTIME[currentAttempt++]); } catch (InterruptedException e1) { // what do we do? log.trace("Thread interrupted while sleeping/waiting for a retry", e1); } } else { throw new BuddyNotInitException("Unable to contact buddy after " + UNINIT_BUDDIES_RETRIES + " retries"); } } else { log.error("Unable to communicate with Buddy for some reason", e); } } } log.trace("addToGroup notification complete"); } private boolean configuredToFetchState() { return configuration.isFetchInMemoryState() || (cache.getCacheLoaderManager() != null && cache.getCacheLoaderManager().isFetchPersistentState()); } private byte[] acquireState(Fqn fqn) throws CacheException { // Call _getState with progressively longer timeouts until we // get state or it doesn't throw a TimeoutException long[] timeouts = {400, 800, 1600, configuration.getStateRetrievalTimeout()}; TimeoutException timeoutException = null; for (int i = 0; i < timeouts.length; i++) { boolean force = (i == timeouts.length - 1); try { byte[] state = generateState(fqn, timeouts[i], force); if (log.isDebugEnabled()) { if (state == null) log.debug("acquireState(): Got null state. Region is probably empty."); else log.debug("acquireState(): Got state"); } return state; } catch (TimeoutException t) { timeoutException = t; if (trace) { log.trace("acquireState(): got a TimeoutException"); } } catch (Exception e) { throw new CacheException("Error acquiring state", e); } catch (Throwable t) { throw new RuntimeException(t); } } // If we got a timeout exception on the final try, // this is a failure condition if (timeoutException != null) { throw new CacheException("acquireState(): Failed getting state due to timeout", timeoutException); } if (log.isDebugEnabled()) { log.debug("acquireState(): Unable to give state"); } return null; } /** * Returns the state for the portion of the cache named by fqn. *

* State returned is a serialized byte[][], element 0 is the transient state * (or null), and element 1 is the persistent state (or null). * * @param fqn Fqn indicating the uppermost node in the * portion of the cache whose state should be returned. * @param timeout max number of ms this method should wait to acquire * a read lock on the nodes being transferred * @param force if a read lock cannot be acquired after * timeout ms, should the lock acquisition * be forced, and any existing transactions holding locks * on the nodes be rolled back? NOTE: * In release 1.2.4, this parameter has no effect. * @return a serialized byte[][], element 0 is the transient state * (or null), and element 1 is the persistent state (or null). */ private byte[] generateState(Fqn fqn, long timeout, boolean force) throws Throwable { MarshalledValueOutputStream out = null; byte[] result = null; try { ExposedByteArrayOutputStream baos = new ExposedByteArrayOutputStream(16 * 1024); out = new MarshalledValueOutputStream(baos); try { stateTransferManager.getState(out, fqn, timeout, force, false); } catch (RegionEmptyException ree) { return null; } result = baos.getRawBuffer(); } finally { Util.close(out); } return result; } /** * Called by the BuddyGroupMembershipMonitor every time a view change occurs. */ private void broadcastBuddyPoolMembership() { broadcastBuddyPoolMembership(null); } private void broadcastBuddyPoolMembership(List

recipients) { // broadcast to other caches if (log.isDebugEnabled()) { log.debug("Instance " + buddyGroup.getDataOwner() + " broadcasting membership in buddy pool " + config.getBuddyPoolName() + " to recipients " + recipients); } AnnounceBuddyPoolNameCommand command = commandsFactory.buildAnnounceBuddyPoolNameCommand(buddyGroup.getDataOwner(), config.getBuddyPoolName()); try { makeRemoteCall(recipients, command); } catch (Exception e) { log.error("Problems broadcasting buddy pool membership info to cluster", e); } } private void makeRemoteCall(List
recipients, ReplicableCommand call) throws Exception { // remove non-members from dest list if (recipients != null) { Iterator
recipientsIt = recipients.iterator(); List
members = cache.getMembers(); while (recipientsIt.hasNext()) { if (!members.contains(recipientsIt.next())) { recipientsIt.remove(); } } } rpcManager.callRemoteMethods(recipients == null ? null : new Vector
(recipients), call, true, config.getBuddyCommunicationTimeout(), false); } private void migrateDefunctData(Node backupRoot, Address dataOwner) { Fqn defunctBackupRootFqn = getDefunctBackupRootFqn(dataOwner); if (trace) log.trace("Migrating defunct data. Backup root is " + backupRoot); if (trace) log.trace("Children of backup root are " + backupRoot.getChildren()); for (Object child : backupRoot.getChildren()) { Fqn childFqn = ((Node) child).getFqn(); cache.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); cache.move(childFqn, defunctBackupRootFqn); } cache.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); backupRoot.getParent().removeChild(backupRoot.getFqn().getLastElement()); } private Fqn getDefunctBackupRootFqn(Address dataOwner) { // the defunct Fqn should be: /_BUDDY_BACKUP_/dataOwnerAddess:DEAD/N // where N is a number. Fqn defunctRoot = buddyFqnTransformer.getDeadBackupRoot(dataOwner); cache.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); Node defunctRootNode = cache.getRoot().addChild(defunctRoot); SortedSet childrenNames = new TreeSet(defunctRootNode.getChildrenNames()); // will be naturally sorted. Integer childName = 1; if (!childrenNames.isEmpty()) { Integer lastChild = (Integer) childrenNames.last(); childName = lastChild + 1; } cache.getInvocationContext().getOptionOverrides().setCacheModeLocal(true); defunctRootNode.addChild(Fqn.fromElements(childName)); return Fqn.fromRelativeElements(defunctRoot, childName); } /** * Asynchronous thread that deals with handling view changes placed on a queue */ private class AsyncViewChangeHandlerThread implements Runnable { private Thread t; private boolean isRunning = true; public void start() { if (t == null || !t.isAlive()) { t = new Thread(this); t.setName("AsyncViewChangeHandlerThread," + cache.getLocalAddress()); t.setDaemon(true); t.start(); } } public void run() { log.trace("Started"); // don't start this thread until the Buddy Manager has initialised as it cocks things up. try { initialisationLatch.await(); } catch (InterruptedException e) { log.debug("Caught InterruptedException", e); } while (!Thread.interrupted() && isRunning) { try { handleEnqueuedViewChange(); } catch (InterruptedException e) { break; } catch (Throwable t) { // Don't let the thread die log.error("Caught exception handling view change", t); } } log.trace("Exiting run()"); } private void handleEnqueuedViewChange() throws Exception { log.trace("Waiting for enqueued view change events"); MembershipChange members = queue.take(); if (members == STOP_NOTIFIER) { log.trace("Caught stop notifier, time to go home."); // time to go home isRunning = false; return; } // there is a strange case where JGroups issues view changes and just includes self in new views, and then // quickly corrects it. Happens intermittently on some unit tests. If this is such a case, please ignore. if (members.newMembers.size() == 1 && members.newMembers.get(0).equals(cache.getLocalAddress())) { log.info("Ignoring membership change event since it only contains self."); return; } broadcastPoolMembership(members); boolean rebroadcast = false; // make sure new buddies have broadcast their pool memberships. while (!buddyPoolInfoAvailable(members.newMembers)) { rebroadcast = true; synchronized (poolInfoNotifierLock) { log.trace("Not received necessary buddy pool info for all new members yet; waiting on poolInfoNotifierLock."); while (!receivedBuddyInfo) poolInfoNotifierLock.wait(); log.trace("Notified!!"); receivedBuddyInfo = false; } } if (rebroadcast) broadcastPoolMembership(members); // always refresh buddy list. reassignBuddies(members.newMembers); // look for missing data owners. // if the view change involves the removal of a data owner of a group in which we participate in, we should // rename the backup the region accordingly, and remove the group from the list in which the current instance participates. if (!members.newMembers.containsAll(buddyGroupsIParticipateIn.keySet())) { Set
toRemove = new HashSet
(); for (Address a : buddyGroupsIParticipateIn.keySet()) { if (!members.newMembers.contains(a)) toRemove.add(a); } for (Address a : toRemove) { BuddyGroup bg = buddyGroupsIParticipateIn.remove(a); Fqn backupRootFqn = buddyFqnTransformer.getBackupRoot(bg.getDataOwner()); Node backupRoot = cache.getNode(backupRootFqn); if (backupRoot != null) { // could be a race condition where the backup region has been removed because we have been removed // from the buddy group, but the buddyGroupsIParticipateIn map hasn't been updated. migrateDefunctData(backupRoot, bg.getDataOwner()); } } } } private void broadcastPoolMembership(MembershipChange members) { log.trace("Broadcasting pool membership details, triggered by view change."); if (members.oldMembers == null) { broadcastBuddyPoolMembership(); } else { List
delta = new ArrayList
(); delta.addAll(members.newMembers); delta.removeAll(members.oldMembers); broadcastBuddyPoolMembership(delta); } } private boolean buddyPoolInfoAvailable(List
newMembers) { boolean infoReceived = true; for (Address address : newMembers) { // make sure no one is concurrently writing to nullBuddyPool. synchronized (nullBuddyPool) { // log.trace("Testing on node " + buddyGroup.getDataOwner() + " for candidate " + address); // log.trace("Is me? " + address.equals(cache.getLocalAddress())); // log.trace("is in bP? " + buddyPool.keySet().contains(address)); // log.trace("is in nBP? " + nullBuddyPool.contains(address)); infoReceived = infoReceived && (address.equals(cache.getLocalAddress()) || buddyPool.keySet().contains(address) || nullBuddyPool.contains(address)); } } if (trace) { log.trace(buddyGroup.getDataOwner() + " received buddy pool info for new members " + newMembers + "? " + infoReceived); } return infoReceived; } public void stop() { if (t != null) t.interrupt(); } } @CacheListener public class ViewChangeListener { private Vector
oldMembers; @ViewChanged public void handleViewChange(ViewChangedEvent event) { View newView = event.getNewView(); if (trace) log.trace("BuddyManager CacheListener - got view change with new view " + newView); Vector
newMembers = newView.getMembers(); // the whole 'oldMembers' concept is only used for buddy pool announcements. if (config.getBuddyPoolName() == null) { enqueueViewChange(new MembershipChange(null, new Vector
(newMembers))); } else { MembershipChange mc = new MembershipChange(oldMembers == null ? null : new Vector
(oldMembers), new Vector
(newMembers)); enqueueViewChange(mc); if (oldMembers == null) oldMembers = new Vector
(); oldMembers.clear(); oldMembers.addAll(newMembers); } } } }jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/buddyreplication/BuddyNotInitException.java0000644000175000017500000000114110537345073033044 0ustar twernertwernerpackage org.jboss.cache.buddyreplication; import org.jboss.cache.CacheException; /** * Exception to depict that a buddy has not been initialised to participate in any comms * * @author Manik Surtani * @since 1.4.1 */ public class BuddyNotInitException extends CacheException { private static final long serialVersionUID = 8968506912780922157L; public BuddyNotInitException() { } public BuddyNotInitException(String msg) { super(msg); } public BuddyNotInitException(String msg, Throwable cause) { super(msg, cause); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/buddyreplication/GravitateResult.java0000755000175000017500000000555211013030101031720 0ustar twernertwernerpackage org.jboss.cache.buddyreplication; import org.jboss.cache.Fqn; import org.jboss.cache.marshall.NodeData; import java.util.List; /** * A class that encapsulates the result of a data gravitation call, a part of the Buddy Replication framwork. A GravitateResult * contains 3 elements; a boolean indicating whether the gravitation request found any data at all, a List of {@link NodeData} objects * containing the data to be gravitated, and an {@link Fqn} of the buddy backup region being gravitated. * * @since 2.0.0 */ public class GravitateResult { private final boolean dataFound; private final List nodeData; private final Fqn buddyBackupFqn; /** * Factory method that creates a GravitateResult indicating that no data has been found. * * @return GravitateResult encapsulating the fact that no data was found */ public static GravitateResult noDataFound() { return new GravitateResult(false, null, null); } /** * Factory method that creates a GravitateResult with the data found and the backup fqn it was found in. * * @param nodeData data found * @param fqn backup fqn the data was found in * @return GravitateResult encapsulating the above */ public static GravitateResult subtreeResult(List nodeData, Fqn fqn) { return new GravitateResult(true, nodeData, fqn); } private GravitateResult(boolean dataFound, List nodeData, Fqn buddyBackupRegion) { this.dataFound = dataFound; this.nodeData = nodeData; this.buddyBackupFqn = buddyBackupRegion; } /** * @return the buddyBackupFqn */ public Fqn getBuddyBackupFqn() { return buddyBackupFqn; } /** * @return true if data was found */ public boolean isDataFound() { return dataFound; } /** * @return the nodeData */ public List getNodeData() { return nodeData; } @Override public String toString() { return "Result dataFound=" + dataFound + " nodeData=" + nodeData + " fqn=" + buddyBackupFqn; } public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; GravitateResult that = (GravitateResult) o; if (dataFound != that.dataFound) return false; if (buddyBackupFqn != null ? !buddyBackupFqn.equals(that.buddyBackupFqn) : that.buddyBackupFqn != null) return false; if (nodeData != null ? !nodeData.equals(that.nodeData) : that.nodeData != null) return false; return true; } public int hashCode() { int result; result = (dataFound ? 1 : 0); result = 31 * result + (nodeData != null ? nodeData.hashCode() : 0); result = 31 * result + (buddyBackupFqn != null ? buddyBackupFqn.hashCode() : 0); return result; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/buddyreplication/BuddyFqnTransformer.java0000644000175000017500000001202611041646535032552 0ustar twernertwernerpackage org.jboss.cache.buddyreplication; import org.jboss.cache.CacheException; import org.jboss.cache.Fqn; import org.jgroups.Address; import java.util.ArrayList; import java.util.List; /** * Knows how to transform between fqn and buddy-formated fqns. * * @author Mircea.Markus@jboss.com * @since 2.2 */ public class BuddyFqnTransformer { public static final String BUDDY_BACKUP_SUBTREE = "_BUDDY_BACKUP_"; public static final Fqn BUDDY_BACKUP_SUBTREE_FQN = Fqn.fromString(BUDDY_BACKUP_SUBTREE); /** * Utility method that retrieves a buddy backup Fqn given the actual Fqn of some data and the data owner's Address. * * @param dataOwnerAddress the JGroups {@link org.jgroups.Address} of the data owner * @param origFqn the original Fqn * @return a backup Fqn */ public Fqn getBackupFqn(Address dataOwnerAddress, Fqn origFqn) { return getBackupFqn(getGroupNameFromAddress(dataOwnerAddress), origFqn); } /** * Utility method that retrieves a buddy backup Fqn given the actual Fqn of some data and the buddy group name. * * @param buddyGroupName the buddy group name * @param origFqn the original Fqn * @return a backup Fqn */ public Fqn getBackupFqn(String buddyGroupName, Fqn origFqn) { if (isBackupFqn(origFqn)) throw new CacheException("Cannot make a backup Fqn from a backup Fqn! Attempting to create a backup of " + origFqn); List elements = new ArrayList(origFqn.size() + 2); elements.add(BuddyManager.BUDDY_BACKUP_SUBTREE); elements.add(buddyGroupName); elements.addAll(origFqn.peekElements()); return Fqn.fromList(elements, true); } /** * Utility method that retrieves a buddy backup Fqn given the actual Fqn of some data and the backup subtree for the * buddy group in question * * @param buddyGroupRoot the subtree under which data for a particular buddy is backed up * @param origFqn the original Fqn * @return a backup Fqn */ public Fqn getBackupFqn(Fqn buddyGroupRoot, Fqn origFqn) { if (isBackupFqn(origFqn)) throw new CacheException("Cannot make a backup Fqn from a backup Fqn! Attempting to create a backup of " + origFqn); List elements = new ArrayList(origFqn.size() + 2); elements.add(BuddyManager.BUDDY_BACKUP_SUBTREE); elements.add(buddyGroupRoot.get(1)); elements.addAll(origFqn.peekElements()); return Fqn.fromList(elements, true); } public boolean isBackupFqn(Fqn name) { return name != null && name.hasElement(BuddyManager.BUDDY_BACKUP_SUBTREE); } public Fqn getActualFqn(Fqn fqn) { if (!isBackupFqn(fqn)) return fqn; if (fqn.equals(BUDDY_BACKUP_SUBTREE_FQN)) return Fqn.ROOT; // remove the first 2 (or 3 in the case of a dead backup region) elements return fqn.getSubFqn(isDeadBackupFqn(fqn) ? 3 : 2, fqn.size()); } /** * Tests whether a given Fqn belongs to a dead backup region. * * @param name fqn to test * @return true if the fqn is a part of a dead backup region; false otherwise. */ @SuppressWarnings("unchecked") public boolean isDeadBackupFqn(Fqn name) { if (name == null || name.size() < 2) return false; Object elem1 = name.get(1); if (elem1 instanceof String) { String strElem1 = (String) elem1; return name.hasElement(BuddyManager.BUDDY_BACKUP_SUBTREE) && strElem1.endsWith(":DEAD"); } else { return false; } } /** * @param dataOwner owner of a data set * @return a backup root for a given data owner */ @SuppressWarnings("unchecked") public Fqn getBackupRoot(Address dataOwner) { return (Fqn) Fqn.fromRelativeElements(BUDDY_BACKUP_SUBTREE_FQN, getGroupNameFromAddress(dataOwner)); } /** * Returns the backup root of a dead data owner * * @param dataOwner owner of data * @return Fqn of dead data owner's root */ @SuppressWarnings("unchecked") public Fqn getDeadBackupRoot(Address dataOwner) { return (Fqn) Fqn.fromRelativeElements(BUDDY_BACKUP_SUBTREE_FQN, getGroupNameFromAddress(dataOwner) + ":DEAD"); } public boolean isDeadBackupRoot(Fqn f) { return f.isDirectChildOf(BUDDY_BACKUP_SUBTREE_FQN) && f.getLastElementAsString().endsWith(":DEAD"); } public String getGroupNameFromAddress(Address address) { return address.toString().replace(':', '_'); } /** * Returns the buddy backp root portion of a given Fqn, provided it is a backup Fqn. If it is not a backup Fqn, Fqn.ROOT is returned. * * @param fqn fqn */ public Fqn getBackupRootFromFqn(Fqn fqn) { if (isBackupFqn(fqn) && fqn.size() > 1) { return fqn.getSubFqn(0, isDeadBackupFqn(fqn) ? 3 : 2); } else { return Fqn.ROOT; } } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/buddyreplication/BuddyGroup.java0000644000175000017500000000463711041646535030710 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.buddyreplication; import net.jcip.annotations.ThreadSafe; import org.jboss.cache.util.ImmutableListCopy; import org.jgroups.Address; import java.io.Serializable; import java.util.Collection; import java.util.Date; import java.util.List; import java.util.Vector; /** * Value object that represents a buddy group * * @author Manik Surtani (manik@jboss.org) */ @ThreadSafe public class BuddyGroup implements Serializable { /** * Serial version. */ private static final long serialVersionUID = 5391883716108410301L; private String groupName; private Address dataOwner; private Date lastModified = new Date(); /** * List
- a list of JGroups addresses */ private final Vector
buddies = new Vector
(); public String getGroupName() { return groupName; } protected void setGroupName(String groupName) { this.groupName = groupName; lastModified = new Date(); } public Address getDataOwner() { return dataOwner; } protected void setDataOwner(Address dataOwner) { this.dataOwner = dataOwner; lastModified = new Date(); } public List
getBuddies() { // defensive copy and immutable. return new ImmutableListCopy
(buddies); } protected void addBuddies(Collection
buddies) { this.buddies.addAll(buddies); lastModified = new Date(); } protected void removeBuddies(Collection
buddies) { this.buddies.removeAll(buddies); lastModified = new Date(); } public Date getLastModified() { return lastModified; } @Override public String toString() { StringBuilder b = new StringBuilder("BuddyGroup: ("); b.append("dataOwner: ").append(dataOwner).append(", "); b.append("groupName: ").append(groupName).append(", "); b.append("buddies: ").append(buddies).append(","); b.append("lastModified: ").append(lastModified).append(")"); return b.toString(); } /** * Added in 2.2.0 as an optimisation for JGroups which internally uses vectors. * * @return a list of buddies * @since 2.2.0 */ public Vector
getBuddiesAsVector() { return buddies; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/buddyreplication/Fqn2BuddyFqnVisitor.java0000644000175000017500000002020411024210175032417 0ustar twernertwernerpackage org.jboss.cache.buddyreplication; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.commands.AbstractVisitor; import org.jboss.cache.commands.ReversibleCommand; import org.jboss.cache.commands.read.ExistsCommand; import org.jboss.cache.commands.read.GetChildrenNamesCommand; import org.jboss.cache.commands.read.GetDataMapCommand; import org.jboss.cache.commands.read.GetKeyValueCommand; import org.jboss.cache.commands.read.GetKeysCommand; import org.jboss.cache.commands.read.GetNodeCommand; import org.jboss.cache.commands.read.GravitateDataCommand; import org.jboss.cache.commands.tx.CommitCommand; import org.jboss.cache.commands.tx.OptimisticPrepareCommand; import org.jboss.cache.commands.tx.PrepareCommand; import org.jboss.cache.commands.tx.RollbackCommand; import org.jboss.cache.commands.write.*; import org.jboss.cache.factories.CommandsFactory; import java.util.ArrayList; import java.util.List; /** * For each command the fqns are changed such that they are under the current buddy group's backup subtree * (e.g., /_buddy_backup_/my_host:7890/) rather than the root (/). * * @author Mircea.Markus@jboss.com * @since 2.2 */ public class Fqn2BuddyFqnVisitor extends AbstractVisitor { private BuddyFqnTransformer buddyFqnTransformer; private final String buddyGroupName; CommandsFactory factory; public Fqn2BuddyFqnVisitor(String buddyGroupName) { this.buddyGroupName = buddyGroupName == null ? "null" : buddyGroupName; } public Fqn2BuddyFqnVisitor(String buddyGroupName, CommandsFactory cf) { this.buddyGroupName = buddyGroupName == null ? "null" : buddyGroupName; this.factory = cf; } @Override public Object visitCommitCommand(InvocationContext ctx, CommitCommand commitCommand) throws Throwable { return commitCommand; } @Override public Object visitPutDataMapCommand(InvocationContext ctx, PutDataMapCommand command) throws Throwable { Fqn transformed = getBackupFqn(command.getFqn()); return factory.buildPutDataMapCommand(null, transformed, command.getData()); } @Override public Object visitPutKeyValueCommand(InvocationContext ctx, PutKeyValueCommand command) throws Throwable { Fqn transformed = getBackupFqn(command.getFqn()); return factory.buildPutKeyValueCommand(null, transformed, command.getKey(), command.getValue()); } @Override public Object visitPutForExternalReadCommand(InvocationContext ctx, PutForExternalReadCommand command) throws Throwable { Fqn transformed = getBackupFqn(command.getFqn()); return factory.buildPutForExternalReadCommand(null, transformed, command.getKey(), command.getValue()); } @Override public Object visitRemoveNodeCommand(InvocationContext ctx, RemoveNodeCommand command) throws Throwable { Fqn transformed = getBackupFqn(command.getFqn()); return factory.buildRemoveNodeCommand(command.getGlobalTransaction(), transformed); } @Override public Object visitClearDataCommand(InvocationContext ctx, ClearDataCommand command) throws Throwable { Fqn transformed = getBackupFqn(command.getFqn()); return factory.buildClearDataCommand(command.getGlobalTransaction(), transformed); } @Override public Object visitEvictFqnCommand(InvocationContext ctx, EvictCommand command) throws Throwable { Fqn fqn = getBackupFqn(command.getFqn()); return factory.buildEvictFqnCommand(fqn); } @Override public Object visitInvalidateCommand(InvocationContext ctx, InvalidateCommand command) throws Throwable { Fqn transformed = getBackupFqn(command.getFqn()); return factory.buildInvalidateCommand(transformed); } @Override public Object visitRemoveKeyCommand(InvocationContext ctx, RemoveKeyCommand command) throws Throwable { Fqn transformed = getBackupFqn(command.getFqn()); return factory.buildRemoveKeyCommand(null, transformed, command.getKey()); } @Override public Object visitGetDataMapCommand(InvocationContext ctx, GetDataMapCommand command) throws Throwable { Fqn transformed = getBackupFqn(command.getFqn()); return factory.buildGetDataMapCommand(transformed); } @Override public Object visitExistsNodeCommand(InvocationContext ctx, ExistsCommand command) throws Throwable { Fqn transformed = getBackupFqn(command.getFqn()); return factory.buildEvictFqnCommand(transformed); } @Override public Object visitGetKeyValueCommand(InvocationContext ctx, GetKeyValueCommand command) throws Throwable { Fqn transformed = getBackupFqn(command.getFqn()); return factory.buildGetKeyValueCommand(transformed, command.getKey(), command.isSendNodeEvent()); } @Override public Object visitGetNodeCommand(InvocationContext ctx, GetNodeCommand command) throws Throwable { Fqn transformed = getBackupFqn(command.getFqn()); return factory.buildGetNodeCommand(transformed); } @Override public Object visitGetKeysCommand(InvocationContext ctx, GetKeysCommand command) throws Throwable { Fqn transformed = getBackupFqn(command.getFqn()); return factory.buildGetKeysCommand(transformed); } @Override public Object visitGetChildrenNamesCommand(InvocationContext ctx, GetChildrenNamesCommand command) throws Throwable { Fqn transformed = getBackupFqn(command.getFqn()); return factory.buildGetChildrenNamesCommand(transformed); } @Override public Object visitMoveCommand(InvocationContext ctx, MoveCommand command) throws Throwable { Fqn transformedFrom = getBackupFqn(command.getFqn()); Fqn transformedTo = getBackupFqn(command.getTo()); return factory.buildMoveCommand(transformedFrom, transformedTo); } @Override public Object visitGravitateDataCommand(InvocationContext ctx, GravitateDataCommand command) throws Throwable { Fqn transformed = getBackupFqn(command.getFqn()); return factory.buildGravitateDataCommand(transformed, command.isSearchSubtrees()); } @Override public Object visitPrepareCommand(InvocationContext ctx, PrepareCommand command) throws Throwable { List toTransform = command.getModifications(); List transformedCommands = transformBatch(toTransform); return factory.buildPrepareCommand(command.getGlobalTransaction(), transformedCommands, command.getLocalAddress(), command.isOnePhaseCommit()); } @Override public Object visitRollbackCommand(InvocationContext ctx, RollbackCommand command) throws Throwable { return factory.buildRollbackCommand(null); } @Override public Object visitOptimisticPrepareCommand(InvocationContext ctx, OptimisticPrepareCommand command) throws Throwable { List transformed = transformBatch(command.getModifications()); return factory.buildOptimisticPrepareCommand(command.getGlobalTransaction(), transformed, command.getData(), command.getLocalAddress(), command.isOnePhaseCommit()); } @Override public Object visitCreateNodeCommand(InvocationContext ctx, CreateNodeCommand command) throws Throwable { return factory.buildCreateNodeCommand(getBackupFqn(command.getFqn())); } public List transformBatch(List toTransform) throws Throwable { List transformedCommands = new ArrayList(toTransform.size()); for (ReversibleCommand com : toTransform) { transformedCommands.add((ReversibleCommand) com.acceptVisitor(null, this)); } return transformedCommands; } /** * Assumes the backup Fqn if the current instance is the data owner. */ public Fqn getBackupFqn(Fqn originalFqn) { return buddyFqnTransformer.getBackupFqn(buddyGroupName, originalFqn); } public void setBuddyFqnTransformer(BuddyFqnTransformer buddyFqnTransformer) { this.buddyFqnTransformer = buddyFqnTransformer; } } ././@LongLink0000000000000000000000000000015200000000000011563 Lustar rootrootjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/buddyreplication/NextMemberBuddyLocatorConfig.javajbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/buddyreplication/NextMemberBuddyLocatorConfig0000644000175000017500000000604011004125045033365 0ustar twernertwernerpackage org.jboss.cache.buddyreplication; import org.jboss.cache.config.BuddyReplicationConfig; import org.jboss.cache.config.BuddyReplicationConfig.BuddyLocatorConfig; import org.jboss.cache.config.Dynamic; import java.util.Properties; /** * Type-specific configuration object for NextMemberBuddyLocator. */ public class NextMemberBuddyLocatorConfig extends BuddyLocatorConfig { private static final long serialVersionUID = 2443438867383733851L; @Dynamic private int numBuddies = 1; @Dynamic private boolean ignoreColocatedBuddies = true; /** * Default constructor. */ public NextMemberBuddyLocatorConfig() { setBuddyLocatorClass(NextMemberBuddyLocator.class.getName()); } /** * Constructor for use by {@link NextMemberBuddyLocator#init(BuddyLocatorConfig)}. * * @param base the config passed in to init(). */ NextMemberBuddyLocatorConfig(BuddyReplicationConfig.BuddyLocatorConfig base) { this(); setBuddyLocatorProperties(base.getBuddyLocatorProperties()); } @Override public String getBuddyLocatorClass() { return NextMemberBuddyLocator.class.getName(); } @Override public void setBuddyLocatorClass(String buddyLocatorClass) { // ignore it } public boolean isIgnoreColocatedBuddies() { return ignoreColocatedBuddies; } public void setIgnoreColocatedBuddies(boolean ignoreColocatedBuddies) { testImmutability("ignoreColocatedBuddies"); this.ignoreColocatedBuddies = ignoreColocatedBuddies; } public int getNumBuddies() { return numBuddies; } public void setNumBuddies(int numBuddies) { testImmutability("numBuddies"); this.numBuddies = numBuddies; } @Override public void setBuddyLocatorProperties(Properties props) { super.setBuddyLocatorProperties(props); if (props != null) { String numBuddiesStr = props.getProperty("numBuddies"); String ignoreColocatedBuddiesStr = props.getProperty("ignoreColocatedBuddies"); if (numBuddiesStr != null) numBuddies = Integer.parseInt(numBuddiesStr); if (ignoreColocatedBuddiesStr != null) { ignoreColocatedBuddies = Boolean.valueOf(ignoreColocatedBuddiesStr); } } } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj instanceof NextMemberBuddyLocatorConfig) { NextMemberBuddyLocatorConfig other = (NextMemberBuddyLocatorConfig) obj; return (other.ignoreColocatedBuddies == this.ignoreColocatedBuddies) && (other.numBuddies == this.numBuddies); } return false; } @Override public int hashCode() { int result = 13; result = 23 * result + (ignoreColocatedBuddies ? 0 : 1); result = 23 * result + numBuddies; return result; } @Override public NextMemberBuddyLocatorConfig clone() throws CloneNotSupportedException { return (NextMemberBuddyLocatorConfig) super.clone(); } }jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/buddyreplication/BuddyLocator.java0000644000175000017500000000514511005374170031203 0ustar twernertwerner/* * JBoss, Home of Professional Open Source * * Distributable under LGPL license. * See terms of license at gnu.org. */ package org.jboss.cache.buddyreplication; import org.jboss.cache.config.BuddyReplicationConfig; import org.jboss.cache.config.BuddyReplicationConfig.BuddyLocatorConfig; import org.jgroups.Address; import java.util.List; import java.util.Map; /** * Buddy Locators help the {@link org.jboss.cache.buddyreplication.BuddyManager} select buddies for its buddy group. *

* Implementations of this class must declare a public no-arguments constructor. *

* * @author Manik Surtani (manik@jboss.org) * @since 1.4.0 */ public interface BuddyLocator { /** * Gets the configuration for this BuddyLocator. * * @return object encapsulating this object's configuration. * Should not return null. If {@link #init(org.jboss.cache.config.BuddyReplicationConfig.BuddyLocatorConfig)} * has not been called or null was passed to it, the * returned value should be the default config for the * given BuddyLocator implementation. */ BuddyLocatorConfig getConfig(); /** * Initialize this BuddyLocator. * * @param config configuration for this BuddyLocator. May be * null, in which case the implementation should * use its default configuration. */ void init(BuddyReplicationConfig.BuddyLocatorConfig config); /** * Choose a set of buddies for the given node. Invoked when a change in * cluster membership is detected. * * @param buddyPoolMap Map mapping nodes in the cluster to * the "buddy pool" they have identified themselves as * belonging too. A BuddyLocator implementation can use * this information to preferentially assign buddies from * the same buddy pool as dataOwner. May be * null if buddy pools aren't configured. * @param currentMembership List
of the current cluster members * @param dataOwner Address of the node for which buddies should be selected * @return List
of the nodes that should serve as buddies for * dataOwner. Will not be null, may * be empty. */ List
locateBuddies(Map buddyPoolMap, List
currentMembership, Address dataOwner); } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/commands/0000755000175000017500000000000011376174023024206 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/commands/VisitableCommand.java0000644000175000017500000000134411005373411030263 0ustar twernertwernerpackage org.jboss.cache.commands; import org.jboss.cache.InvocationContext; /** * A type of command that can accept {@link org.jboss.cache.commands.Visitor}s, such as {@link org.jboss.cache.interceptors.base.CommandInterceptor}s. * * @author Manik Surtani (manik@jboss.org) * @since 2.2.0 */ public interface VisitableCommand extends ReplicableCommand { /** * Accept a visitor, and return the result of accepting this visitor. * * @param ctx invocation context * @param visitor visitor to accept * @return arbitrary return value * @throws Throwable in the event of problems */ Object acceptVisitor(InvocationContext ctx, Visitor visitor) throws Throwable; } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/commands/ReplicableCommand.java0000644000175000017500000000355311006321651030407 0ustar twernertwernerpackage org.jboss.cache.commands; import org.jboss.cache.InvocationContext; /** * The core of the command-based cache framework. Commands correspond to specific areas of functionality in the cache, * and can be replicated using the {@link org.jboss.cache.marshall.Marshaller} framework. * * @author Mircea.Markus@jboss.com * @author Manik Surtani * @since 2.2.0 */ public interface ReplicableCommand extends Cloneable { /** * Performs the primary function of the command. Please see specific implementation classes for details on what is * performed as well as return types. * Important: this method will be invoked at the end of interceptors chain. It should never be called directly from * a custom interceptor. * * @param ctx invocation context * @return arbitrary return value generated by performing this command * @throws Throwable in the event of problems. */ Object perform(InvocationContext ctx) throws Throwable; /** * Used by marshallers to convert this command into an id for streaming. * * @return the method id of this command. This is compatible with pre-2.2.0 MethodCall ids. */ int getCommandId(); /** * Used by marshallers to stream this command across a network * * @return an object array of arguments, compatible with pre-2.2.0 MethodCall args. */ Object[] getParameters(); /** * Used by the {@link org.jboss.cache.factories.CommandsFactory} to create a command from raw data read off a stream. * * @param commandId command id to set. This is usually unused but *could* be used in the event of a command having multiple IDs, such as {@link org.jboss.cache.commands.write.PutKeyValueCommand}. * @param parameters object array of args */ void setParameters(int commandId, Object[] parameters); } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/commands/read/0000755000175000017500000000000011376174023025121 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/commands/read/GetKeysCommand.java0000644000175000017500000000261611032145203030626 0ustar twernertwernerpackage org.jboss.cache.commands.read; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.NodeSPI; import org.jboss.cache.commands.Visitor; /** * Implements functionality defined by {@link org.jboss.cache.Cache#getKeys(org.jboss.cache.Fqn)} *

* This is the equivalent of the old MethodCallDefinitions.getKeysMethodLocal method call from 2.1.x. *

* * @author Mircea.Markus@jboss.com * @since 2.2.0 */ public class GetKeysCommand extends AbstractDataCommand { public static final int METHOD_ID = 25; public GetKeysCommand(Fqn fqn) { this.fqn = fqn; } public GetKeysCommand() { } /** * Retrieves Set of keys for all the data stored in a node referenced by the specified Fqn. * * @param ctx invocation context * @return a Set of data keys contained in a node for a given Fqn, or null if the Fqn refers to a node that does not exist. */ @SuppressWarnings("unchecked") public Object perform(InvocationContext ctx) { NodeSPI n = dataContainer.peek(fqn); if (n == null) return null; return n.getKeysDirect(); } public Object acceptVisitor(InvocationContext ctx, Visitor visitor) throws Throwable { return visitor.visitGetKeysCommand(ctx, this); } public int getCommandId() { return METHOD_ID; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/commands/read/GetKeyValueCommand.java0000644000175000017500000000746411031017377031457 0ustar twernertwernerpackage org.jboss.cache.commands.read; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.DataContainer; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.NodeSPI; import org.jboss.cache.commands.Visitor; import org.jboss.cache.notifications.Notifier; /** * Implements functionality defined by {@link org.jboss.cache.Cache#get(String, Object)} *

* This is the equivalent of the old MethodCallDefinitions.getKeyValueMethodLocal method call from 2.1.x. *

* * @author Mircea.Markus@jboss.com * @since 2.2.0 */ public class GetKeyValueCommand extends AbstractDataCommand { public static final int METHOD_ID = 26; private static final Log log = LogFactory.getLog(GetKeyValueCommand.class); private static boolean trace = log.isTraceEnabled(); private Notifier notifier; private Object key; boolean sendNodeEvent; public GetKeyValueCommand(Fqn fqn, Object key, boolean sendNodeEvent) { this.fqn = fqn; this.key = key; this.sendNodeEvent = sendNodeEvent; } public GetKeyValueCommand() { } public void initialize(DataContainer dataContainer, Notifier notifier) { this.dataContainer = dataContainer; this.notifier = notifier; } /** * Retrieves the value stored under a specified key in a node referenced by the specified Fqn. * * @return an Object of type V, stored under a specific key in a node for a given Fqn, or null if the Fqn refers to a node that does not exist. */ public Object perform(InvocationContext ctx) { if (trace) { log.trace(new StringBuilder("get(").append("\"").append(fqn).append("\", \"").append(key).append("\", \""). append(sendNodeEvent).append("\")")); } NodeSPI n = dataContainer.peek(fqn); if (n == null) { log.trace("node not found"); return null; } if (sendNodeEvent) notifier.notifyNodeVisited(fqn, true, ctx); Object result = n.getDirect(key); if (sendNodeEvent) notifier.notifyNodeVisited(fqn, false, ctx); return result; } public Object acceptVisitor(InvocationContext ctx, Visitor visitor) throws Throwable { return visitor.visitGetKeyValueCommand(ctx, this); } public Object getKey() { return key; } public boolean isSendNodeEvent() { return sendNodeEvent; } public void setKey(Object key) { this.key = key; } public int getCommandId() { return METHOD_ID; } @Override public Object[] getParameters() { return new Object[]{fqn, key, sendNodeEvent}; } @Override public void setParameters(int commandId, Object[] args) { fqn = (Fqn) args[0]; key = args[1]; sendNodeEvent = (Boolean) args[2]; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; if (!super.equals(o)) return false; GetKeyValueCommand that = (GetKeyValueCommand) o; if (sendNodeEvent != that.sendNodeEvent) return false; if (key != null ? !key.equals(that.key) : that.key != null) return false; return true; } @Override public int hashCode() { int result = super.hashCode(); result = 31 * result + (key != null ? key.hashCode() : 0); result = 31 * result + (sendNodeEvent ? 1 : 0); return result; } @Override public String toString() { return "GetKeyValueCommand{" + "fqn=" + fqn + ", key=" + key + ", sendNodeEvent=" + sendNodeEvent + '}'; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/commands/read/GravitateDataCommand.java0000644000175000017500000002016111032145203031766 0ustar twernertwernerpackage org.jboss.cache.commands.read; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.CacheSPI; import org.jboss.cache.DataContainer; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.Node; import org.jboss.cache.NodeSPI; import org.jboss.cache.buddyreplication.BuddyFqnTransformer; import org.jboss.cache.buddyreplication.BuddyManager; import org.jboss.cache.buddyreplication.GravitateResult; import org.jboss.cache.commands.Visitor; import org.jboss.cache.marshall.NodeData; import org.jgroups.Address; import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.TreeSet; /** * Used with buddy replication's {@link org.jboss.cache.interceptors.DataGravitatorInterceptor}. *

* This is the equivalent of the old MethodCallDefinitions.dataGravitationMethod method call from 2.1.x. *

* * @author Manik Surtani * @since 2.2.0 */ public class GravitateDataCommand extends AbstractDataCommand { public static final int METHOD_ID = 35; /* dependencies */ private CacheSPI spi; /* parametres */ private boolean searchSubtrees; private Address localAddress; private static final Log log = LogFactory.getLog(GravitateDataCommand.class); private static boolean trace = log.isTraceEnabled(); private BuddyFqnTransformer buddyFqnTransformer; public GravitateDataCommand(Fqn fqn, boolean searchSubtrees, Address localAddress) { this.fqn = fqn; this.searchSubtrees = searchSubtrees; this.localAddress = localAddress; } public GravitateDataCommand(Address localAddress) { this.localAddress = localAddress; } public void initialize(DataContainer dataContainer, CacheSPI spi, BuddyFqnTransformer transformer) { this.dataContainer = dataContainer; this.spi = spi; buddyFqnTransformer = transformer; } /** * Searches for data to gravitate given an Fqn and whether buddy backup subtrees are to be searched as well. Note that * data stored under the Fqn, along with all children, are retrieved. * * @param ctx invocation context * @return a {@link org.jboss.cache.buddyreplication.GravitateResult} containing node data, as well as information on whether this was found in a primary or backup tree. */ @SuppressWarnings("unchecked") public Object perform(InvocationContext ctx) { // for now, perform a very simple series of getData calls. if (trace) log.trace("Caller is asking for " + fqn); try { ctx.setOriginLocal(false); // use a get() call into the cache to make sure cache loading takes place. // no need to cache the original skipDataGravitation setting here - it will always be false of we got here!! //todo 2.2 use dataContainer for peek and load the data in the CLInterceptor rather than using the SPI for than!!! ctx.getOptionOverrides().setSkipDataGravitation(true); Node actualNode = spi.getNode(fqn); ctx.getOptionOverrides().setSkipDataGravitation(false); if (trace) log.trace("In local tree, this is " + actualNode); Fqn backupNodeFqn = null; if (actualNode == null && searchSubtrees) { log.trace("Looking at backup trees."); NodeSPI backupSubtree = dataContainer.peek(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN); if (backupSubtree != null) { // need to loop through backupSubtree's children Set allGroupNames = backupSubtree.getChildrenNamesDirect(); if (allGroupNames != null) { for (Object groupName : allGroupNames) { // groupName is the name of a buddy group since all child names in this // collection are direct children of BUDDY_BACKUP_SUBTREE_FQN Fqn backupRoot = Fqn.fromRelativeElements(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN, groupName); if (buddyFqnTransformer.isDeadBackupRoot(backupRoot)) { Set deadChildNames = new TreeSet(spi.getChildrenNames(backupRoot)); Integer[] elems = deadChildNames.toArray(new Integer[deadChildNames.size()]); // these are integers. we need to start with the highest/most recent. for (int i = elems.length - 1; i > -1; i--) { Integer versionOfDefunctData = elems[i]; backupNodeFqn = Fqn.fromRelativeFqn(Fqn.fromRelativeElements(backupRoot, versionOfDefunctData), fqn); // use a get() call into the cache to make sure cache loading takes place. ctx.getOptionOverrides().setSkipDataGravitation(true); actualNode = spi.peek(backupNodeFqn, false); ctx.getOptionOverrides().setSkipDataGravitation(false); // break out of the inner loop searching through the dead node's various backups if (actualNode != null) break; } } else { backupNodeFqn = Fqn.fromRelativeFqn(backupRoot, fqn); // use a get() call into the cache to make sure cache loading takes place. ctx.getOptionOverrides().setSkipDataGravitation(true); actualNode = spi.getNode(backupNodeFqn); ctx.getOptionOverrides().setSkipDataGravitation(false); } if (trace) log.trace("Looking for " + backupNodeFqn + ". Search result: " + actualNode); // break out of outer loop searching through all available backups. if (actualNode != null) break; } } } } if (actualNode == null) { return GravitateResult.noDataFound(); } else { // make sure we LOAD data for this node!! actualNode.getData(); } if (backupNodeFqn == null && searchSubtrees) { backupNodeFqn = buddyFqnTransformer.getBackupFqn(buddyFqnTransformer.getGroupNameFromAddress(localAddress), fqn); } List list = dataContainer.buildNodeData(new LinkedList(), (NodeSPI) actualNode); return GravitateResult.subtreeResult(list, backupNodeFqn); } catch (RuntimeException re) { if (trace) log.trace("Caught throwable", re); throw re; } finally { ctx.setOriginLocal(true); } } public Object acceptVisitor(InvocationContext ctx, Visitor visitor) throws Throwable { return visitor.visitGravitateDataCommand(ctx, this); } public int getCommandId() { return METHOD_ID; } public boolean isSearchSubtrees() { return searchSubtrees; } @Override public Object[] getParameters() { return new Object[]{fqn, searchSubtrees}; } @Override public void setParameters(int commandId, Object[] args) { fqn = (Fqn) args[0]; searchSubtrees = (Boolean) args[1]; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; if (!super.equals(o)) return false; GravitateDataCommand that = (GravitateDataCommand) o; if (searchSubtrees != that.searchSubtrees) return false; return true; } @Override public int hashCode() { int result = super.hashCode(); result = 31 * result + (searchSubtrees ? 1 : 0); return result; } @Override public String toString() { return "GravitateDataCommand{" + "fqn=" + fqn + ", searchSubtrees=" + searchSubtrees + '}'; } void setSearchSubtrees(boolean searchSubtrees) { this.searchSubtrees = searchSubtrees; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/commands/read/GetDataMapCommand.java0000644000175000017500000000262611013030101031210 0ustar twernertwernerpackage org.jboss.cache.commands.read; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.NodeSPI; import org.jboss.cache.commands.Visitor; import org.jboss.cache.util.MapCopy; /** * Implements functionality defined by {@link org.jboss.cache.Cache#getData(org.jboss.cache.Fqn)} *

* This is the equivalent of the old MethodCallDefinitions.getDataMapMethodLocal method call from 2.1.x. *

* * @author Mircea.Markus@jboss.com * @since 2.2.0 */ public class GetDataMapCommand extends AbstractDataCommand { public static final int METHOD_ID = 24; public GetDataMapCommand(Fqn fqn) { this.fqn = fqn; } public GetDataMapCommand() { } /** * Retrieves an unmodifiable map of data contained in a node referenced by the specified Fqn. * * @return an unmodifiable Map of data contained in a node for a given Fqn, or null if the Fqn refers to a node that does not exist. */ public Object perform(InvocationContext ctx) { NodeSPI n = dataContainer.peek(fqn); if (n == null) return null; return new MapCopy(n.getDataDirect()); } public Object acceptVisitor(InvocationContext ctx, Visitor visitor) throws Throwable { return visitor.visitGetDataMapCommand(ctx, this); } public int getCommandId() { return METHOD_ID; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/commands/read/GetChildrenNamesCommand.java0000644000175000017500000000377611013030101032424 0ustar twernertwernerpackage org.jboss.cache.commands.read; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.NodeSPI; import org.jboss.cache.commands.Visitor; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Map; import java.util.Set; /** * This command retrieves the names of children for a specific Fqn, as defined by {@link org.jboss.cache.Node#getChildrenNames()} *

* This is the equivalent of the old MethodCallDefinitions.getChildrenNamesMethodLocal method call from 2.1.x. *

* * @author Mircea.Markus@jboss.com * @since 2.2.0 */ public class GetChildrenNamesCommand extends AbstractDataCommand { public static final int METHOD_ID = 23; public GetChildrenNamesCommand() { } public GetChildrenNamesCommand(Fqn fqn) { this.fqn = fqn; } /** * Retrieves the names of children for a specific Fqn. * * @param ctx invocation context * @return a Set of child names, for a given Fqn, or null if the Fqn refers to a node that does not exist. */ public Object perform(InvocationContext ctx) { NodeSPI n = dataContainer.peek(fqn); if (n == null) return null; Map childrenMap = n.getChildrenMapDirect(); if (childrenMap == null || childrenMap.isEmpty()) return Collections.emptySet(); Set childNames = new HashSet(); Collection s = childrenMap.values(); // prune deleted children - JBCACHE-1136 for (Object c : s) { NodeSPI child = (NodeSPI) c; if (!child.isDeleted()) { Object e = child.getFqn().getLastElement(); childNames.add(e); } } return childNames; } public Object acceptVisitor(InvocationContext ctx, Visitor visitor) throws Throwable { return visitor.visitGetChildrenNamesCommand(ctx, this); } public int getCommandId() { return METHOD_ID; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/commands/read/GetNodeCommand.java0000644000175000017500000000227711006366210030607 0ustar twernertwernerpackage org.jboss.cache.commands.read; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.commands.Visitor; /** * Implements functionality defined by {@link org.jboss.cache.Cache#getNode(org.jboss.cache.Fqn)} *

* This is the equivalent of the old MethodCallDefinitions.getNodeMethodLocal method call from 2.1.x. *

* * @author Mircea.Markus@jboss.com * @since 2.2.0 */ public class GetNodeCommand extends AbstractDataCommand { public static final int METHOD_ID = 31; public GetNodeCommand(Fqn fqn) { this.fqn = fqn; } public GetNodeCommand() { } /** * Retrieves a {@link org.jboss.cache.Node} referenced by the specified Fqn. * * @param ctx invocation context * @return a Node, or null if the Fqn refers to a node that does not exist. */ public Object perform(InvocationContext ctx) { return dataContainer.peek(fqn); } public Object acceptVisitor(InvocationContext ctx, Visitor visitor) throws Throwable { return visitor.visitGetNodeCommand(ctx, this); } public int getCommandId() { return METHOD_ID; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/commands/read/AbstractDataCommand.java0000644000175000017500000000310511014645153031613 0ustar twernertwernerpackage org.jboss.cache.commands.read; import org.jboss.cache.DataContainer; import org.jboss.cache.Fqn; import org.jboss.cache.commands.DataCommand; /** * An abstract class providing basic functionality of all {@link DataCommand}s. * * @author Manik Surtani (manik@jboss.org) * @since 2.2.0 */ public abstract class AbstractDataCommand implements DataCommand { protected Fqn fqn; protected DataContainer dataContainer; public void initialize(DataContainer dataContainer) { this.dataContainer = dataContainer; } public Fqn getFqn() { return fqn; } void setFqn(Fqn fqn) { this.fqn = fqn; } /** * Basic versions of these methods */ public Object[] getParameters() { return new Object[]{fqn}; } /** * Basic versions of these methods */ public void setParameters(int commandId, Object[] args) { fqn = (Fqn) args[0]; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; AbstractDataCommand that = (AbstractDataCommand) o; return !(fqn != null ? !fqn.equals(that.fqn) : that.fqn != null); } @Override public int hashCode() { int result; result = (fqn != null ? fqn.hashCode() : 0); result = 31 * result + getClass().hashCode(); return result; } @Override public String toString() { return getClass().getSimpleName() + "{" + "fqn=" + fqn + '}'; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/commands/read/ExistsCommand.java0000644000175000017500000000276311032145203030535 0ustar twernertwernerpackage org.jboss.cache.commands.read; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.commands.Visitor; /** * Checks whether a given node exists in current in-memory state of the cache. * Does not acquire any locks in doing so (result may be dirty read). Does * not attempt to load nodes from a cache loader (may return false if a * node has been evicted). *

* Specifically used by the {@link org.jboss.cache.loader.ClusteredCacheLoader} to * implement {@link org.jboss.cache.loader.CacheLoader#exists(org.jboss.cache.Fqn)} *

* This is the equivalent of the old MethodCallDefinitions.existsMethod method call from 2.1.x. *

* * @author Mircea.Markus@jboss.com * @since 2.2.0 */ public class ExistsCommand extends AbstractDataCommand { public static final int METHOD_ID = 16; public ExistsCommand(Fqn fqn) { this.fqn = fqn; } public ExistsCommand() { } /** * Checks whether a node represented by a given Fqn exists. * Deleted and invalid nodes are not considered. * * @return true if the node exists, false otherwise. */ public Object perform(InvocationContext ctx) { return dataContainer.exists(fqn); } public Object acceptVisitor(InvocationContext ctx, Visitor visitor) throws Throwable { return visitor.visitExistsNodeCommand(ctx, this); } public int getCommandId() { return METHOD_ID; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/commands/Visitor.java0000644000175000017500000002073311032145203026500 0ustar twernertwernerpackage org.jboss.cache.commands; import org.jboss.cache.InvocationContext; import org.jboss.cache.commands.read.ExistsCommand; import org.jboss.cache.commands.read.GetChildrenNamesCommand; import org.jboss.cache.commands.read.GetDataMapCommand; import org.jboss.cache.commands.read.GetKeyValueCommand; import org.jboss.cache.commands.read.GetKeysCommand; import org.jboss.cache.commands.read.GetNodeCommand; import org.jboss.cache.commands.read.GravitateDataCommand; import org.jboss.cache.commands.tx.CommitCommand; import org.jboss.cache.commands.tx.OptimisticPrepareCommand; import org.jboss.cache.commands.tx.PrepareCommand; import org.jboss.cache.commands.tx.RollbackCommand; import org.jboss.cache.commands.write.ClearDataCommand; import org.jboss.cache.commands.write.CreateNodeCommand; import org.jboss.cache.commands.write.EvictCommand; import org.jboss.cache.commands.write.InvalidateCommand; import org.jboss.cache.commands.write.MoveCommand; import org.jboss.cache.commands.write.PutDataMapCommand; import org.jboss.cache.commands.write.PutForExternalReadCommand; import org.jboss.cache.commands.write.PutKeyValueCommand; import org.jboss.cache.commands.write.RemoveKeyCommand; import org.jboss.cache.commands.write.RemoveNodeCommand; /** * This interface is the core of JBoss Cache, where each {@link VisitableCommand} can be visited by a Visitor implementation. * Visitors which are accepted by the {@link org.jboss.cache.commands.VisitableCommand} are able to modify the command * based on any logic encapsulated by the visitor. * * @author Mircea.Markus@jboss.com * @author Manik Surtani * @since 2.2.0 */ public interface Visitor { /** * Visits a PutDataMapCommand. * * @param ctx invocation context * @param command command to visit * @return response from the visit * @throws Throwable in the event of problems. */ Object visitPutDataMapCommand(InvocationContext ctx, PutDataMapCommand command) throws Throwable; /** * Visits a PutKeyValueCommand. * * @param ctx invocation context * @param command command to visit * @return response from the visit * @throws Throwable in the event of problems. */ Object visitPutKeyValueCommand(InvocationContext ctx, PutKeyValueCommand command) throws Throwable; /** * Visits a PutForExternalReadCommand. * * @param ctx invocation context * @param command command to visit * @return response from the visit * @throws Throwable in the event of problems. */ Object visitPutForExternalReadCommand(InvocationContext ctx, PutForExternalReadCommand command) throws Throwable; /** * Visits a RemoveNodeCommand. * * @param ctx invocation context * @param command command to visit * @return response from the visit * @throws Throwable in the event of problems. */ Object visitRemoveNodeCommand(InvocationContext ctx, RemoveNodeCommand command) throws Throwable; /** * Visits a RemoveDataCommand. * * @param ctx invocation context * @param command command to visit * @return response from the visit * @throws Throwable in the event of problems. */ Object visitClearDataCommand(InvocationContext ctx, ClearDataCommand command) throws Throwable; /** * Visits a EvictCommand. * * @param ctx invocation context * @param command command to visit * @return response from the visit * @throws Throwable in the event of problems. */ Object visitEvictFqnCommand(InvocationContext ctx, EvictCommand command) throws Throwable; /** * Visits a InvalidateCommand. * * @param ctx invocation context * @param command command to visit * @return response from the visit * @throws Throwable in the event of problems. */ Object visitInvalidateCommand(InvocationContext ctx, InvalidateCommand command) throws Throwable; /** * Visits a RemoveKeyCommand. * * @param ctx invocation context * @param command command to visit * @return response from the visit * @throws Throwable in the event of problems. */ Object visitRemoveKeyCommand(InvocationContext ctx, RemoveKeyCommand command) throws Throwable; /** * Visits a GetDataMapCommand. * * @param ctx invocation context * @param command command to visit * @return response from the visit * @throws Throwable in the event of problems. */ Object visitGetDataMapCommand(InvocationContext ctx, GetDataMapCommand command) throws Throwable; /** * Visits a RemoteExistsCommand. * * @param ctx invocation context * @param command command to visit * @return response from the visit * @throws Throwable in the event of problems. */ Object visitExistsNodeCommand(InvocationContext ctx, ExistsCommand command) throws Throwable; /** * Visits a GetKeyValueCommand. * * @param ctx invocation context * @param command command to visit * @return response from the visit * @throws Throwable in the event of problems. */ Object visitGetKeyValueCommand(InvocationContext ctx, GetKeyValueCommand command) throws Throwable; /** * Visits a GetNodeCommand. * * @param ctx invocation context * @param command command to visit * @return response from the visit * @throws Throwable in the event of problems. */ Object visitGetNodeCommand(InvocationContext ctx, GetNodeCommand command) throws Throwable; /** * Visits a GetKeysCommand. * * @param ctx invocation context * @param command command to visit * @return response from the visit * @throws Throwable in the event of problems. */ Object visitGetKeysCommand(InvocationContext ctx, GetKeysCommand command) throws Throwable; /** * Visits a GetChildrenNamesCommand. * * @param ctx invocation context * @param command command to visit * @return response from the visit * @throws Throwable in the event of problems. */ Object visitGetChildrenNamesCommand(InvocationContext ctx, GetChildrenNamesCommand command) throws Throwable; /** * Visits a MoveCommand. * * @param ctx invocation context * @param command command to visit * @return response from the visit * @throws Throwable in the event of problems. */ Object visitMoveCommand(InvocationContext ctx, MoveCommand command) throws Throwable; /** * Visits a GravitateDataCommand. * * @param ctx invocation context * @param command command to visit * @return response from the visit * @throws Throwable in the event of problems. */ Object visitGravitateDataCommand(InvocationContext ctx, GravitateDataCommand command) throws Throwable; /** * Visits a PrepareCommand. * * @param ctx invocation context * @param command command to visit * @return response from the visit * @throws Throwable in the event of problems. */ Object visitPrepareCommand(InvocationContext ctx, PrepareCommand command) throws Throwable; /** * Visits a RollbackCommand. * * @param ctx invocation context * @param command command to visit * @return response from the visit * @throws Throwable in the event of problems. */ Object visitRollbackCommand(InvocationContext ctx, RollbackCommand command) throws Throwable; /** * Visits a CommitCommand. * * @param ctx invocation context * @param command command to visit * @return response from the visit * @throws Throwable in the event of problems. */ Object visitCommitCommand(InvocationContext ctx, CommitCommand command) throws Throwable; /** * Visits a OptimisticPrepareCommand. * * @param ctx invocation context * @param command command to visit * @return response from the visit * @throws Throwable in the event of problems. */ Object visitOptimisticPrepareCommand(InvocationContext ctx, OptimisticPrepareCommand command) throws Throwable; /** * Visits a CreateNodeCommand. * * @param ctx invocation context * @param command command to visit * @return response from the visit * @throws Throwable in the event of problems. */ Object visitCreateNodeCommand(InvocationContext ctx, CreateNodeCommand command) throws Throwable; } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/commands/remote/0000755000175000017500000000000011376174023025501 5ustar twernertwerner././@LongLink0000000000000000000000000000015200000000000011563 Lustar rootrootjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/commands/remote/DataGravitationCleanupCommand.javajbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/commands/remote/DataGravitationCleanupCommand0000644000175000017500000001702411041646535033321 0ustar twernertwernerpackage org.jboss.cache.commands.remote; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.DataContainer; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.NodeSPI; import org.jboss.cache.buddyreplication.BuddyFqnTransformer; import org.jboss.cache.buddyreplication.BuddyManager; import org.jboss.cache.commands.ReplicableCommand; import org.jboss.cache.commands.write.EvictCommand; import org.jboss.cache.commands.write.RemoveNodeCommand; import org.jboss.cache.factories.CommandsFactory; import org.jboss.cache.interceptors.InterceptorChain; import org.jboss.cache.transaction.GlobalTransaction; import org.jboss.cache.transaction.TransactionTable; import java.util.List; /** * Data gravitation cleanup handler. Primarily used by the {@link org.jboss.cache.interceptors.DataGravitatorInterceptor}. * This is not a {@link org.jboss.cache.commands.VisitableCommand} and hence * not passed up the {@link org.jboss.cache.interceptors.base.CommandInterceptor} chain. *

* * @author Mircea.Markus@jboss.com * @since 2.2 */ public class DataGravitationCleanupCommand implements ReplicableCommand { public static final int METHOD_ID = 34; private static final Log log = LogFactory.getLog(DataGravitationCleanupCommand.class); private static boolean trace = log.isTraceEnabled(); /* dependencies */ private BuddyManager buddyManager; private TransactionTable transactionTable; private InterceptorChain invoker; private CommandsFactory commandsFactory; private DataContainer dataContainer; /* parameters */ private GlobalTransaction globalTransaction; private Fqn fqn; private Fqn backup; private BuddyFqnTransformer buddyFqnTransformer; public DataGravitationCleanupCommand(Fqn primary, Fqn backup) { this.fqn = primary; this.backup = backup; } public DataGravitationCleanupCommand() { } public void initialize(BuddyManager buddyManager, InterceptorChain invoker, TransactionTable transactionTable, CommandsFactory commandsFactory, DataContainer dataContainer, BuddyFqnTransformer buddyFqnTransformer) { this.buddyManager = buddyManager; this.invoker = invoker; this.transactionTable = transactionTable; this.commandsFactory = commandsFactory; this.dataContainer = dataContainer; this.buddyFqnTransformer = buddyFqnTransformer; } /** * Performs a cleanup on nodes that would have been previously gravitated away from the current cache instance. */ public Object perform(InvocationContext ctx) throws Throwable { if (buddyManager.isDataGravitationRemoveOnFind()) { if (trace) log.trace("DataGravitationCleanup: Removing primary (" + fqn + ") and backup (" + backup + ")"); GlobalTransaction gtx = transactionTable.getCurrentTransaction(); if (!executeRemove(gtx, fqn)) { // only attempt to clean up the backup if the primary did not exist - a waste of a call otherwise. Object result = executeRemove(gtx, backup); if (wasNodeRemoved(result) && buddyFqnTransformer.isDeadBackupFqn(backup) && buddyFqnTransformer.isDeadBackupRoot(backup.getAncestor(backup.size() - 2))) { // if this is a DIRECT child of a DEAD buddy backup region, then remove the empty dead region structural node. NodeSPI deadBackupRoot = dataContainer.peek(backup.getParent(), false); if (deadBackupRoot.getChildrenMapDirect().isEmpty()) { if (trace) log.trace("Removing dead backup region " + deadBackupRoot.getFqn()); executeRemove(gtx, deadBackupRoot.getFqn()); // now check the grand parent and see if we are free of versions deadBackupRoot = dataContainer.peek(deadBackupRoot.getFqn().getParent(), false); if (deadBackupRoot.getChildrenMapDirect().isEmpty()) { if (trace) log.trace("Removing dead backup region " + deadBackupRoot.getFqn()); executeRemove(gtx, deadBackupRoot.getFqn()); } } } } else { if (trace) log.trace("Managed to remove primary (" + fqn + "). Not bothering with backups."); } } else { if (trace) log.trace("DataGravitationCleanup: Evicting primary (" + fqn + ") and backup (" + backup + ")"); evictNode(fqn); evictNode(backup); } return null; } /** * Returns true if such a node was removed. */ private boolean executeRemove(GlobalTransaction gtx, Fqn toRemove) throws Throwable { Object result; RemoveNodeCommand removeBackupCommand = commandsFactory.buildRemoveNodeCommand(gtx, toRemove); InvocationContext ctx = invoker.getInvocationContext(); ctx.getOptionOverrides().setCacheModeLocal(true); result = invoker.invoke(ctx, removeBackupCommand); return result != null && (Boolean) result; } private boolean wasNodeRemoved(Object result) { return result != null && (Boolean) result; } private void evictNode(Fqn fqn) throws Throwable { if (dataContainer.exists(fqn)) { List toEvict = dataContainer.getNodesForEviction(fqn, true); for (Fqn aFqn : toEvict) { EvictCommand evictFqnCommand = commandsFactory.buildEvictFqnCommand(aFqn); invoker.invoke(evictFqnCommand); } } else { if (trace) log.trace("Not evicting " + fqn + " as it doesn't exist"); } } public int getCommandId() { return METHOD_ID; } public Fqn getBackup() { return backup; } public GlobalTransaction getGlobalTransaction() { return globalTransaction; } public void setGlobalTransaction(GlobalTransaction gtx) { this.globalTransaction = gtx; } public Fqn getFqn() { return fqn; } public Object[] getParameters() { return new Object[]{fqn, backup}; //To change body of implemented methods use File | Settings | File Templates. } public void setParameters(int commandId, Object[] args) { fqn = (Fqn) args[0]; backup = (Fqn) args[1]; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; if (!super.equals(o)) return false; DataGravitationCleanupCommand that = (DataGravitationCleanupCommand) o; if (backup != null ? !backup.equals(that.backup) : that.backup != null) return false; if (globalTransaction != null ? !globalTransaction.equals(that.globalTransaction) : that.globalTransaction != null) return false; return true; } @Override public int hashCode() { int result = super.hashCode(); result = 31 * result + (globalTransaction != null ? globalTransaction.hashCode() : 0); result = 31 * result + (backup != null ? backup.hashCode() : 0); return result; } @Override public String toString() { return "DataGravitationCleanupCommand{" + "fqn=" + fqn + ", backup=" + backup + '}'; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/commands/remote/ReplicateCommand.java0000644000175000017500000001721611017455042031555 0ustar twernertwernerpackage org.jboss.cache.commands.remote; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.InvocationContext; import org.jboss.cache.commands.ReplicableCommand; import org.jboss.cache.commands.VisitableCommand; import org.jboss.cache.commands.read.GravitateDataCommand; import org.jboss.cache.commands.write.PutForExternalReadCommand; import org.jboss.cache.interceptors.InterceptorChain; import java.util.List; /** * Command that implements cluster replication logic. Essentially mimics the replicate() and replicateAll() methods * in 2.1.x, we may need to revisit the usefulness of such a command. *

* This is not a {@link org.jboss.cache.commands.VisitableCommand} and hence * not passed up the {@link org.jboss.cache.interceptors.base.CommandInterceptor} chain. *

* * @author Mircea.Markus@jboss.com * @since 2.2 */ public class ReplicateCommand implements ReplicableCommand { public static final int SINGLE_METHOD_ID = 13; public static final int MULTIPLE_METHOD_ID = 14; private InterceptorChain invoker; private static final Log log = LogFactory.getLog(ReplicateCommand.class); private static boolean trace = log.isTraceEnabled(); /** * optimisation - rather than constructing a new list each for scenarios where a single modification needs * to be replicated rather use this instance. */ private ReplicableCommand singleModification; private List modifications; public ReplicateCommand(List modifications) { if (modifications != null && modifications.size() == 1) { singleModification = modifications.get(0); } else { this.modifications = modifications; } } public ReplicateCommand(ReplicableCommand command) { this.singleModification = command; } public ReplicateCommand() { } public void initialize(InterceptorChain interceptorChain) { this.invoker = interceptorChain; } public void setSingleModification(ReplicableCommand singleModification) { this.singleModification = singleModification; } public void setModifications(List modifications) { if (modifications != null && modifications.size() == 1) singleModification = modifications.get(0); else this.modifications = modifications; } /** * Executes commands replicated to the current cache instance by other cache instances. * * @param ctx invocation context, ignored. * @return if this is a single command being processed and it is a {@link org.jboss.cache.commands.read.GravitateDataCommand}, the result of processing this command is returned. Otherwise, null is returned. * @throws Throwable */ public Object perform(InvocationContext ctx) throws Throwable { if (isSingleCommand()) { Object retVal = processSingleCommand(singleModification); // only send back the result of the execution if it is a data gravitation command. // all other commands don't need to send back return values, there will be an unnecessary overhead of // marshalling results that won't ever be used. if (singleModification instanceof GravitateDataCommand) return retVal; else return null; } for (ReplicableCommand command : modifications) processSingleCommand(command); return null; } private Object processSingleCommand(ReplicableCommand cacheCommand) throws Throwable { Object result; try { if (trace) log.trace("Invoking command " + cacheCommand + ", with originLocal flag set to false."); if (cacheCommand instanceof VisitableCommand) { Object retVal = invoker.invokeRemote((VisitableCommand) cacheCommand); // we only need to return values for a set of remote calls; not every call. if (returnValueForRemoteCall(cacheCommand)) { result = retVal; } else { result = null; } } else { result = cacheCommand.perform(null); } } catch (Throwable ex) { if (!(cacheCommand instanceof PutForExternalReadCommand)) { throw ex; } else { if (trace) log.trace("Caught an exception, but since this is a putForExternalRead() call, suppressing the exception. Exception is:", ex); result = null; } } return result; } private boolean returnValueForRemoteCall(ReplicableCommand cacheCommand) { return cacheCommand instanceof GravitateDataCommand || cacheCommand instanceof ClusteredGetCommand; } public int getCommandId() { return isSingleCommand() ? SINGLE_METHOD_ID : MULTIPLE_METHOD_ID; } public List getModifications() { return modifications; } public ReplicableCommand getSingleModification() { return singleModification; } public Object[] getParameters() { if (isSingleCommand()) return new Object[]{singleModification}; else return new Object[]{modifications}; } @SuppressWarnings("unchecked") public void setParameters(int commandId, Object[] args) { if (commandId == SINGLE_METHOD_ID) { singleModification = (ReplicableCommand) args[0]; } else { modifications = (List) args[0]; } } public boolean isSingleCommand() { return singleModification != null; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; ReplicateCommand that = (ReplicateCommand) o; if (modifications != null ? !modifications.equals(that.modifications) : that.modifications != null) return false; if (singleModification != null ? !singleModification.equals(that.singleModification) : that.singleModification != null) return false; return true; } @Override public int hashCode() { int result; result = (singleModification != null ? singleModification.hashCode() : 0); result = 31 * result + (modifications != null ? modifications.hashCode() : 0); return result; } @Override public String toString() { return "ReplicateCommand{" + "cmds=" + (isSingleCommand() ? singleModification : modifications) + '}'; } /** * Clones this replicate command, but with an empty modification list. * * @return a clone */ @Override public ReplicateCommand clone() { ReplicateCommand clone = null; try { clone = (ReplicateCommand) super.clone(); clone.initialize(invoker); } catch (CloneNotSupportedException e) { // should never get here } return clone; } public boolean containsCommandType(Class aClass) { if (isSingleCommand()) { return getSingleModification().getClass().equals(aClass); } else { for (ReplicableCommand command : getModifications()) { if (command.getClass().equals(aClass)) return true; } } return false; } }././@LongLink0000000000000000000000000000015000000000000011561 Lustar rootrootjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/commands/remote/RemoveFromBuddyGroupCommand.javajbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/commands/remote/RemoveFromBuddyGroupCommand.j0000644000175000017500000000447011006112607033234 0ustar twernertwernerpackage org.jboss.cache.commands.remote; import org.jboss.cache.InvocationContext; import org.jboss.cache.buddyreplication.BuddyManager; import org.jboss.cache.commands.ReplicableCommand; /** * Removes a buddy from a group. This is not a {@link org.jboss.cache.commands.VisitableCommand} and hence * not passed up the {@link org.jboss.cache.interceptors.base.CommandInterceptor} chain. *

* * @author Mircea.Markus@jboss.com * @since 2.2 */ public class RemoveFromBuddyGroupCommand implements ReplicableCommand { public static final int METHOD_ID = 30; private BuddyManager buddyManager; private String groupName; public RemoveFromBuddyGroupCommand(String groupName) { this.groupName = groupName; } public RemoveFromBuddyGroupCommand() { } public void initialize(BuddyManager buddyManager) { this.buddyManager = buddyManager; } /** * This method calls the relevant handler on the buddy manager to deal with being removed from a buddy group * * @param ctx invocation context, ignored. * @return null */ public Object perform(InvocationContext ctx) { if (buddyManager != null) buddyManager.handleRemoveFromBuddyGroup(groupName); return null; } public int getCommandId() { return METHOD_ID; } public String getGroupName() { return groupName; } public Object[] getParameters() { return new Object[]{groupName}; } public void setParameters(int commandId, Object[] args) { groupName = (String) args[0]; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; RemoveFromBuddyGroupCommand that = (RemoveFromBuddyGroupCommand) o; if (groupName != null ? !groupName.equals(that.groupName) : that.groupName != null) return false; return true; } @Override public int hashCode() { return (groupName != null ? groupName.hashCode() : 0); } @Override public String toString() { return "RemoveFromBuddyGroupCommand{" + "buddyManager=" + buddyManager + ", groupName='" + groupName + '\'' + '}'; } } ././@LongLink0000000000000000000000000000014600000000000011566 Lustar rootrootjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/commands/remote/AssignToBuddyGroupCommand.javajbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/commands/remote/AssignToBuddyGroupCommand.jav0000644000175000017500000000573311015516661033244 0ustar twernertwernerpackage org.jboss.cache.commands.remote; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.buddyreplication.BuddyGroup; import org.jboss.cache.buddyreplication.BuddyManager; import org.jboss.cache.commands.ReplicableCommand; import java.util.Arrays; import java.util.Map; /** * Assigns a buddy to a group. This is not a {@link org.jboss.cache.commands.VisitableCommand} and hence * not passed up the {@link org.jboss.cache.interceptors.base.CommandInterceptor} chain. *

* * @author Mircea.Markus@jboss.com * @since 2.2.0 */ public class AssignToBuddyGroupCommand implements ReplicableCommand { public static final int METHOD_ID = 29; /* dependencies */ private BuddyManager buddyManager; /* parameters */ private BuddyGroup group; private Map state; public AssignToBuddyGroupCommand(BuddyGroup group, Map state) { this.group = group; this.state = state; } public AssignToBuddyGroupCommand() { } public void initialize(BuddyManager manager) { this.buddyManager = manager; } /** * This method calls the relevant handler on the buddy manager to deal with being assigned to a buddy group * * @param ctx invocation context, ignored. * @return null * @throws Throwable in the event of problems */ public Object perform(InvocationContext ctx) throws Throwable { if (buddyManager != null) buddyManager.handleAssignToBuddyGroup(group, state); return null; } public int getCommandId() { return METHOD_ID; } public BuddyGroup getGroup() { return group; } public Map getState() { return state; } public Object[] getParameters() { return new Object[]{group, state}; } @SuppressWarnings("unchecked") public void setParameters(int commandId, Object[] args) { group = (BuddyGroup) args[0]; state = (Map) args[1]; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; AssignToBuddyGroupCommand that = (AssignToBuddyGroupCommand) o; if (group != null ? !group.equals(that.group) : that.group != null) return false; if (state != null ? !state.equals(that.state) : that.state != null) return false; return true; } @Override public int hashCode() { int result; result = (group != null ? group.hashCode() : 0); result = 31 * result + (state != null ? state.hashCode() : 0); return result; } @Override public String toString() { return "AssignToBuddyGroupCommand{" + "buddyManager=" + buddyManager + ", group=" + group + ", state=" + (state == null ? null : Arrays.asList(state)) + '}'; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/commands/remote/ClusteredGetCommand.java0000644000175000017500000001317311026046072032234 0ustar twernertwernerpackage org.jboss.cache.commands.remote; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.DataContainer; import org.jboss.cache.InvocationContext; import org.jboss.cache.commands.DataCommand; import org.jboss.cache.commands.ReplicableCommand; import org.jboss.cache.commands.read.ExistsCommand; import org.jboss.cache.commands.read.GetChildrenNamesCommand; import org.jboss.cache.commands.read.GetDataMapCommand; import org.jboss.cache.interceptors.InterceptorChain; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * Issues a clustered get call, for use primarily by the {@link org.jboss.cache.loader.ClusteredCacheLoader}. This is * not a {@link org.jboss.cache.commands.VisitableCommand} and hence * not passed up the {@link org.jboss.cache.interceptors.base.CommandInterceptor} chain. *

* * @author Mircea.Markus@jboss.com * @since 2.2.0 */ public class ClusteredGetCommand implements ReplicableCommand { public static final int METHOD_ID = 22; private DataCommand dataCommand; private boolean searchBackupSubtrees; private DataContainer dataContainer; private InterceptorChain interceptorChain; private static final Log log = LogFactory.getLog(ClusteredGetCommand.class); private static boolean trace = log.isTraceEnabled(); public ClusteredGetCommand(boolean searchBackupSubtrees, DataCommand dataCommand) { this.searchBackupSubtrees = searchBackupSubtrees; this.dataCommand = dataCommand; } public ClusteredGetCommand() { } public void initialize(DataContainer dataContainer, InterceptorChain interceptorChain) { this.dataContainer = dataContainer; this.interceptorChain = interceptorChain; } /** * Invokes a {@link org.jboss.cache.commands.DataCommand} on a remote cache and returns results. * * @param context invocation context, ignored. * @return a List containing 2 elements: a boolean, (true or false) and a value (Object) which is the result of invoking a remote get specified by {@link #getDataCommand()}. If buddy replication is used one further element is added - an Fqn of the backup subtree in which this node may be found. */ public Object perform(InvocationContext context) throws Throwable { if (trace) log.trace("Clustered Get called with params: " + dataCommand + ", " + searchBackupSubtrees); Object callResults = null; try { if (trace) log.trace("Clustered get: invoking call with Fqn " + dataCommand.getFqn()); InvocationContext ctx = interceptorChain.getInvocationContext(); ctx.setOriginLocal(false); // very hacky to be calling this command directly. callResults = dataCommand.perform(ctx); boolean found = validResult(callResults); if (trace) log.trace("Got result " + callResults + ", found=" + found); if (found && callResults == null) callResults = createEmptyResults(); } catch (Exception e) { log.warn("Problems processing clusteredGet call", e); } List results = new ArrayList(2); if (callResults != null) { results.add(true); results.add(callResults); } else { results.add(false); results.add(null); } return results; } public int getCommandId() { return METHOD_ID; } /** * Returns true if the call results returned a valid result. */ private boolean validResult(Object callResults) { if (dataCommand instanceof GetDataMapCommand || dataCommand instanceof GetChildrenNamesCommand) { return callResults != null && dataContainer.exists(dataCommand.getFqn()); } return dataCommand instanceof ExistsCommand && (Boolean) callResults; } /** * Creates an empty Collection class based on the return type of the method called. */ private Object createEmptyResults() { if (dataCommand instanceof GetDataMapCommand || dataCommand instanceof GetChildrenNamesCommand) { return Collections.emptyMap(); } return null; } public Boolean getSearchBackupSubtrees() { return searchBackupSubtrees; } public DataCommand getDataCommand() { return dataCommand; } public Object[] getParameters() { return new Object[]{dataCommand, searchBackupSubtrees}; //To change body of implemented methods use File | Settings | File Templates. } public void setParameters(int commandId, Object[] args) { dataCommand = (DataCommand) args[0]; searchBackupSubtrees = (Boolean) args[1]; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; ClusteredGetCommand that = (ClusteredGetCommand) o; if (dataCommand != null ? !dataCommand.equals(that.dataCommand) : that.dataCommand != null) return false; return searchBackupSubtrees == that.searchBackupSubtrees; } @Override public int hashCode() { int result; result = (dataCommand != null ? dataCommand.hashCode() : 0); result = 31 * result + (searchBackupSubtrees ? 1 : 0); return result; } @Override public String toString() { return "ClusteredGetCommand{" + "dataCommand=" + dataCommand + ", searchBackupSubtrees=" + searchBackupSubtrees + '}'; } } ././@LongLink0000000000000000000000000000015100000000000011562 Lustar rootrootjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/commands/remote/AnnounceBuddyPoolNameCommand.javajbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/commands/remote/AnnounceBuddyPoolNameCommand.0000644000175000017500000000645311015516661033200 0ustar twernertwernerpackage org.jboss.cache.commands.remote; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.InvocationContext; import org.jboss.cache.buddyreplication.BuddyManager; import org.jboss.cache.commands.ReplicableCommand; import org.jgroups.Address; /** * Announces a buddy pool name to the cluster. This is not a {@link org.jboss.cache.commands.VisitableCommand} and hence * not passed up the {@link org.jboss.cache.interceptors.base.CommandInterceptor} chain. *

* * @author Mircea.Markus@jboss.com * @since 2.2.0 */ public class AnnounceBuddyPoolNameCommand implements ReplicableCommand { public static final int METHOD_ID = 28; private static final Log log = LogFactory.getLog(AnnounceBuddyPoolNameCommand.class); /* dependencies*/ private BuddyManager buddyManager; /*parameters */ private Address address; private String buddyPoolName; public AnnounceBuddyPoolNameCommand() { } public AnnounceBuddyPoolNameCommand(Address address, String buddyPoolName) { this.address = address; this.buddyPoolName = buddyPoolName; } public void initialize(BuddyManager buddyManager) { this.buddyManager = buddyManager; } /** * This method calls the relevant handler on the buddy manager to deal with this pool broadcast. * * @param ctx invocation context, ignored. * @return null * @throws Throwable in the event of problems */ public Object perform(InvocationContext ctx) throws Throwable { if (buddyManager != null) buddyManager.handlePoolNameBroadcast(address, buddyPoolName); else if (log.isWarnEnabled()) log.warn("Received annouceBuddyPoolName call from [" + address + "] but buddy replication is not enabled on this node!"); return null; } public int getCommandId() { return METHOD_ID; } public Address getAddress() { return address; } public String getBuddyPoolName() { return buddyPoolName; } public Object[] getParameters() { return new Object[]{address, buddyPoolName}; } public void setParameters(int commandId, Object[] args) { address = (Address) args[0]; buddyPoolName = (String) args[1]; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; AnnounceBuddyPoolNameCommand that = (AnnounceBuddyPoolNameCommand) o; if (address != null ? !address.equals(that.address) : that.address != null) return false; if (buddyPoolName != null ? !buddyPoolName.equals(that.buddyPoolName) : that.buddyPoolName != null) return false; return true; } @Override public int hashCode() { int result; result = (address != null ? address.hashCode() : 0); result = 31 * result + (buddyPoolName != null ? buddyPoolName.hashCode() : 0); return result; } @Override public String toString() { return "AnnounceBuddyPoolNameCommand{" + "buddyManager=" + buddyManager + ", address=" + address + ", buddyPoolName='" + buddyPoolName + '\'' + '}'; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/commands/ReversibleCommand.java0000644000175000017500000000176311006321651030450 0ustar twernertwernerpackage org.jboss.cache.commands; import org.jboss.cache.transaction.GlobalTransaction; /** * A reversible command is one that can be rolled back. Also typically has a reference to a {@link org.jboss.cache.transaction.GlobalTransaction}. * * @author Mircea.Markus@jboss.com * @author Manik Surtani (manik@jboss.org) * @since 2.2.0 */ public interface ReversibleCommand extends DataCommand { /** * Reverses a command that has already been invoked. * * Important: this method will be invoked at the end of interceptors chain. It should never be called directly from * a custom interceptor. */ void rollback(); /** * @return a GlobalTransaction associated with this command. */ GlobalTransaction getGlobalTransaction(); /** * Sets a GlobalTransaction on this command. * * @param gtx global transaction to set */ void setGlobalTransaction(GlobalTransaction gtx); } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/commands/tx/0000755000175000017500000000000011376174024024642 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/commands/tx/PrepareCommand.java0000644000175000017500000001020211032145203030357 0ustar twernertwernerpackage org.jboss.cache.commands.tx; import org.jboss.cache.InvocationContext; import org.jboss.cache.commands.ReplicableCommand; import org.jboss.cache.commands.ReversibleCommand; import org.jboss.cache.commands.Visitor; import org.jboss.cache.transaction.GlobalTransaction; import org.jgroups.Address; import java.util.Collection; import java.util.List; /** * The prepare phase of a 2-phase commit, or the single prepare/commit phase of a single-phase commit. * * @author Mircea.Markus@jboss.com * @since 2.2 */ public class PrepareCommand extends AbstractTransactionCommand { public static final int METHOD_ID = 10; protected List modifications; protected Address localAddress; protected boolean onePhaseCommit; public PrepareCommand(GlobalTransaction gtx, List modifications, Address localAddress, boolean onePhaseCommit) { this.globalTransaction = gtx; this.modifications = modifications; this.localAddress = localAddress; this.onePhaseCommit = onePhaseCommit; } public void removeModifications(Collection modificationsToRemove) { if (modifications != null) modifications.removeAll(modificationsToRemove); } public PrepareCommand() { } public Object acceptVisitor(InvocationContext ctx, Visitor visitor) throws Throwable { return visitor.visitPrepareCommand(ctx, this); } public List getModifications() { return modifications; } public Address getLocalAddress() { return localAddress; } public boolean isOnePhaseCommit() { return onePhaseCommit; } public boolean existModifications() { return modifications != null && modifications.size() > 0; } public int getModificationsCount() { return modifications != null ? modifications.size() : 0; } public int getCommandId() { return METHOD_ID; } @Override public Object[] getParameters() { return new Object[]{globalTransaction, modifications, localAddress, onePhaseCommit}; } @Override @SuppressWarnings("unchecked") public void setParameters(int commandId, Object[] args) { globalTransaction = (GlobalTransaction) args[0]; modifications = (List) args[1]; localAddress = (Address) args[2]; onePhaseCommit = (Boolean) args[3]; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; if (!super.equals(o)) return false; PrepareCommand that = (PrepareCommand) o; if (onePhaseCommit != that.onePhaseCommit) return false; if (localAddress != null ? !localAddress.equals(that.localAddress) : that.localAddress != null) return false; if (modifications != null ? !modifications.equals(that.modifications) : that.modifications != null) return false; return true; } @Override public int hashCode() { int result = super.hashCode(); result = 31 * result + (modifications != null ? modifications.hashCode() : 0); result = 31 * result + (localAddress != null ? localAddress.hashCode() : 0); result = 31 * result + (onePhaseCommit ? 1 : 0); return result; } @Override public PrepareCommand clone() throws CloneNotSupportedException { return (PrepareCommand) super.clone(); } @Override public String toString() { return "PrepareCommand{" + "globalTransaction=" + globalTransaction + ", modifications=" + modifications + ", localAddress=" + localAddress + ", onePhaseCommit=" + onePhaseCommit + '}'; } public boolean containsModificationType(Class replicableCommandClass) { for (ReplicableCommand mod : getModifications()) { if (mod.getClass().equals(replicableCommandClass)) { return true; } } return false; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/commands/tx/AbstractTransactionCommand.java0000644000175000017500000000343511006112607032747 0ustar twernertwernerpackage org.jboss.cache.commands.tx; import org.jboss.cache.InvocationContext; import org.jboss.cache.commands.VisitableCommand; import org.jboss.cache.transaction.GlobalTransaction; /** * Base class for transaction boundary commands that deal with global transactions * * @author Manik Surtani (manik@jboss.org) * @since 2.2.0 */ public abstract class AbstractTransactionCommand implements VisitableCommand { protected GlobalTransaction globalTransaction; /** * Default implementation which is a no-op. Transaction boundary commands always return a null. * * @return null */ public Object perform(InvocationContext ctx) { return null; } public GlobalTransaction getGlobalTransaction() { return globalTransaction; } public void setGlobalTransaction(GlobalTransaction gtx) { this.globalTransaction = gtx; } public Object[] getParameters() { return new Object[]{globalTransaction}; } public void setParameters(int commandId, Object[] args) { globalTransaction = (GlobalTransaction) args[0]; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; AbstractTransactionCommand that = (AbstractTransactionCommand) o; if (globalTransaction != null ? !globalTransaction.equals(that.globalTransaction) : that.globalTransaction != null) return false; return true; } @Override public int hashCode() { return (globalTransaction != null ? globalTransaction.hashCode() : 0); } @Override public String toString() { return getClass().getSimpleName() + "{" + "gtx=" + globalTransaction + '}'; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/commands/tx/CommitCommand.java0000644000175000017500000000146311006112607030225 0ustar twernertwernerpackage org.jboss.cache.commands.tx; import org.jboss.cache.InvocationContext; import org.jboss.cache.commands.Visitor; import org.jboss.cache.transaction.GlobalTransaction; /** * Represents a commit() call, the 2nd part of a 2-phase commit. * * @author Mircea.Markus@jboss.com * @since 2.2 */ public class CommitCommand extends AbstractTransactionCommand { public static final int METHOD_ID = 11; public CommitCommand(GlobalTransaction globalTransaction) { this.globalTransaction = globalTransaction; } public CommitCommand() { } public Object acceptVisitor(InvocationContext ctx, Visitor visitor) throws Throwable { return visitor.visitCommitCommand(ctx, this); } public int getCommandId() { return METHOD_ID; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/commands/tx/OptimisticPrepareCommand.java0000644000175000017500000000524711012534035032444 0ustar twernertwernerpackage org.jboss.cache.commands.tx; import org.jboss.cache.InvocationContext; import org.jboss.cache.commands.ReversibleCommand; import org.jboss.cache.commands.Visitor; import org.jboss.cache.transaction.GlobalTransaction; import org.jgroups.Address; import java.util.List; import java.util.Map; /** * An optimistic version of {@link PrepareCommand}. * * @author Mircea.Markus@jboss.com * @since 2.2 */ public class OptimisticPrepareCommand extends PrepareCommand { public static final int METHOD_ID = 18; private Map data; public OptimisticPrepareCommand(GlobalTransaction gtx, List modifications, Map data, Address address, boolean onePhaseCommit) { super(gtx, modifications, address, onePhaseCommit); this.data = data; } public OptimisticPrepareCommand() { } @Override public Object acceptVisitor(InvocationContext ctx, Visitor visitor) throws Throwable { return visitor.visitOptimisticPrepareCommand(ctx, this); } public Map getData() { return data; } @Override public int getCommandId() { return METHOD_ID; } @Override public Object[] getParameters() { return new Object[]{globalTransaction, modifications, data, localAddress, onePhaseCommit}; } @Override public OptimisticPrepareCommand clone() throws CloneNotSupportedException { return (OptimisticPrepareCommand) super.clone(); } @Override @SuppressWarnings("unchecked") public void setParameters(int commandId, Object[] args) { globalTransaction = (GlobalTransaction) args[0]; modifications = (List) args[1]; data = (Map) args[2]; localAddress = (Address) args[3]; onePhaseCommit = (Boolean) args[4]; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; if (!super.equals(o)) return false; OptimisticPrepareCommand that = (OptimisticPrepareCommand) o; if (data != null ? !data.equals(that.data) : that.data != null) return false; return true; } @Override public int hashCode() { int result = super.hashCode(); result = 31 * result + (data != null ? data.hashCode() : 0); return result; } @Override public String toString() { return "OptimisticPrepareCommand{" + "data=" + data + "modifications=" + modifications + ", localAddress=" + localAddress + ", onePhaseCommit=" + onePhaseCommit + '}'; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/commands/tx/RollbackCommand.java0000644000175000017500000000144511006112607030526 0ustar twernertwernerpackage org.jboss.cache.commands.tx; import org.jboss.cache.InvocationContext; import org.jboss.cache.commands.Visitor; import org.jboss.cache.transaction.GlobalTransaction; /** * The rollback phase of a 2-phase commit. * * @author Mircea.Markus@jboss.com * @since 2.2 */ public class RollbackCommand extends AbstractTransactionCommand { public static final int METHOD_ID = 12; public RollbackCommand(GlobalTransaction globalTransaction) { this.globalTransaction = globalTransaction; } public RollbackCommand() { } public Object acceptVisitor(InvocationContext ctx, Visitor visitor) throws Throwable { return visitor.visitRollbackCommand(ctx, this); } public int getCommandId() { return METHOD_ID; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/commands/AbstractVisitor.java0000644000175000017500000001355011032145203030163 0ustar twernertwernerpackage org.jboss.cache.commands; import org.jboss.cache.InvocationContext; import org.jboss.cache.commands.read.ExistsCommand; import org.jboss.cache.commands.read.GetChildrenNamesCommand; import org.jboss.cache.commands.read.GetDataMapCommand; import org.jboss.cache.commands.read.GetKeyValueCommand; import org.jboss.cache.commands.read.GetKeysCommand; import org.jboss.cache.commands.read.GetNodeCommand; import org.jboss.cache.commands.read.GravitateDataCommand; import org.jboss.cache.commands.tx.CommitCommand; import org.jboss.cache.commands.tx.OptimisticPrepareCommand; import org.jboss.cache.commands.tx.PrepareCommand; import org.jboss.cache.commands.tx.RollbackCommand; import org.jboss.cache.commands.write.ClearDataCommand; import org.jboss.cache.commands.write.CreateNodeCommand; import org.jboss.cache.commands.write.EvictCommand; import org.jboss.cache.commands.write.InvalidateCommand; import org.jboss.cache.commands.write.MoveCommand; import org.jboss.cache.commands.write.PutDataMapCommand; import org.jboss.cache.commands.write.PutForExternalReadCommand; import org.jboss.cache.commands.write.PutKeyValueCommand; import org.jboss.cache.commands.write.RemoveKeyCommand; import org.jboss.cache.commands.write.RemoveNodeCommand; import java.util.Collection; /** * An abstract implementation of a Visitor that delegates all visit calls to a default handler which can be overridden. * * @author Mircea.Markus@jboss.com * @author Manik Surtani * @since 2.2.0 */ public abstract class AbstractVisitor implements Visitor { public Object visitPutDataMapCommand(InvocationContext ctx, PutDataMapCommand command) throws Throwable { return handleDefault(ctx, command); } public Object visitPutKeyValueCommand(InvocationContext ctx, PutKeyValueCommand command) throws Throwable { return handleDefault(ctx, command); } public Object visitPutForExternalReadCommand(InvocationContext ctx, PutForExternalReadCommand command) throws Throwable { return handleDefault(ctx, command); } public Object visitRemoveNodeCommand(InvocationContext ctx, RemoveNodeCommand command) throws Throwable { return handleDefault(ctx, command); } public Object visitClearDataCommand(InvocationContext ctx, ClearDataCommand command) throws Throwable { return handleDefault(ctx, command); } public Object visitEvictFqnCommand(InvocationContext ctx, EvictCommand command) throws Throwable { return handleDefault(ctx, command); } public Object visitInvalidateCommand(InvocationContext ctx, InvalidateCommand command) throws Throwable { return handleDefault(ctx, command); } public Object visitRemoveKeyCommand(InvocationContext ctx, RemoveKeyCommand command) throws Throwable { return handleDefault(ctx, command); } public Object visitGetDataMapCommand(InvocationContext ctx, GetDataMapCommand command) throws Throwable { return handleDefault(ctx, command); } public Object visitExistsNodeCommand(InvocationContext ctx, ExistsCommand command) throws Throwable { return handleDefault(ctx, command); } public Object visitGetKeyValueCommand(InvocationContext ctx, GetKeyValueCommand command) throws Throwable { return handleDefault(ctx, command); } public Object visitGetNodeCommand(InvocationContext ctx, GetNodeCommand command) throws Throwable { return handleDefault(ctx, command); } public Object visitGetKeysCommand(InvocationContext ctx, GetKeysCommand command) throws Throwable { return handleDefault(ctx, command); } public Object visitGetChildrenNamesCommand(InvocationContext ctx, GetChildrenNamesCommand command) throws Throwable { return handleDefault(ctx, command); } public Object visitMoveCommand(InvocationContext ctx, MoveCommand command) throws Throwable { return handleDefault(ctx, command); } public Object visitGravitateDataCommand(InvocationContext ctx, GravitateDataCommand command) throws Throwable { return handleDefault(ctx, command); } public Object visitPrepareCommand(InvocationContext ctx, PrepareCommand command) throws Throwable { return handleDefault(ctx, command); } public Object visitRollbackCommand(InvocationContext ctx, RollbackCommand command) throws Throwable { return handleDefault(ctx, command); } public Object visitCommitCommand(InvocationContext ctx, CommitCommand command) throws Throwable { return handleDefault(ctx, command); } public Object visitOptimisticPrepareCommand(InvocationContext ctx, OptimisticPrepareCommand command) throws Throwable { return handleDefault(ctx, command); } public Object visitCreateNodeCommand(InvocationContext ctx, CreateNodeCommand command) throws Throwable { return handleDefault(ctx, command); } /** * A default handler for all commands visited. This is called for any visit method called, unless a visit command is * appropriately overridden. * * @param ctx invocation context * @param command command to handle * @return return value * @throws Throwable in the case of a problem */ protected Object handleDefault(InvocationContext ctx, VisitableCommand command) throws Throwable { return null; } /** * Helper method to visit a collection of VisitableCommands. * * @param ctx Invocation context * @param toVisit collection of commands to visit * @throws Throwable in the event of problems */ public void visitCollection(InvocationContext ctx, Collection toVisit) throws Throwable { for (VisitableCommand command : toVisit) { command.acceptVisitor(ctx, this); } } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/commands/VersionedDataCommand.java0000644000175000017500000000171311005373411031071 0ustar twernertwernerpackage org.jboss.cache.commands; import org.jboss.cache.optimistic.DataVersion; /** * Just like a {@link org.jboss.cache.commands.DataCommand}, except that these are versioned and reversible too. Versioning * is currently used by optimistic locking, and in future will be used by MVCC as well. * * @author Mircea.Markus@jboss.com * @author Manik Surtani (manik@jboss.org) * @since 2.2.0 */ public interface VersionedDataCommand extends ReversibleCommand { /** * @return the DataVersion pertaining to this command. */ DataVersion getDataVersion(); /** * Sets the DataVersion pertaining to this command. * * @param dataVersion to set */ void setDataVersion(DataVersion dataVersion); /** * Has data version set? (i.e. not null) * * @return true if getDataVersion() would not return null; false otherwise. */ boolean isVersioned(); } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/commands/write/0000755000175000017500000000000011376174022025337 5ustar twernertwernerjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/commands/write/EvictCommand.java0000644000175000017500000000646511031154111030547 0ustar twernertwernerpackage org.jboss.cache.commands.write; import org.jboss.cache.DataContainer; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.NodeSPI; import org.jboss.cache.commands.Visitor; import org.jboss.cache.commands.read.AbstractDataCommand; import org.jboss.cache.notifications.Notifier; import java.util.List; /** * Implements functionality defined by {@link org.jboss.cache.Cache#evict(org.jboss.cache.Fqn)} * * @author Mircea.Markus@jboss.com * @since 2.2 */ public class EvictCommand extends AbstractDataCommand { public static final int METHOD_ID = 8; public static final int VERSIONED_METHOD_ID = 9; private boolean recursive = false; protected Notifier notifier; public EvictCommand(Fqn fqn) { this.fqn = fqn; } public EvictCommand() { } public void initialize(Notifier notifier, DataContainer dataContainer) { super.initialize(dataContainer); this.notifier = notifier; } /** * Evicts a node. *

* See {@link org.jboss.cache.interceptors.EvictionInterceptor#visitEvictFqnCommand(org.jboss.cache.InvocationContext, EvictCommand)} * which is where the return value is used * * @return true if the node was removed from the tree or if it is resident. Returns false if the node still exists; i.e. was only data removed because it still has children. */ public Object perform(InvocationContext ctx) { NodeSPI node = dataContainer.peek(fqn, false, true); if (node == null) { return false; } else if (node.isResident()) { return true; } else if (recursive) { List nodesToEvict = dataContainer.getNodesForEviction(fqn, true); for (Fqn aFqn : nodesToEvict) { evictNode(aFqn, ctx); } return !nodesToEvict.isEmpty(); } else { return evictNode(fqn, ctx); } } boolean evictNode(Fqn fqn, InvocationContext ctx) { notifier.notifyNodeEvicted(fqn, true, ctx); try { return dataContainer.evict(fqn); } finally { notifier.notifyNodeEvicted(fqn, false, ctx); } } public Object acceptVisitor(InvocationContext ctx, Visitor visitor) throws Throwable { return visitor.visitEvictFqnCommand(ctx, this); } public int getCommandId() { return METHOD_ID; } public boolean isRecursive() { return recursive; } public void setRecursive(boolean recursive) { this.recursive = recursive; } @Override public Object[] getParameters() { throw new UnsupportedOperationException(getClass().getSimpleName() + " is not meant to be marshalled and replicated!"); } @Override public void setParameters(int commandId, Object[] args) { throw new UnsupportedOperationException(getClass().getSimpleName() + " is not meant to be marshalled and replicated!"); } @Override public String toString() { return "EvictCommand{" + "fqn=" + fqn + ", recursive=" + recursive + "}"; } public void rollback() { // this is a no-op. } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/commands/write/InvalidateCommand.java0000644000175000017500000000660611032145203031555 0ustar twernertwernerpackage org.jboss.cache.commands.write; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.CacheSPI; import org.jboss.cache.DataContainer; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.NodeSPI; import org.jboss.cache.commands.Visitor; import org.jboss.cache.commands.read.AbstractDataCommand; import org.jboss.cache.notifications.Notifier; /** * Removes a node's content from memory - never removes the node. * It also clenups data for resident nodes - which are not being touched by eviction. * * @author Mircea.Markus@jboss.com * @since 2.2 */ public class InvalidateCommand extends AbstractDataCommand { public static final int METHOD_ID = 47; private static final Log log = LogFactory.getLog(InvalidateCommand.class); private static boolean trace = log.isTraceEnabled(); /* dependencies*/ protected CacheSPI spi; protected Notifier notifier; public InvalidateCommand(Fqn fqn) { this.fqn = fqn; } public InvalidateCommand() { } public void initialize(CacheSPI cacheSpi, DataContainer dataContainer, Notifier notifier) { this.spi = cacheSpi; this.dataContainer = dataContainer; this.notifier = notifier; } /** * Performs an invalidation on a specified node * * @param ctx invocation context * @return null */ public Object perform(InvocationContext ctx) { NodeSPI node = enforceNodeLoading(); if (trace) log.trace("Invalidating fqn:" + fqn); if (node == null) { return null; } evictNode(fqn, ctx); invalidateNode(node); return null; } boolean evictNode(Fqn fqn, InvocationContext ctx) { notifier.notifyNodeEvicted(fqn, true, ctx); try { return dataContainer.evict(fqn); } finally { notifier.notifyNodeEvicted(fqn, false, ctx); } } /** * //TODO: 2.2.0: rather than using CacheSPI this should use peek(). The other interceptors should obtain locks and load nodes if necessary for this InvalidateCommand. * //Even better - this can be handles in the interceptors before call interceptor */ protected NodeSPI enforceNodeLoading() { return spi.getNode(fqn); } /** * mark the node to be removed (and all children) as invalid so anyone holding a direct reference to it will * be aware that it is no longer valid. */ protected void invalidateNode(NodeSPI node) { node.setValid(false, true); // root nodes can never be invalid if (fqn.isRoot()) node.setValid(true, false); // non-recursive. } public Object acceptVisitor(InvocationContext ctx, Visitor visitor) throws Throwable { return visitor.visitInvalidateCommand(ctx, this); } public int getCommandId() { return METHOD_ID; } @Override public String toString() { return "InvalidateCommand{" + "fqn=" + fqn + '}'; } @Override public Object[] getParameters() { return new Object[]{fqn}; } @Override public void setParameters(int commandId, Object[] args) { fqn = (Fqn) args[0]; } void setFqn(Fqn newFqn) { this.fqn = newFqn; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/commands/write/PutKeyValueCommand.java0000644000175000017500000001250411031017377031716 0ustar twernertwernerpackage org.jboss.cache.commands.write; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.CacheException; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.NodeSPI; import org.jboss.cache.commands.Visitor; import org.jboss.cache.notifications.event.NodeModifiedEvent; import org.jboss.cache.optimistic.DataVersion; import org.jboss.cache.transaction.GlobalTransaction; import java.util.Collections; import java.util.Map; /** * Implements functionality defined by {@link org.jboss.cache.Cache#put(org.jboss.cache.Fqn, Object, Object)}. * * @author Mircea.Markus@jboss.com * @since 2.2 */ public class PutKeyValueCommand extends AbstractVersionedDataCommand { public static final int METHOD_ID = 3; public static final int VERSIONED_METHOD_ID = 39; private static final Log log = LogFactory.getLog(PutKeyValueCommand.class); private static boolean trace = log.isTraceEnabled(); /* parametres */ protected Object key; protected Object value; protected Object oldValue; public PutKeyValueCommand(GlobalTransaction gtx, Fqn fqn, Object key, Object value) { this.globalTransaction = gtx; this.fqn = fqn; this.key = key; this.value = value; } public PutKeyValueCommand() { } /** * Puts the specified key and value into the data map in the node referenced by the specified Fqn. * * @param ctx invocation context * @return the value being overwritten, if any, otherwise a null. */ public Object perform(InvocationContext ctx) { if (trace) { log.trace(new StringBuilder("perform(").append(globalTransaction).append(", \""). append(fqn).append("\", k=").append(key).append(", v=").append(value).append(")")); } NodeSPI n = dataContainer.peekStrict(globalTransaction, fqn, false); if (notifier.shouldNotifyOnNodeModified()) { notifier.notifyNodeModified(fqn, true, NodeModifiedEvent.ModificationType.PUT_DATA, n.getDataDirect(), ctx); } oldValue = n.putDirect(key, value); if (notifier.shouldNotifyOnNodeModified()) { Map newData = Collections.singletonMap(key, value); notifier.notifyNodeModified(fqn, false, NodeModifiedEvent.ModificationType.PUT_DATA, newData, ctx); } return oldValue; } public void rollback() { NodeSPI n = dataContainer.peek(fqn, false, false); if (n == null) throw new CacheException("node " + fqn + " not found for rollback!"); if (oldValue == null) { n.removeDirect(key); } else { n.putDirect(key, oldValue); } } public Object acceptVisitor(InvocationContext ctx, Visitor visitor) throws Throwable { return visitor.visitPutKeyValueCommand(ctx, this); } public Object getKey() { return key; } public Object getValue() { return value; } public void setKey(Object key) { this.key = key; } public void setValue(Object value) { this.value = value; } public int getCommandId() { if (isVersioned()) { return VERSIONED_METHOD_ID; } else { return METHOD_ID; } } @Override public Object[] getParameters() { if (isVersioned()) return new Object[]{globalTransaction, fqn, key, value, false, dataVersion}; else return new Object[]{globalTransaction, fqn, key, value, false}; } @Override public void setParameters(int commandId, Object[] args) { globalTransaction = (GlobalTransaction) args[0]; fqn = (Fqn) args[1]; key = args[2]; value = args[3]; if (isVersionedId(commandId)) dataVersion = (DataVersion) args[5]; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; if (!super.equals(o)) return false; PutKeyValueCommand that = (PutKeyValueCommand) o; if (globalTransaction != null ? !globalTransaction.equals(that.globalTransaction) : that.globalTransaction != null) return false; if (key != null ? !key.equals(that.key) : that.key != null) return false; if (value != null ? !value.equals(that.value) : that.value != null) return false; return true; } @Override public int hashCode() { int result = super.hashCode(); result = 31 * result + (globalTransaction != null ? globalTransaction.hashCode() : 0); result = 31 * result + (key != null ? key.hashCode() : 0); result = 31 * result + (value != null ? value.hashCode() : 0); return result; } @Override protected boolean isVersionedId(int commandId) { return commandId == VERSIONED_METHOD_ID; } @Override public String toString() { return getClass().getSimpleName() + "{" + "fqn=" + fqn + ", dataVersion=" + dataVersion + ", globalTransaction=" + globalTransaction + ", key=" + key + ", value=" + value + ", oldValue=" + oldValue + '}'; } } ././@LongLink0000000000000000000000000000014700000000000011567 Lustar rootrootjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/commands/write/OptimisticInvalidateCommand.javajbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/commands/write/OptimisticInvalidateCommand.ja0000644000175000017500000001216411032145203033267 0ustar twernertwernerpackage org.jboss.cache.commands.write; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.CacheException; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.NodeSPI; import org.jboss.cache.commands.VersionedDataCommand; import org.jboss.cache.config.Option; import org.jboss.cache.optimistic.DataVersion; import org.jboss.cache.transaction.GlobalTransaction; import javax.transaction.Transaction; import javax.transaction.TransactionManager; import java.util.Collections; /** * Behaves like {@link org.jboss.cache.commands.write.InvalidateCommand}. Also, potentially throws a cache exception if * data versioning is used and the node in memory has a newer data version than what is passed in. *

* Finally, the data version of the in-memory node is updated to the version being evicted to prevent versions * going out of sync. *

* * @author Mircea.Markus@jboss.com * @since 2.2 */ public class OptimisticInvalidateCommand extends InvalidateCommand implements VersionedDataCommand { private static final Log log = LogFactory.getLog(OptimisticInvalidateCommand.class); private static boolean trace = log.isTraceEnabled(); /* dependencies */ private TransactionManager transactionManager; /** * Params. */ protected GlobalTransaction globalTransaction; private DataVersion dataVersion; public OptimisticInvalidateCommand(Fqn fqn) { super(fqn); } public OptimisticInvalidateCommand() { } public void initialize(TransactionManager txManager) { this.transactionManager = txManager; } @Override public Object perform(InvocationContext ctx) { NodeSPI node = enforceNodeLoading(); if (trace) log.trace("Invalidating fqn:" + fqn); if (node == null) { // check if a tombstone already exists NodeSPI nodeSPI = dataContainer.peek(fqn, false, true); if (nodeSPI == null) { if (dataVersion == null) { if (trace) log.trace("Would have created a tombstone since the node doesn't exist, but the version to invalidate is null and hence cannot create a tombstone!"); return null; } createTombstone(ctx); nodeSPI = (NodeSPI) dataContainer.getRoot().getChild(fqn); } node = nodeSPI; } removeData(ctx); invalidateNode(node); updateDataVersion(); return null; } protected void createTombstone(InvocationContext ctx) { if (trace) log.trace("Node doesn't exist; creating a tombstone with data version " + dataVersion); // create the node we need. Option o = ctx.getOptionOverrides(); boolean origCacheModeLocal = o.isCacheModeLocal(); o.setCacheModeLocal(true); o.setDataVersion(dataVersion); // if we are in a tx this call should happen outside of any tx try { Transaction suspended = null; if (transactionManager != null) { suspended = transactionManager.suspend(); } spi.put(fqn, Collections.emptyMap()); if (suspended != null) transactionManager.resume(suspended); ctx.getOptionOverrides().setCacheModeLocal(origCacheModeLocal); } catch (Exception e) { log.error("Unable to create tombstone!", e); } } private void updateDataVersion() { if (dataVersion != null) { NodeSPI n = dataContainer.peek(fqn, false, true); n.setVersion(dataVersion); } } protected void removeData(InvocationContext ctx) throws CacheException { NodeSPI n = dataContainer.peekVersioned(fqn, dataVersion); if (n == null) { log.warn("node " + fqn + " not found"); return; } notifier.notifyNodeEvicted(fqn, true, ctx); n.clearDataDirect(); n.setDataLoaded(false); notifier.notifyNodeEvicted(fqn, false, ctx); } public DataVersion getDataVersion() { return dataVersion; } public void setDataVersion(DataVersion dataVersion) { this.dataVersion = dataVersion; } public GlobalTransaction getGlobalTransaction() { return globalTransaction; } public void setGlobalTransaction(GlobalTransaction gtx) { this.globalTransaction = gtx; } public boolean isVersioned() { return dataVersion != null; } @Override public String toString() { return "OptimisticInvalidateCommand{" + "dataVersion=" + dataVersion + " ,fqn=" + fqn + '}'; } @Override public Object[] getParameters() { return new Object[]{fqn, dataVersion}; } @Override public void setParameters(int commandId, Object[] args) { fqn = (Fqn) args[0]; dataVersion = (DataVersion) args[1]; } public void rollback() { //no op } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/commands/write/RemoveNodeCommand.java0000644000175000017500000001375111023757460031555 0ustar twernertwernerpackage org.jboss.cache.commands.write; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.NodeSPI; import org.jboss.cache.commands.Visitor; import org.jboss.cache.optimistic.DataVersion; import org.jboss.cache.transaction.GlobalTransaction; import java.util.HashMap; import java.util.Map; /** * Implements functionality defined by {@link org.jboss.cache.CacheSPI#removeNode(org.jboss.cache.Fqn)} * * @author Mircea.Markus@jboss.com * @since 2.2 */ public class RemoveNodeCommand extends AbstractVersionedDataCommand { public static final int METHOD_ID = 5; public static final int VERSIONED_METHOD_ID = 40; private static final Log log = LogFactory.getLog(RemoveNodeCommand.class); private static boolean trace = log.isTraceEnabled(); /*parameters*/ private boolean skipSendingNodeEvents = false; protected Fqn parentFqn; protected NodeSPI targetNode; protected Map originalData; public RemoveNodeCommand(GlobalTransaction globalTransaction, Fqn fqn) { this.globalTransaction = globalTransaction; this.fqn = fqn; } public RemoveNodeCommand() { } /** * Removes the node referenced by the specified Fqn. */ public Object perform(InvocationContext ctx) { if (trace) log.trace("perform(" + globalTransaction + ", \"" + fqn + ")"); // Find the node targetNode = dataContainer.peekVersioned(fqn, dataVersion, true); if (targetNode == null) { if (trace) log.trace("node " + fqn + " not found"); return false; } notifyBeforeRemove(targetNode, ctx); NodeSPI parentNode = targetNode.getParent(); boolean found = targetNode.isValid() && !targetNode.isDeleted(); targetNode.markAsDeleted(true, true); if (globalTransaction != null && found) { prepareForRollback(parentNode); } notifyAfterRemove(ctx); return found; } private void prepareForRollback(NodeSPI parentNode) { parentFqn = parentNode.getFqn(); Map targetData = targetNode.getDataDirect(); if (!targetData.isEmpty()) { originalData = new HashMap(targetNode.getDataDirect()); } } private void notifyBeforeRemove(NodeSPI n, InvocationContext ctx) { if (!skipSendingNodeEvents) { notifier.notifyNodeRemoved(fqn, true, n.getDataDirect(), ctx); } } private void notifyAfterRemove(InvocationContext ctx) { if (!skipSendingNodeEvents) { notifier.notifyNodeRemoved(fqn, false, null, ctx); } } public void rollback() { if (targetNode != null) { Object childName = targetNode.getFqn().getLastElement(); if (trace) { log.trace("rollback(parent: " + parentFqn + ", child: " + childName + ", node=" + targetNode + ")"); } if (parentFqn == null || childName == null) { log.error("parent fqn or childName or childNode was null"); return; } NodeSPI parentNode = dataContainer.peek(parentFqn); if (parentNode == null) { log.warn("node " + parentFqn + " not found"); return; } parentNode.addChild(childName, targetNode); targetNode.markAsDeleted(false, true); targetNode.clearDataDirect(); if (originalData != null) targetNode.putAllDirect(originalData); targetNode.setValid(true, true); } } public Object acceptVisitor(InvocationContext ctx, Visitor visitor) throws Throwable { return visitor.visitRemoveNodeCommand(ctx, this); } public boolean isSkipSendingNodeEvents() { return skipSendingNodeEvents; } public int getCommandId() { return isVersioned() ? VERSIONED_METHOD_ID : METHOD_ID; } @Override public Object[] getParameters() { if (isVersioned()) return new Object[]{globalTransaction, fqn, true, skipSendingNodeEvents, dataVersion}; else return new Object[]{globalTransaction, fqn, true, skipSendingNodeEvents}; } @Override public void setParameters(int commandId, Object[] args) { globalTransaction = (GlobalTransaction) args[0]; fqn = (Fqn) args[1]; skipSendingNodeEvents = (Boolean) args[3]; if (isVersionedId(commandId)) dataVersion = (DataVersion) args[4]; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; if (!super.equals(o)) return false; RemoveNodeCommand that = (RemoveNodeCommand) o; if (skipSendingNodeEvents != that.skipSendingNodeEvents) return false; if (globalTransaction != null ? !globalTransaction.equals(that.globalTransaction) : that.globalTransaction != null) return false; return true; } @Override public int hashCode() { int result = super.hashCode(); result = 31 * result + (globalTransaction != null ? globalTransaction.hashCode() : 0); result = 31 * result + (skipSendingNodeEvents ? 1 : 0); return result; } @Override protected boolean isVersionedId(int id) { return id == VERSIONED_METHOD_ID; } public void setSkipSendingNodeEvents(boolean skipSendingNodeEvents) { this.skipSendingNodeEvents = skipSendingNodeEvents; } @Override public String toString() { return "RemoveNodeCommand{" + "fqn=" + fqn + ", dataVersion=" + dataVersion + ", globalTransaction=" + globalTransaction + ", skipSendingNodeEvents=" + skipSendingNodeEvents + ", parentFqn=" + parentFqn + ", targetNode=" + targetNode + '}'; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/commands/write/CreateNodeCommand.java0000644000175000017500000000442711014645153031516 0ustar twernertwernerpackage org.jboss.cache.commands.write; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.NodeSPI; import org.jboss.cache.commands.ReversibleCommand; import org.jboss.cache.commands.Visitor; import org.jboss.cache.commands.read.AbstractDataCommand; import org.jboss.cache.transaction.GlobalTransaction; import java.util.LinkedList; import java.util.List; /** * Command that creates a node. Primarily to be used as an undo command for removing nodes. * * @author Manik Surtani (manik@jboss.org) * @since 2.2.0 */ public class CreateNodeCommand extends AbstractDataCommand implements ReversibleCommand { public static final int METHOD_ID = 48; private final List newlyCreated = new LinkedList(); public CreateNodeCommand(Fqn fqn) { this.fqn = fqn; newlyCreated.add(fqn); } public CreateNodeCommand() { } public int getCommandId() { return METHOD_ID; } public void setGlobalTransaction(GlobalTransaction gtx) { // no op } public GlobalTransaction getGlobalTransaction() { return null; } /** * Creates a node in the cache, specified by the given Fqn. */ public Object perform(InvocationContext ctx) { Object[] results = dataContainer.createNodes(fqn); List created = (List) results[0]; boolean foundFqn = false; if (!created.isEmpty()) { for (NodeSPI n : created) { if (fqn.equals(n.getFqn())) foundFqn = true; newlyCreated.add(n.getFqn()); } } if (newlyCreated != null && !foundFqn) newlyCreated.remove(fqn); return results[1]; } public Object acceptVisitor(InvocationContext ctx, Visitor visitor) throws Throwable { return visitor.visitCreateNodeCommand(ctx, this); } public void rollback() { if (newlyCreated != null) { for (Fqn f : newlyCreated) dataContainer.removeFromDataStructure(f, true); } } @Override public String toString() { return "CreateNodeCommand{" + "fqn=" + fqn + ", newlyCreated=" + newlyCreated + '}'; } List getNewlyCreated() { return newlyCreated; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/commands/write/RemoveKeyCommand.java0000644000175000017500000001120111032145203031366 0ustar twernertwernerpackage org.jboss.cache.commands.write; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.NodeSPI; import org.jboss.cache.commands.Visitor; import org.jboss.cache.notifications.event.NodeModifiedEvent; import org.jboss.cache.optimistic.DataVersion; import org.jboss.cache.transaction.GlobalTransaction; import java.util.Collections; import java.util.Map; /** * Implements functionality defined by {@link org.jboss.cache.Cache#remove(org.jboss.cache.Fqn, Object)} * * @author Mircea.Markus@jboss.com * @since 2.2 */ public class RemoveKeyCommand extends AbstractVersionedDataCommand { public static final int METHOD_ID = 6; public static final int VERSIONED_METHOD_ID = 41; private static final Log log = LogFactory.getLog(RemoveKeyCommand.class); private static boolean trace = log.isTraceEnabled(); /* parameters */ private Object key; /* internally used for rollback */ private Object oldValue; public RemoveKeyCommand(GlobalTransaction gtx, Fqn fqn, Object key) { this.globalTransaction = gtx; this.fqn = fqn; this.key = key; } public RemoveKeyCommand() { } /** * Removes the specified key from the data map in the node referenced by the specified Fqn. * * @param ctx invocation context * @return the value being removed, if any, otherwise a null. */ public Object perform(InvocationContext ctx) { if (trace) log.trace("perform(" + globalTransaction + ", \"" + fqn + "\", key=" + key + ")"); NodeSPI n = dataContainer.peek(fqn, false, false); if (n == null) { if (log.isDebugEnabled()) log.debug("node " + fqn + " not found"); return null; } if (notifier.shouldNotifyOnNodeModified()) { notifier.notifyNodeModified(fqn, true, NodeModifiedEvent.ModificationType.REMOVE_DATA, n.getDataDirect(), ctx); } this.oldValue = n.removeDirect(key); if (notifier.shouldNotifyOnNodeModified()) { Map removedData = Collections.singletonMap(key, oldValue); notifier.notifyNodeModified(fqn, false, NodeModifiedEvent.ModificationType.REMOVE_DATA, removedData, ctx); } return oldValue; } public void rollback() { NodeSPI targetNode = dataContainer.peek(fqn, false, true); if (oldValue != null) { targetNode.putDirect(key, oldValue); } } public Object acceptVisitor(InvocationContext ctx, Visitor visitor) throws Throwable { return visitor.visitRemoveKeyCommand(ctx, this); } public Object getKey() { return key; } public void setKey(Object key) { this.key = key; } public int getCommandId() { return isVersioned() ? VERSIONED_METHOD_ID : METHOD_ID; } @Override public Object[] getParameters() { if (isVersioned()) return new Object[]{globalTransaction, fqn, key, true, dataVersion}; else return new Object[]{globalTransaction, fqn, key, true}; } @Override public void setParameters(int commandId, Object[] args) { globalTransaction = (GlobalTransaction) args[0]; fqn = (Fqn) args[1]; key = args[2]; if (isVersionedId(commandId)) dataVersion = (DataVersion) args[4]; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; if (!super.equals(o)) return false; RemoveKeyCommand that = (RemoveKeyCommand) o; if (globalTransaction != null ? !globalTransaction.equals(that.globalTransaction) : that.globalTransaction != null) return false; if (key != null ? !key.equals(that.key) : that.key != null) return false; return true; } @Override public int hashCode() { int result = super.hashCode(); result = 31 * result + (globalTransaction != null ? globalTransaction.hashCode() : 0); result = 31 * result + (key != null ? key.hashCode() : 0); return result; } @Override protected boolean isVersionedId(int id) { return id == VERSIONED_METHOD_ID; } @Override public String toString() { return "RemoveKeyCommand{" + "fqn=" + fqn + ", dataVersion=" + dataVersion + ", globalTransaction=" + globalTransaction + ", key=" + key + ", oldValue=" + oldValue + '}'; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/commands/write/ClearDataCommand.java0000644000175000017500000001061611023757460031327 0ustar twernertwernerpackage org.jboss.cache.commands.write; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.NodeSPI; import org.jboss.cache.commands.Visitor; import org.jboss.cache.notifications.event.NodeModifiedEvent; import org.jboss.cache.optimistic.DataVersion; import org.jboss.cache.transaction.GlobalTransaction; import java.util.HashMap; import java.util.Map; /** * Implements functionality defined by {@link org.jboss.cache.Cache#clearData(String)}} * * @author Mircea.Markus@jboss.com * @since 2.2 */ public class ClearDataCommand extends AbstractVersionedDataCommand { public static final int METHOD_ID = 7; public static final int VERSIONED_METHOD_ID = 42; private static final Log log = LogFactory.getLog(ClearDataCommand.class); private static boolean trace = log.isTraceEnabled(); /* parameters*/ private HashMap originalData; public ClearDataCommand(GlobalTransaction gtx, Fqn fqn) { this.globalTransaction = gtx; this.fqn = fqn; } public ClearDataCommand() { } /** * Clears the data map in the node referenced by the specified Fqn. * * @param ctx invocation context * @return null */ public Object perform(InvocationContext ctx) { if (trace) log.trace("perform(" + globalTransaction + ", \"" + fqn + "\")"); NodeSPI targetNode = dataContainer.peekVersioned(fqn, dataVersion); if (targetNode == null) { log.warn("node " + fqn + " not found"); return null; } Map data = targetNode.getDataDirect(); prepareDataForRollback(data); notifier.notifyNodeModified(fqn, true, NodeModifiedEvent.ModificationType.REMOVE_DATA, data, ctx); targetNode.clearDataDirect(); notifier.notifyNodeModified(fqn, false, NodeModifiedEvent.ModificationType.REMOVE_DATA, data, ctx); return null; } private void prepareDataForRollback(Map data) { if (globalTransaction != null && !data.isEmpty()) { originalData = new HashMap(data); } } public void rollback() { if (trace) log.trace("rollback(" + globalTransaction + ", \"" + fqn + "\", " + originalData + ")"); NodeSPI nodeSpi = dataContainer.peek(fqn, false, true); if (nodeSpi == null) { if (trace) log.trace("Not rolling back node clearance for node: " + fqn + " as it does not exist in the cache. " + "This might be the result of an NoOp clear operation"); return; } nodeSpi.putAllDirect(originalData); } public Object acceptVisitor(InvocationContext ctx, Visitor visitor) throws Throwable { return visitor.visitClearDataCommand(ctx, this); } public int getCommandId() { return isVersioned() ? VERSIONED_METHOD_ID : METHOD_ID; } @Override public Object[] getParameters() { if (isVersioned()) return new Object[]{globalTransaction, fqn, true, dataVersion}; else return new Object[]{globalTransaction, fqn, true}; } @Override public void setParameters(int commandId, Object[] args) { globalTransaction = (GlobalTransaction) args[0]; fqn = (Fqn) args[1]; if (isVersionedId(commandId)) dataVersion = (DataVersion) args[3]; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; if (!super.equals(o)) return false; ClearDataCommand that = (ClearDataCommand) o; return !(globalTransaction != null ? !globalTransaction.equals(that.globalTransaction) : that.globalTransaction != null); } @Override public int hashCode() { int result = super.hashCode(); result = 31 * result + (globalTransaction != null ? globalTransaction.hashCode() : 0); return result; } @Override protected boolean isVersionedId(int id) { return id == VERSIONED_METHOD_ID; } @Override public String toString() { return "RemoveDataCommand{" + "fqn=" + fqn + ", dataVersion=" + dataVersion + ", globalTransaction=" + globalTransaction + ", originalData=" + originalData + '}'; } } ././@LongLink0000000000000000000000000000014500000000000011565 Lustar rootrootjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/commands/write/PutForExternalReadCommand.javajbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/commands/write/PutForExternalReadCommand.java0000644000175000017500000000240311015045001033176 0ustar twernertwernerpackage org.jboss.cache.commands.write; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.commands.Visitor; import org.jboss.cache.transaction.GlobalTransaction; /** * Represents the {@link org.jboss.cache.Cache#putForExternalRead(org.jboss.cache.Fqn, Object, Object)} method call. * * @author Manik Surtani (manik@jboss.org) * @since 2.2.0 */ public class PutForExternalReadCommand extends PutKeyValueCommand { public static final int METHOD_ID = 45; public static final int VERSIONED_METHOD_ID = 46; public PutForExternalReadCommand(GlobalTransaction gtx, Fqn fqn, Object key, Object value) { super(gtx, fqn, key, value); } public PutForExternalReadCommand() { super(); } @Override public int getCommandId() { if (isVersioned()) { return VERSIONED_METHOD_ID; } else { return METHOD_ID; } } @Override protected boolean isVersionedId(int commandId) { return commandId == VERSIONED_METHOD_ID; } @Override public Object acceptVisitor(InvocationContext ctx, Visitor visitor) throws Throwable { return visitor.visitPutForExternalReadCommand(ctx, this); } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/commands/write/PutDataMapCommand.java0000644000175000017500000001165111015516661031504 0ustar twernertwernerpackage org.jboss.cache.commands.write; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.NodeSPI; import org.jboss.cache.commands.Visitor; import org.jboss.cache.notifications.event.NodeModifiedEvent; import org.jboss.cache.optimistic.DataVersion; import org.jboss.cache.transaction.GlobalTransaction; import java.util.Collections; import java.util.HashMap; import java.util.Map; /** * Implements functionality defined by {@link org.jboss.cache.Cache#put(String, java.util.Map)}. * * @author Mircea.Markus@jboss.com * @since 2.2 */ public class PutDataMapCommand extends AbstractVersionedDataCommand { public static final int METHOD_ID = 1; public static final int ERASE_METHOD_ID = 2; public static final int VERSIONED_METHOD_ID = 37; public static final int ERASE_VERSIONED_METHOD_ID = 38; private static final Log log = LogFactory.getLog(PutDataMapCommand.class); private static boolean trace = log.isTraceEnabled(); /* parameters*/ private Map data; private Map oldData; public PutDataMapCommand(GlobalTransaction globalTransaction, Fqn fqn, Map data) { this.globalTransaction = globalTransaction; this.fqn = fqn; this.data = data; } public PutDataMapCommand() { } /** * Adds the provided data map to the data map in the node referenced by the specified Fqn. */ public Object perform(InvocationContext ctx) { if (trace) { log.trace("perform(" + globalTransaction + ", \"" + fqn + "\", " + data + ")"); } NodeSPI nodeSPI = dataContainer.peekStrict(globalTransaction, fqn, false); Map existingData = nodeSPI.getDataDirect(); if (!existingData.isEmpty()) { oldData = new HashMap(existingData); // defensive copy } if (notifier.shouldNotifyOnNodeModified()) { notifier.notifyNodeModified(fqn, true, NodeModifiedEvent.ModificationType.PUT_MAP, oldData == null ? Collections.emptyMap() : oldData, ctx); } nodeSPI.putAllDirect(data); if (notifier.shouldNotifyOnNodeModified()) { notifier.notifyNodeModified(fqn, false, NodeModifiedEvent.ModificationType.PUT_MAP, nodeSPI.getDataDirect(), ctx); } return null; } public void rollback() { if (trace) log.trace("rollback(" + globalTransaction + ", " + fqn + ", " + data + ")"); NodeSPI n = dataContainer.peek(fqn, false, true); if (n != null) { n.clearDataDirect(); if (oldData != null) n.putAllDirect(oldData); } } public Object acceptVisitor(InvocationContext ctx, Visitor visitor) throws Throwable { return visitor.visitPutDataMapCommand(ctx, this); } public Map getData() { return data; } public void setData(Map data) { this.data = data; } public int getCommandId() { if (isVersioned()) { return VERSIONED_METHOD_ID; } else { return METHOD_ID; } } @Override public Object[] getParameters() { if (isVersioned()) return new Object[]{globalTransaction, fqn, data, false, dataVersion}; else return new Object[]{globalTransaction, fqn, data, false}; } @Override public void setParameters(int commandId, Object[] args) { globalTransaction = (GlobalTransaction) args[0]; fqn = (Fqn) args[1]; data = (Map) args[2]; if (isVersionedId(commandId)) dataVersion = (DataVersion) args[4]; } @Override protected boolean isVersionedId(int id) { return id == VERSIONED_METHOD_ID || id == ERASE_VERSIONED_METHOD_ID; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; if (!super.equals(o)) return false; PutDataMapCommand that = (PutDataMapCommand) o; if (data != null ? !data.equals(that.data) : that.data != null) return false; if (globalTransaction != null ? !globalTransaction.equals(that.globalTransaction) : that.globalTransaction != null) return false; return true; } @Override public int hashCode() { int result = super.hashCode(); result = 31 * result + (globalTransaction != null ? globalTransaction.hashCode() : 0); result = 31 * result + (data != null ? data.hashCode() : 0); return result; } @Override public String toString() { return "PutDataMapCommand{" + "fqn=" + fqn + ", dataVersion=" + dataVersion + ", data=" + data + ", globalTransaction=" + globalTransaction + '}'; } Map getOldData() { return oldData; } } jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/commands/write/MoveCommand.java0000644000175000017500000001177011032145203030401 0ustar twernertwernerpackage org.jboss.cache.commands.write; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.DataContainer; import org.jboss.cache.Fqn; import org.jboss.cache.InvocationContext; import org.jboss.cache.NodeNotExistsException; import org.jboss.cache.NodeSPI; import org.jboss.cache.commands.ReversibleCommand; import org.jboss.cache.commands.Visitor; import org.jboss.cache.commands.read.AbstractDataCommand; import org.jboss.cache.notifications.Notifier; import org.jboss.cache.transaction.GlobalTransaction; /** * Implements functionality defined by {@link org.jboss.cache.Cache#move(org.jboss.cache.Fqn, org.jboss.cache.Fqn)} * * @author Mircea.Markus@jboss.com * @since 2.2 */ // TODO: 2.2.0: Make sure this is properly intercepted, i.e., locked and loaded!! public class MoveCommand extends AbstractDataCommand implements ReversibleCommand { public static final int METHOD_ID = 36; private static final Log log = LogFactory.getLog(MoveCommand.class); private static boolean trace = log.isTraceEnabled(); /* dependencies */ private Notifier notifier; /* params */ private Fqn to; private GlobalTransaction globalTransaction; public MoveCommand() { } public void initialize(Notifier notifier, DataContainer dataContainer) { this.notifier = notifier; this.dataContainer = dataContainer; } public MoveCommand(Fqn from, Fqn to) { this.fqn = from; this.to = to; } public GlobalTransaction getGlobalTransaction() { return globalTransaction; } public void setGlobalTransaction(GlobalTransaction globalTransaction) { this.globalTransaction = globalTransaction; } /** * Moves a node, from fqn to to, and returns null. * * @param ctx invocation context * @return null */ public Object perform(InvocationContext ctx) { move(fqn, to, false, ctx); return null; } public void rollback() { move(Fqn.fromRelativeElements(to, fqn.getLastElement()), fqn.getParent(), true, null); } private void adjustFqn(NodeSPI node, Fqn newBase) { Fqn newFqn = Fqn.fromRelativeElements(newBase, node.getFqn().getLastElement()); node.setFqn(newFqn); } public Object acceptVisitor(InvocationContext ctx, Visitor visitor) throws Throwable { return visitor.visitMoveCommand(ctx, this); } private void move(Fqn toMoveFqn, Fqn newParentFqn, boolean skipNotifications, InvocationContext ctx) { // the actual move algorithm. NodeSPI newParent = dataContainer.peek(newParentFqn, false, false); if (newParent == null) { throw new NodeNotExistsException("New parent node " + newParentFqn + " does not exist when attempting to move node!!"); } NodeSPI node = dataContainer.peek(toMoveFqn, false, false); if (node == null) { throw new NodeNotExistsException("Node " + toMoveFqn + " does not exist when attempting to move node!!"); } if (trace) log.trace("Moving " + fqn + " to sit under " + to); NodeSPI oldParent = node.getParent(); Object nodeName = toMoveFqn.getLastElement(); // now that we have the parent and target nodes: // first correct the pointers at the pruning point oldParent.removeChildDirect(nodeName); newParent.addChild(nodeName, node); // parent pointer is calculated on the fly using Fqns. // notify if (!skipNotifications) notifier.notifyNodeMoved(toMoveFqn, Fqn.fromRelativeElements(newParentFqn, toMoveFqn.getLastElement()), true, ctx); // now adjust Fqns of node and all children. adjustFqn(node, newParent.getFqn()); if (!skipNotifications) notifier.notifyNodeMoved(toMoveFqn, Fqn.fromRelativeElements(newParentFqn, toMoveFqn.getLastElement()), false, ctx); } public Fqn getTo() { return to; } public int getCommandId() { return METHOD_ID; } @Override public Object[] getParameters() { return new Object[]{fqn, to}; } @Override public void setParameters(int commandId, Object[] args) { fqn = (Fqn) args[0]; to = (Fqn) args[1]; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; if (!super.equals(o)) return false; MoveCommand that = (MoveCommand) o; if (to != null ? !to.equals(that.to) : that.to != null) return false; return true; } @Override public int hashCode() { int result = super.hashCode(); result = 31 * result + (to != null ? to.hashCode() : 0); return result; } @Override public String toString() { return "MoveCommand{" + "fqn=" + fqn + ", to=" + to + '}'; } } ././@LongLink0000000000000000000000000000015000000000000011561 Lustar rootrootjbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/commands/write/AbstractVersionedDataCommand.javajbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/commands/write/AbstractVersionedDataCommand.j0000644000175000017500000000440311014645153033223 0ustar twernertwernerpackage org.jboss.cache.commands.write; import org.jboss.cache.DataContainer; import org.jboss.cache.Fqn; import org.jboss.cache.commands.VersionedDataCommand; import org.jboss.cache.commands.read.AbstractDataCommand; import org.jboss.cache.notifications.Notifier; import org.jboss.cache.optimistic.DataVersion; import org.jboss.cache.transaction.GlobalTransaction; /** * Base version of {@link org.jboss.cache.commands.DataCommand} which handles common behaviour * * @author Manik Surtani (manik@jboss.org) * @since 2.2.0 */ public abstract class AbstractVersionedDataCommand extends AbstractDataCommand implements VersionedDataCommand { protected Notifier notifier; protected DataVersion dataVersion; protected GlobalTransaction globalTransaction; public void initialize(Notifier notifier, DataContainer dataContainer) { this.notifier = notifier; this.dataContainer = dataContainer; } public DataVersion getDataVersion() { return dataVersion; } public void setDataVersion(DataVersion dataVersion) { this.dataVersion = dataVersion; } public GlobalTransaction getGlobalTransaction() { return globalTransaction; } public void setGlobalTransaction(GlobalTransaction globalTransaction) { this.globalTransaction = globalTransaction; } public boolean isVersioned() { return dataVersion != null; } // basic implementations @Override public Object[] getParameters() { if (isVersioned()) return new Object[]{fqn, dataVersion}; else return new Object[]{fqn}; } // basic implementations @Override public void setParameters(int commandId, Object[] args) { fqn = (Fqn) args[0]; if (isVersionedId(commandId)) dataVersion = (DataVersion) args[1]; } protected abstract boolean isVersionedId(int id); @Override public boolean equals(Object o) { if (!super.equals(o)) return false; AbstractVersionedDataCommand that = (AbstractVersionedDataCommand) o; return !(fqn != null ? !fqn.equals(that.fqn) : that.fqn != null); } @Override public int hashCode() { return 31 * super.hashCode() + (dataVersion != null ? dataVersion.hashCode() : 0); } }jbosscache-core-2.2.2.GA/src/main/java/org/jboss/cache/commands/DataCommand.java0000644000175000017500000000071711005373411027215 0ustar twernertwernerpackage org.jboss.cache.commands; import org.jboss.cache.Fqn; /** * Commands of this type manipulate data in the cache. * * @author Mircea.Markus@jboss.com * @author Manik Surtani (manik@jboss.org) * @since 2.2.0 */ public interface DataCommand extends VisitableCommand { /** * Returns the Fqn of the node on which this command operates. * * @return an Fqn */ Fqn getFqn(); }